Browse Source

Change extensions .cpp to .c

Ginger Bill 8 years ago
parent
commit
4d30ef7eda
21 changed files with 1 additions and 23843 deletions
  1. 1 1
      build.bat
  2. 0 235
      src/array.cpp
  3. 0 1356
      src/checker/checker.cpp
  4. 0 545
      src/checker/decl.cpp
  5. 0 193
      src/checker/entity.cpp
  6. 0 4452
      src/checker/expr.cpp
  7. 0 1130
      src/checker/stmt.cpp
  8. 0 1487
      src/checker/types.cpp
  9. 0 250
      src/common.cpp
  10. 0 400
      src/exact_value.cpp
  11. 0 265
      src/main.cpp
  12. 0 1305
      src/old_vm.cpp
  13. 0 3243
      src/parser.cpp
  14. 0 221
      src/printer.cpp
  15. 0 5419
      src/ssa.cpp
  16. 0 493
      src/ssa_opt.cpp
  17. 0 1439
      src/ssa_print.cpp
  18. 0 422
      src/string.cpp
  19. 0 105
      src/timings.cpp
  20. 0 816
      src/tokenizer.cpp
  21. 0 66
      src/unicode.cpp

+ 1 - 1
build.bat

@@ -46,7 +46,7 @@ rem pushd %build_dir%
 	del *.pdb > NUL 2> NUL
 	del *.ilk > NUL 2> NUL
 
-	cl %compiler_settings% "src\main.cpp" ^
+	cl %compiler_settings% "src\main.c" ^
 		/link %linker_settings% -OUT:%exe_name% ^
 	&& odin run code/demo.odin
 	rem odin run code/demo.odin

+ 0 - 235
src/array.cpp

@@ -1,235 +0,0 @@
-#define ARRAY_GROW_FORMULA(x) (2*(x) + 8)
-GB_STATIC_ASSERT(ARRAY_GROW_FORMULA(0) > 0);
-
-#define Array(Type_) struct { \
-	gbAllocator allocator; \
-	Type_ *     e; \
-	isize       count; \
-	isize       capacity; \
-}
-
-typedef Array(void) ArrayVoid;
-
-#define array_init_reserve(x_, allocator_, init_capacity_) do { \
-	GB_ASSERT((x_) != NULL); \
-	void **e = cast(void **)&((x_)->e); \
-	(x_)->allocator = (allocator_); \
-	(x_)->count = 0; \
-	(x_)->capacity = (init_capacity_); \
-	*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_capacity_)); \
-} while (0)
-
-#define array_init_count(x_, allocator_, init_count_) do { \
-	GB_ASSERT((x_) != NULL); \
-	void **e = cast(void **)&((x_)->e); \
-	(x_)->allocator = (allocator_); \
-	(x_)->count = (init_count_); \
-	(x_)->capacity = (init_count_); \
-	*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_count_)); \
-} while (0)
-
-#define array_init(x_, allocator_)        do { array_init_reserve(x_, allocator_, ARRAY_GROW_FORMULA(0)); } while (0)
-#define array_free(x_)                    do { gb_free((x_)->allocator, (x_)->e); } while (0)
-#define array_set_capacity(x_, capacity_) do { array__set_capacity((x_), (capacity_), gb_size_of(*(x_)->e)); } while (0)
-
-#define array_grow(x_, min_capacity_) do { \
-	isize new_capacity = ARRAY_GROW_FORMULA((x_)->capacity); \
-	if (new_capacity < (min_capacity_)) { \
-		new_capacity = (min_capacity_); \
-	} \
-	array_set_capacity(x_, new_capacity); \
-} while (0)
-
-#define array_add(x_, item_) do { \
-	if ((x_)->capacity < (x_)->count+1) { \
-		array_grow(x_, 0); \
-	} \
-	(x_)->e[(x_)->count++] = item_; \
-} while (0)
-
-#define array_pop(x_)   do { GB_ASSERT((x_)->count > 0); (x_)->count--; } while (0)
-#define array_clear(x_) do { (x_)->count = 0; } while (0)
-
-#define array_resize(x_, new_count_) do { \
-	if ((x_)->capacity < (new_count_)) { \
-		array_grow((x_), (new_count_)); \
-	} \
-	(x_)->count = (new_count_); \
-} while (0)
-
-#define array_reserve(x_, new_capacity_) do { \
-	if ((x_)->capacity < (new_capacity_)) { \
-		array_set_capacity((x_), (new_capacity_)); \
-	} \
-} while (0)
-
-
-
-
-void array__set_capacity(void *ptr, isize capacity, isize element_size) {
-	GB_ASSERT(ptr != NULL);
-	ArrayVoid *x = cast(ArrayVoid *)ptr;
-
-	GB_ASSERT(element_size > 0);
-
-	if (capacity == x->capacity) {
-		return;
-	}
-
-	if (capacity < x->count) {
-		if (x->capacity < capacity) {
-			isize new_capacity = ARRAY_GROW_FORMULA(x->capacity);
-			if (new_capacity < capacity) {
-				new_capacity = capacity;
-			}
-			array__set_capacity(ptr, new_capacity, element_size);
-		}
-		x->count = capacity;
-	}
-
-	{
-		// TODO(bill): Resize rather than copy and delete
-		void *new_data = gb_alloc(x->allocator, element_size*capacity);
-		gb_memmove(new_data, x->e, element_size*x->count);
-		gb_free(x->allocator, x->e);
-		x->capacity = capacity;
-		x->e = new_data;
-	}
-}
-
-
-#if 0
-template <typename T>
-struct Array {
-	gbAllocator allocator;
-	T *         data;
-	isize       count;
-	isize       capacity;
-
-	T &operator[](isize index) {
-		GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
-		return data[index];
-	}
-
-	T const &operator[](isize index) const {
-		GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
-		return data[index];
-	}
-};
-
-template <typename T> void     array_init        (Array<T> *array, gbAllocator a, isize init_capacity = ARRAY_GROW_FORMULA(0));
-template <typename T> void     array_init_count  (Array<T> *array, gbAllocator a, isize count);
-template <typename T> Array<T> array_make        (T *data, isize count, isize capacity);
-template <typename T> void     array_free        (Array<T> *array);
-template <typename T> void     array_add         (Array<T> *array, T const &t);
-template <typename T> T        array_pop         (Array<T> *array);
-template <typename T> void     array_clear       (Array<T> *array);
-template <typename T> void     array_reserve     (Array<T> *array, isize capacity);
-template <typename T> void     array_resize      (Array<T> *array, isize count);
-template <typename T> void     array_set_capacity(Array<T> *array, isize capacity);
-
-
-template <typename T>
-void array_init(Array<T> *array, gbAllocator a, isize init_capacity) {
-	array->allocator = a;
-	array->data = gb_alloc_array(a, T, init_capacity);
-	array->count = 0;
-	array->capacity = init_capacity;
-}
-
-template <typename T>
-void array_init_count(Array<T> *array, gbAllocator a, isize count) {
-	array->allocator = a;
-	array->data = gb_alloc_array(a, T, count);
-	array->count = count;
-	array->capacity = count;
-}
-
-
-template <typename T>
-Array<T> array_make(T *data, isize count, isize capacity) {
-	Array<T> a = {0};
-	a.data = data;
-	a.count = count;
-	a.capacity = capacity;
-	return a;
-}
-
-
-template <typename T>
-void array_free(Array<T> *array) {
-	if (array->allocator.proc != NULL) {
-		gb_free(array->allocator, array->data);
-	}
-	array->count = 0;
-	array->capacity = 0;
-}
-
-template <typename T>
-void array__grow(Array<T> *array, isize min_capacity) {
-	isize new_capacity = ARRAY_GROW_FORMULA(array->capacity);
-	if (new_capacity < min_capacity) {
-		new_capacity = min_capacity;
-	}
-	array_set_capacity(array, new_capacity);
-}
-
-template <typename T>
-void array_add(Array<T> *array, T const &t) {
-	if (array->capacity < array->count+1) {
-		array__grow(array, 0);
-	}
-	array->data[array->count] = t;
-	array->count++;
-}
-
-template <typename T>
-T array_pop(Array<T> *array) {
-	GB_ASSERT(array->count > 0);
-	array->count--;
-	return array->data[array->count];
-}
-
-template <typename T>
-void array_clear(Array<T> *array) {
-	array->count = 0;
-}
-
-template <typename T>
-void array_reserve(Array<T> *array, isize capacity) {
-	if (array->capacity < capacity) {
-		array_set_capacity(array, capacity);
-	}
-}
-
-template <typename T>
-void array_resize(Array<T> *array, isize count) {
-	if (array->capacity < count) {
-		array__grow(array, count);
-	}
-	array->count = count;
-}
-
-template <typename T>
-void array_set_capacity(Array<T> *array, isize capacity) {
-	if (capacity == array->capacity) {
-		return;
-	}
-
-	if (capacity < array->count) {
-		array_resize(array, capacity);
-	}
-
-	T *new_data = NULL;
-	if (capacity > 0) {
-		new_data = gb_alloc_array(array->allocator, T, capacity);
-		gb_memmove(new_data, array->data, gb_size_of(T) * array->capacity);
-	}
-	gb_free(array->allocator, array->data);
-	array->data = new_data;
-	array->capacity = capacity;
-}
-
-
-
-#endif

+ 0 - 1356
src/checker/checker.cpp

@@ -1,1356 +0,0 @@
-#include "../exact_value.cpp"
-#include "entity.cpp"
-#include "types.cpp"
-
-#define MAP_TYPE Entity *
-#define MAP_FUNC map_entity_
-#define MAP_NAME MapEntity
-#include "../map.c"
-
-typedef enum AddressingMode {
-	Addressing_Invalid,
-	Addressing_NoValue,
-	Addressing_Value,
-	Addressing_Variable,
-	Addressing_Constant,
-	Addressing_Type,
-	Addressing_Builtin,
-	Addressing_Count,
-} AddressingMode;
-
-typedef struct Operand {
-	AddressingMode mode;
-	Type *         type;
-	ExactValue     value;
-	AstNode *      expr;
-	BuiltinProcId  builtin_id;
-} Operand;
-
-typedef struct TypeAndValue {
-	AddressingMode mode;
-	Type *         type;
-	ExactValue     value;
-} TypeAndValue;
-
-
-
-typedef struct DeclInfo {
-	Scope *scope;
-
-	Entity **entities;
-	isize    entity_count;
-
-	AstNode *type_expr;
-	AstNode *init_expr;
-	AstNode *proc_decl; // AstNode_ProcDecl
-	u32      var_decl_tags;
-
-	MapBool deps; // Key: Entity *
-} DeclInfo;
-
-typedef struct ExprInfo {
-	bool           is_lhs; // Debug info
-	AddressingMode mode;
-	Type *         type; // Type_Basic
-	ExactValue     value;
-} ExprInfo;
-
-ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue value) {
-	ExprInfo ei = {is_lhs, mode, type, value};
-	return ei;
-}
-
-typedef struct ProcedureInfo {
-	AstFile * file;
-	Token     token;
-	DeclInfo *decl;
-	Type *    type; // Type_Procedure
-	AstNode * body; // AstNode_BlockStatement
-	u32       tags;
-} ProcedureInfo;
-
-typedef struct Scope {
-	Scope *        parent;
-	Scope *        prev, *next;
-	Scope *        first_child;
-	Scope *        last_child;
-	MapEntity      elements; // Key: String
-	MapEntity      implicit; // Key: String
-
-	Array(Scope *) shared;
-	Array(Scope *) imported;
-	bool           is_proc;
-	bool           is_global;
-	bool           is_file;
-	bool           is_init;
-	AstFile *      file;
-} Scope;
-gb_global Scope *universal_scope = NULL;
-
-typedef enum ExprKind {
-	Expr_Expr,
-	Expr_Stmt,
-} ExprKind;
-
-typedef enum BuiltinProcId {
-	BuiltinProc_Invalid,
-
-	BuiltinProc_new,
-	BuiltinProc_new_slice,
-
-	BuiltinProc_size_of,
-	BuiltinProc_size_of_val,
-	BuiltinProc_align_of,
-	BuiltinProc_align_of_val,
-	BuiltinProc_offset_of,
-	BuiltinProc_offset_of_val,
-	BuiltinProc_type_of_val,
-
-	BuiltinProc_type_info,
-	BuiltinProc_type_info_of_val,
-
-	BuiltinProc_compile_assert,
-	BuiltinProc_assert,
-	BuiltinProc_panic,
-
-	BuiltinProc_copy,
-	BuiltinProc_append,
-
-	BuiltinProc_swizzle,
-
-	// BuiltinProc_ptr_offset,
-	// BuiltinProc_ptr_sub,
-	BuiltinProc_slice_ptr,
-
-	BuiltinProc_min,
-	BuiltinProc_max,
-	BuiltinProc_abs,
-
-	BuiltinProc_enum_to_string,
-
-	BuiltinProc_Count,
-} BuiltinProcId;
-typedef struct BuiltinProc {
-	String   name;
-	isize    arg_count;
-	bool      variadic;
-	ExprKind kind;
-} BuiltinProc;
-gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
-	{STR_LIT(""),                 0, false, Expr_Stmt},
-
-	{STR_LIT("new"),              1, false, Expr_Expr},
-	{STR_LIT("new_slice"),        2, true,  Expr_Expr},
-
-	{STR_LIT("size_of"),          1, false, Expr_Expr},
-	{STR_LIT("size_of_val"),      1, false, Expr_Expr},
-	{STR_LIT("align_of"),         1, false, Expr_Expr},
-	{STR_LIT("align_of_val"),     1, false, Expr_Expr},
-	{STR_LIT("offset_of"),        2, false, Expr_Expr},
-	{STR_LIT("offset_of_val"),    1, false, Expr_Expr},
-	{STR_LIT("type_of_val"),      1, false, Expr_Expr},
-
-	{STR_LIT("type_info"),        1, false, Expr_Expr},
-	{STR_LIT("type_info_of_val"), 1, false, Expr_Expr},
-
-	{STR_LIT("compile_assert"),   1, false, Expr_Stmt},
-	{STR_LIT("assert"),           1, false, Expr_Stmt},
-	{STR_LIT("panic"),            1, false, Expr_Stmt},
-
-	{STR_LIT("copy"),             2, false, Expr_Expr},
-	{STR_LIT("append"),           2, false, Expr_Expr},
-
-	{STR_LIT("swizzle"),          1, true,  Expr_Expr},
-
-	// {STR_LIT("ptr_offset"),       2, false, Expr_Expr},
-	// {STR_LIT("ptr_sub"),          2, false, Expr_Expr},
-	{STR_LIT("slice_ptr"),        2, true,  Expr_Expr},
-
-	{STR_LIT("min"),              2, false, Expr_Expr},
-	{STR_LIT("max"),              2, false, Expr_Expr},
-	{STR_LIT("abs"),              1, false, Expr_Expr},
-
-	{STR_LIT("enum_to_string"),   1, false, Expr_Expr},
-};
-
-typedef enum ImplicitValueId {
-	ImplicitValue_Invalid,
-
-	ImplicitValue_context,
-
-	ImplicitValue_Count,
-} ImplicitValueId;
-typedef struct ImplicitValueInfo {
-	String  name;
-	String  backing_name;
-	Type *  type;
-} ImplicitValueInfo;
-// NOTE(bill): This is initialized later
-gb_global ImplicitValueInfo implicit_value_infos[ImplicitValue_Count] = {0};
-
-
-
-typedef struct CheckerContext {
-	Scope *   scope;
-	DeclInfo *decl;
-	u32       stmt_state_flags;
-} CheckerContext;
-
-#define MAP_TYPE TypeAndValue
-#define MAP_FUNC map_tav_
-#define MAP_NAME MapTypeAndValue
-#include "../map.c"
-
-#define MAP_TYPE Scope *
-#define MAP_FUNC map_scope_
-#define MAP_NAME MapScope
-#include "../map.c"
-
-#define MAP_TYPE DeclInfo *
-#define MAP_FUNC map_decl_info_
-#define MAP_NAME MapDeclInfo
-#include "../map.c"
-
-#define MAP_TYPE AstFile *
-#define MAP_FUNC map_ast_file_
-#define MAP_NAME MapAstFile
-#include "../map.c"
-
-#define MAP_TYPE ExprInfo
-#define MAP_FUNC map_expr_info_
-#define MAP_NAME MapExprInfo
-#include "../map.c"
-
-
-// NOTE(bill): Symbol tables
-typedef struct CheckerInfo {
-	MapTypeAndValue     types;           // Key: AstNode * | Expression -> Type (and value)
-	MapEntity           definitions;     // Key: AstNode * | Identifier -> Entity
-	MapEntity           uses;            // Key: AstNode * | Identifier -> Entity
-	MapScope            scopes;          // Key: AstNode * | Node       -> Scope
-	MapExprInfo         untyped;         // Key: AstNode * | Expression -> ExprInfo
-	MapDeclInfo         entities;        // Key: Entity *
-	MapEntity           foreign_procs;   // Key: String
-	MapAstFile          files;           // Key: String (full path)
-	MapIsize            type_info_map;   // Key: Type *
-	isize               type_info_count;
-	Entity *            implicit_values[ImplicitValue_Count];
-} CheckerInfo;
-
-typedef struct Checker {
-	Parser *    parser;
-	CheckerInfo info;
-
-	AstFile *              curr_ast_file;
-	BaseTypeSizes          sizes;
-	Scope *                global_scope;
-	Array(ProcedureInfo)   procs; // NOTE(bill): Procedures to check
-
-	gbArena                arena;
-	gbArena                tmp_arena;
-	gbAllocator            allocator;
-	gbAllocator            tmp_allocator;
-
-	CheckerContext         context;
-
-	Array(Type *)          proc_stack;
-	bool                    in_defer; // TODO(bill): Actually handle correctly
-} Checker;
-
-typedef struct CycleChecker {
-	Array(Entity *) path; // Entity_TypeName
-} CycleChecker;
-
-
-
-
-CycleChecker *cycle_checker_add(CycleChecker *cc, Entity *e) {
-	if (cc == NULL) {
-		return NULL;
-	}
-	if (cc->path.e == NULL) {
-		array_init(&cc->path, heap_allocator());
-	}
-	GB_ASSERT(e != NULL && e->kind == Entity_TypeName);
-	array_add(&cc->path, e);
-	return cc;
-}
-
-void cycle_checker_destroy(CycleChecker *cc) {
-	if (cc != NULL && cc->path.e != NULL)  {
-		array_free(&cc->path);
-	}
-}
-
-
-void init_declaration_info(DeclInfo *d, Scope *scope) {
-	d->scope = scope;
-	map_bool_init(&d->deps, heap_allocator());
-}
-
-DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) {
-	DeclInfo *d = gb_alloc_item(a, DeclInfo);
-	init_declaration_info(d, scope);
-	return d;
-}
-
-void destroy_declaration_info(DeclInfo *d) {
-	map_bool_destroy(&d->deps);
-}
-
-bool decl_info_has_init(DeclInfo *d) {
-	if (d->init_expr != NULL) {
-		return true;
-	}
-	if (d->proc_decl != NULL) {
-		ast_node(pd, ProcDecl, d->proc_decl);
-		if (pd->body != NULL) {
-			return true;
-		}
-	}
-
-	return false;
-}
-
-
-
-
-
-Scope *make_scope(Scope *parent, gbAllocator allocator) {
-	Scope *s = gb_alloc_item(allocator, Scope);
-	s->parent = parent;
-	map_entity_init(&s->elements,   heap_allocator());
-	map_entity_init(&s->implicit,   heap_allocator());
-	array_init(&s->shared,   heap_allocator());
-	array_init(&s->imported, heap_allocator());
-
-	if (parent != NULL && parent != universal_scope) {
-		DLIST_APPEND(parent->first_child, parent->last_child, s);
-	}
-	return s;
-}
-
-void destroy_scope(Scope *scope) {
-	for_array(i, scope->elements.entries) {
-		Entity *e =scope->elements.entries.e[i].value;
-		if (e->kind == Entity_Variable) {
-			if (!(e->flags & EntityFlag_Used)) {
-#if 0
-				warning(e->token, "Unused variable `%.*s`", LIT(e->token.string));
-#endif
-			}
-		}
-	}
-
-	for (Scope *child = scope->first_child; child != NULL; child = child->next) {
-		destroy_scope(child);
-	}
-
-	map_entity_destroy(&scope->elements);
-	map_entity_destroy(&scope->implicit);
-	array_free(&scope->shared);
-	array_free(&scope->imported);
-
-	// NOTE(bill): No need to free scope as it "should" be allocated in an arena (except for the global scope)
-}
-
-void add_scope(Checker *c, AstNode *node, Scope *scope) {
-	GB_ASSERT(node != NULL);
-	GB_ASSERT(scope != NULL);
-	map_scope_set(&c->info.scopes, hash_pointer(node), scope);
-}
-
-
-void check_open_scope(Checker *c, AstNode *node) {
-	GB_ASSERT(node != NULL);
-	GB_ASSERT(node->kind == AstNode_Invalid ||
-	          is_ast_node_stmt(node) ||
-	          is_ast_node_type(node));
-	Scope *scope = make_scope(c->context.scope, c->allocator);
-	add_scope(c, node, scope);
-	if (node->kind == AstNode_ProcType) {
-		scope->is_proc = true;
-	}
-	c->context.scope = scope;
-	c->context.stmt_state_flags |= StmtStateFlag_bounds_check;
-}
-
-void check_close_scope(Checker *c) {
-	c->context.scope = c->context.scope->parent;
-}
-
-void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entity **entity_) {
-	bool gone_thru_proc = false;
-	HashKey key = hash_string(name);
-	for (Scope *s = scope; s != NULL; s = s->parent) {
-		Entity **found = map_entity_get(&s->elements, key);
-		if (found) {
-			Entity *e = *found;
-			if (gone_thru_proc) {
-				if (e->kind == Entity_Variable &&
-				    !e->scope->is_file &&
-				    !e->scope->is_global) {
-					continue;
-				}
-			}
-
-			if (entity_) *entity_ = e;
-			if (scope_) *scope_ = s;
-			return;
-		}
-
-		if (s->is_proc) {
-			gone_thru_proc = true;
-		} else {
-			// Check shared scopes - i.e. other files @ global scope
-			for_array(i, s->shared) {
-				Scope *shared = s->shared.e[i];
-				Entity **found = map_entity_get(&shared->elements, key);
-				if (found) {
-					Entity *e = *found;
-					if (e->kind == Entity_Variable &&
-					    !e->scope->is_file &&
-					    !e->scope->is_global) {
-						continue;
-					}
-
-					if (e->scope != shared) {
-						// Do not return imported entities even #load ones
-						continue;
-					}
-					if (!is_entity_exported(e)) {
-						continue;
-					}
-					if (entity_) *entity_ = e;
-					if (scope_) *scope_ = shared;
-					return;
-				}
-			}
-		}
-	}
-
-
-	if (entity_) *entity_ = NULL;
-	if (scope_) *scope_ = NULL;
-}
-
-Entity *scope_lookup_entity(Scope *s, String name) {
-	Entity *entity = NULL;
-	scope_lookup_parent_entity(s, name, NULL, &entity);
-	return entity;
-}
-
-Entity *current_scope_lookup_entity(Scope *s, String name) {
-	HashKey key = hash_string(name);
-	Entity **found = map_entity_get(&s->elements, key);
-	if (found) {
-		return *found;
-	}
-	for_array(i, s->shared) {
-		Entity **found = map_entity_get(&s->shared.e[i]->elements, key);
-		if (found) {
-			return *found;
-		}
-	}
-	return NULL;
-}
-
-
-
-Entity *scope_insert_entity(Scope *s, Entity *entity) {
-	String name = entity->token.string;
-	HashKey key = hash_string(name);
-	Entity **found = map_entity_get(&s->elements, key);
-	if (found) {
-		return *found;
-	}
-	map_entity_set(&s->elements, key, entity);
-	if (entity->scope == NULL) {
-		entity->scope = s;
-	}
-	return NULL;
-}
-
-void check_scope_usage(Checker *c, Scope *scope) {
-	// TODO(bill): Use this?
-}
-
-
-void add_dependency(DeclInfo *d, Entity *e) {
-	map_bool_set(&d->deps, hash_pointer(e), cast(bool)true);
-}
-
-void add_declaration_dependency(Checker *c, Entity *e) {
-	if (e == NULL) {
-		return;
-	}
-	if (c->context.decl != NULL) {
-		DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e));
-		if (found) {
-			add_dependency(c->context.decl, e);
-		}
-	}
-}
-
-
-void add_global_entity(Entity *entity) {
-	String name = entity->token.string;
-	if (gb_memchr(name.text, ' ', name.len)) {
-		return; // NOTE(bill): `untyped thing`
-	}
-	if (scope_insert_entity(universal_scope, entity)) {
-		compiler_error("double declaration");
-	}
-}
-
-void add_global_constant(gbAllocator a, String name, Type *type, ExactValue value) {
-	Entity *entity = alloc_entity(a, Entity_Constant, NULL, make_token_ident(name), type);
-	entity->Constant.value = value;
-	add_global_entity(entity);
-}
-
-
-
-void init_universal_scope(void) {
-	// NOTE(bill): No need to free these
-	gbAllocator a = heap_allocator();
-	universal_scope = make_scope(NULL, a);
-
-// Types
-	for (isize i = 0; i < gb_count_of(basic_types); i++) {
-		add_global_entity(make_entity_type_name(a, NULL, make_token_ident(basic_types[i].Basic.name), &basic_types[i]));
-	}
-	for (isize i = 0; i < gb_count_of(basic_type_aliases); i++) {
-		add_global_entity(make_entity_type_name(a, NULL, make_token_ident(basic_type_aliases[i].Basic.name), &basic_type_aliases[i]));
-	}
-
-// Constants
-	add_global_constant(a, str_lit("true"),  t_untyped_bool, make_exact_value_bool(true));
-	add_global_constant(a, str_lit("false"), t_untyped_bool, make_exact_value_bool(false));
-
-	add_global_entity(make_entity_nil(a, str_lit("nil"), t_untyped_nil));
-
-// Builtin Procedures
-	for (isize i = 0; i < gb_count_of(builtin_procs); i++) {
-		BuiltinProcId id = cast(BuiltinProcId)i;
-		Entity *entity = alloc_entity(a, Entity_Builtin, NULL, make_token_ident(builtin_procs[i].name), t_invalid);
-		entity->Builtin.id = id;
-		add_global_entity(entity);
-	}
-
-	t_u8_ptr = make_type_pointer(a, t_u8);
-	t_int_ptr = make_type_pointer(a, t_int);
-}
-
-
-
-
-void init_checker_info(CheckerInfo *i) {
-	gbAllocator a = heap_allocator();
-	map_tav_init(&i->types,            a);
-	map_entity_init(&i->definitions,   a);
-	map_entity_init(&i->uses,          a);
-	map_scope_init(&i->scopes,         a);
-	map_decl_info_init(&i->entities,   a);
-	map_expr_info_init(&i->untyped,    a);
-	map_entity_init(&i->foreign_procs, a);
-	map_isize_init(&i->type_info_map,  a);
-	map_ast_file_init(&i->files,       a);
-	i->type_info_count = 0;
-
-}
-
-void destroy_checker_info(CheckerInfo *i) {
-	map_tav_destroy(&i->types);
-	map_entity_destroy(&i->definitions);
-	map_entity_destroy(&i->uses);
-	map_scope_destroy(&i->scopes);
-	map_decl_info_destroy(&i->entities);
-	map_expr_info_destroy(&i->untyped);
-	map_entity_destroy(&i->foreign_procs);
-	map_isize_destroy(&i->type_info_map);
-	map_ast_file_destroy(&i->files);
-}
-
-
-void init_checker(Checker *c, Parser *parser, BaseTypeSizes sizes) {
-	gbAllocator a = heap_allocator();
-
-	c->parser = parser;
-	init_checker_info(&c->info);
-	c->sizes = sizes;
-
-	array_init(&c->proc_stack, a);
-	array_init(&c->procs, a);
-
-	// NOTE(bill): Is this big enough or too small?
-	isize item_size = gb_max3(gb_size_of(Entity), gb_size_of(Type), gb_size_of(Scope));
-	isize total_token_count = 0;
-	for_array(i, c->parser->files) {
-		AstFile *f = &c->parser->files.e[i];
-		total_token_count += f->tokens.count;
-	}
-	isize arena_size = 2 * item_size * total_token_count;
-	gb_arena_init_from_allocator(&c->arena, a, arena_size);
-	gb_arena_init_from_allocator(&c->tmp_arena, a, arena_size);
-
-
-	c->allocator     = gb_arena_allocator(&c->arena);
-	c->tmp_allocator = gb_arena_allocator(&c->tmp_arena);
-
-	c->global_scope = make_scope(universal_scope, c->allocator);
-	c->context.scope = c->global_scope;
-}
-
-void destroy_checker(Checker *c) {
-	destroy_checker_info(&c->info);
-	destroy_scope(c->global_scope);
-	array_free(&c->proc_stack);
-	array_free(&c->procs);
-
-	gb_arena_free(&c->arena);
-}
-
-
-TypeAndValue *type_and_value_of_expression(CheckerInfo *i, AstNode *expression) {
-	TypeAndValue *found = map_tav_get(&i->types, hash_pointer(expression));
-	return found;
-}
-
-
-Entity *entity_of_ident(CheckerInfo *i, AstNode *identifier) {
-	if (identifier->kind == AstNode_Ident) {
-		Entity **found = map_entity_get(&i->definitions, hash_pointer(identifier));
-		if (found) {
-			return *found;
-		}
-		found = map_entity_get(&i->uses, hash_pointer(identifier));
-		if (found) {
-			return *found;
-		}
-	}
-	return NULL;
-}
-
-Type *type_of_expr(CheckerInfo *i, AstNode *expression) {
-	TypeAndValue *found = type_and_value_of_expression(i, expression);
-	if (found) {
-		return found->type;
-	}
-	if (expression->kind == AstNode_Ident) {
-		Entity *entity = entity_of_ident(i, expression);
-		if (entity) {
-			return entity->type;
-		}
-	}
-
-	return NULL;
-}
-
-
-void add_untyped(CheckerInfo *i, AstNode *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value) {
-	map_expr_info_set(&i->untyped, hash_pointer(expression), make_expr_info(lhs, mode, basic_type, value));
-}
-
-void add_type_and_value(CheckerInfo *i, AstNode *expression, AddressingMode mode, Type *type, ExactValue value) {
-	GB_ASSERT(expression != NULL);
-	if (mode == Addressing_Invalid) {
-		return;
-	}
-
-	if (mode == Addressing_Constant) {
-		if (is_type_constant_type(type)) {
-			GB_ASSERT(value.kind != ExactValue_Invalid);
-			if (!(type != t_invalid || is_type_constant_type(type))) {
-				compiler_error("add_type_and_value - invalid type: %s", type_to_string(type));
-			}
-		}
-	}
-
-	TypeAndValue tv = {0};
-	tv.type  = type;
-	tv.value = value;
-	tv.mode  = mode;
-	map_tav_set(&i->types, hash_pointer(expression), tv);
-}
-
-void add_entity_definition(CheckerInfo *i, AstNode *identifier, Entity *entity) {
-	GB_ASSERT(identifier != NULL);
-	if (identifier->kind == AstNode_Ident) {
-		GB_ASSERT(identifier->kind == AstNode_Ident);
-		HashKey key = hash_pointer(identifier);
-		map_entity_set(&i->definitions, key, entity);
-	} else {
-		// NOTE(bill): Error should handled elsewhere
-	}
-}
-
-bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
-	if (str_ne(entity->token.string, str_lit("_"))) {
-		Entity *insert_entity = scope_insert_entity(scope, entity);
-		if (insert_entity) {
-			Entity *up = insert_entity->using_parent;
-			if (up != NULL) {
-				error(entity->token,
-				      "Redeclararation of `%.*s` in this scope through `using`\n"
-				      "\tat %.*s(%td:%td)",
-				      LIT(entity->token.string),
-				      LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
-				return false;
-			} else {
-				TokenPos pos = insert_entity->token.pos;
-				if (token_pos_are_equal(pos, entity->token.pos)) {
-					// NOTE(bill): Error should have been handled already
-					return false;
-				}
-				error(entity->token,
-				      "Redeclararation of `%.*s` in this scope\n"
-				      "\tat %.*s(%td:%td)",
-				      LIT(entity->token.string),
-				      LIT(pos.file), pos.line, pos.column);
-				return false;
-			}
-		}
-	}
-	if (identifier != NULL) {
-		add_entity_definition(&c->info, identifier, entity);
-	}
-	return true;
-}
-
-void add_entity_use(Checker *c, AstNode *identifier, Entity *entity) {
-	GB_ASSERT(identifier != NULL);
-	if (identifier->kind != AstNode_Ident) {
-		return;
-	}
-	map_entity_set(&c->info.uses, hash_pointer(identifier), entity);
-	add_declaration_dependency(c, entity); // TODO(bill): Should this be here?
-}
-
-
-void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d) {
-	GB_ASSERT(str_eq(identifier->Ident.string, e->token.string));
-	add_entity(c, e->scope, identifier, e);
-	map_decl_info_set(&c->info.entities, hash_pointer(e), d);
-}
-
-void add_type_info_type(Checker *c, Type *t) {
-	if (t == NULL) {
-		return;
-	}
-	t = default_type(t);
-	if (is_type_untyped(t)) {
-		return; // Could be nil
-	}
-
-	if (map_isize_get(&c->info.type_info_map, hash_pointer(t)) != NULL) {
-		// Types have already been added
-		return;
-	}
-
-	isize ti_index = -1;
-	for_array(i, c->info.type_info_map.entries) {
-		MapIsizeEntry *e = &c->info.type_info_map.entries.e[i];
-		Type *prev_type = cast(Type *)e->key.ptr;
-		if (are_types_identical(t, prev_type)) {
-			// Duplicate entry
-			ti_index = e->value;
-			break;
-		}
-	}
-	if (ti_index < 0) {
-		// Unique entry
-		// NOTE(bill): map entries grow linearly and in order
-		ti_index = c->info.type_info_count;
-		c->info.type_info_count++;
-	}
-	map_isize_set(&c->info.type_info_map, hash_pointer(t), ti_index);
-
-
-
-
-	// Add nested types
-
-	if (t->kind == Type_Named) {
-		// NOTE(bill): Just in case
-		add_type_info_type(c, t->Named.base);
-		return;
-	}
-
-	Type *bt = base_type(t);
-	add_type_info_type(c, bt);
-
-	switch (bt->kind) {
-	case Type_Basic: {
-		switch (bt->Basic.kind) {
-		case Basic_string:
-			add_type_info_type(c, t_u8_ptr);
-			add_type_info_type(c, t_int);
-			break;
-		case Basic_any:
-			add_type_info_type(c, t_type_info_ptr);
-			add_type_info_type(c, t_rawptr);
-			break;
-		}
-	} break;
-
-	case Type_Maybe:
-		add_type_info_type(c, bt->Maybe.elem);
-		add_type_info_type(c, t_bool);
-		break;
-
-	case Type_Pointer:
-		add_type_info_type(c, bt->Pointer.elem);
-		break;
-
-	case Type_Array:
-		add_type_info_type(c, bt->Array.elem);
-		add_type_info_type(c, make_type_pointer(c->allocator, bt->Array.elem));
-		add_type_info_type(c, t_int);
-		break;
-	case Type_Slice:
-		add_type_info_type(c, bt->Slice.elem);
-		add_type_info_type(c, make_type_pointer(c->allocator, bt->Slice.elem));
-		add_type_info_type(c, t_int);
-		break;
-	case Type_Vector:
-		add_type_info_type(c, bt->Vector.elem);
-		add_type_info_type(c, t_int);
-		break;
-
-	case Type_Record: {
-		switch (bt->Record.kind) {
-		case TypeRecord_Enum:
-			add_type_info_type(c, bt->Record.enum_base);
-			break;
-
-		case TypeRecord_Union:
-			add_type_info_type(c, t_int);
-			/* fallthrough */
-		default:
-			for (isize i = 0; i < bt->Record.field_count; i++) {
-				Entity *f = bt->Record.fields[i];
-				add_type_info_type(c, f->type);
-			}
-			break;
-		}
-	} break;
-
-	case Type_Tuple:
-		for (isize i = 0; i < bt->Tuple.variable_count; i++) {
-			Entity *var = bt->Tuple.variables[i];
-			add_type_info_type(c, var->type);
-		}
-		break;
-
-	case Type_Proc:
-		add_type_info_type(c, bt->Proc.params);
-		add_type_info_type(c, bt->Proc.results);
-		break;
-	}
-}
-
-
-void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u32 tags) {
-	ProcedureInfo info = {0};
-	info.file = file;
-	info.token = token;
-	info.decl  = decl;
-	info.type  = type;
-	info.body  = body;
-	info.tags  = tags;
-	array_add(&c->procs, info);
-}
-
-void push_procedure(Checker *c, Type *type) {
-	array_add(&c->proc_stack, type);
-}
-
-void pop_procedure(Checker *c) {
-	array_pop(&c->proc_stack);
-}
-
-Type *const curr_procedure(Checker *c) {
-	isize count = c->proc_stack.count;
-	if (count > 0) {
-		return c->proc_stack.e[count-1];
-	}
-	return NULL;
-}
-
-void add_curr_ast_file(Checker *c, AstFile *file) {
-	TokenPos zero_pos = {0};
-	global_error_collector.prev = zero_pos;
-	c->curr_ast_file = file;
-	c->context.decl = file->decl_info;
-}
-
-
-
-
-void add_dependency_to_map(MapEntity *map, CheckerInfo *info, Entity *node) {
-	if (node == NULL) {
-		return;
-	}
-	if (map_entity_get(map, hash_pointer(node)) != NULL) {
-		return;
-	}
-	map_entity_set(map, hash_pointer(node), node);
-
-
-	DeclInfo **found = map_decl_info_get(&info->entities, hash_pointer(node));
-	if (found == NULL) {
-		return;
-	}
-
-	DeclInfo *decl = *found;
-	for_array(i, decl->deps.entries) {
-		Entity *e = cast(Entity *)decl->deps.entries.e[i].key.ptr;
-		add_dependency_to_map(map, info, e);
-	}
-}
-
-MapEntity generate_minimum_dependency_map(CheckerInfo *info, Entity *start) {
-	MapEntity map = {0}; // Key: Entity *
-	map_entity_init(&map, heap_allocator());
-
-	for_array(i, info->entities.entries) {
-		MapDeclInfoEntry *entry = &info->entities.entries.e[i];
-		Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
-		if (e->scope->is_global) {
-			// NOTE(bill): Require runtime stuff
-			add_dependency_to_map(&map, info, e);
-		}
-	}
-
-	add_dependency_to_map(&map, info, start);
-
-	return map;
-}
-
-
-
-
-#include "expr.cpp"
-#include "decl.cpp"
-#include "stmt.cpp"
-
-void init_preload_types(Checker *c) {
-	if (t_type_info == NULL) {
-		Entity *e = current_scope_lookup_entity(c->global_scope, str_lit("Type_Info"));
-		if (e == NULL) {
-			compiler_error("Could not find type declaration for `Type_Info`\n"
-			               "Is `runtime.odin` missing from the `core` directory relative to odin.exe?");
-		}
-		t_type_info = e->type;
-		t_type_info_ptr = make_type_pointer(c->allocator, t_type_info);
-		GB_ASSERT(is_type_union(e->type));
-		TypeRecord *record = &base_type(e->type)->Record;
-
-		t_type_info_member = record->other_fields[0]->type;
-		t_type_info_member_ptr = make_type_pointer(c->allocator, t_type_info_member);
-
-		if (record->field_count != 18) {
-			compiler_error("Invalid `Type_Info` layout");
-		}
-		t_type_info_named     = record->fields[ 1]->type;
-		t_type_info_integer   = record->fields[ 2]->type;
-		t_type_info_float     = record->fields[ 3]->type;
-		t_type_info_any       = record->fields[ 4]->type;
-		t_type_info_string    = record->fields[ 5]->type;
-		t_type_info_boolean   = record->fields[ 6]->type;
-		t_type_info_pointer   = record->fields[ 7]->type;
-		t_type_info_maybe     = record->fields[ 8]->type;
-		t_type_info_procedure = record->fields[ 9]->type;
-		t_type_info_array     = record->fields[10]->type;
-		t_type_info_slice     = record->fields[11]->type;
-		t_type_info_vector    = record->fields[12]->type;
-		t_type_info_tuple     = record->fields[13]->type;
-		t_type_info_struct    = record->fields[14]->type;
-		t_type_info_union     = record->fields[15]->type;
-		t_type_info_raw_union = record->fields[16]->type;
-		t_type_info_enum      = record->fields[17]->type;
-	}
-
-	if (t_allocator == NULL) {
-		Entity *e = current_scope_lookup_entity(c->global_scope, str_lit("Allocator"));
-		if (e == NULL) {
-			compiler_error("Could not find type declaration for `Allocator`\n"
-			               "Is `runtime.odin` missing from the `core` directory relative to odin.exe?");
-		}
-		t_allocator = e->type;
-		t_allocator_ptr = make_type_pointer(c->allocator, t_allocator);
-	}
-
-	if (t_context == NULL) {
-		Entity *e = current_scope_lookup_entity(c->global_scope, str_lit("Context"));
-		if (e == NULL) {
-			compiler_error("Could not find type declaration for `Context`\n"
-			               "Is `runtime.odin` missing from the `core` directory relative to odin.exe?");
-		}
-		t_context = e->type;
-		t_context_ptr = make_type_pointer(c->allocator, t_context);
-
-	}
-
-}
-
-void add_implicit_value(Checker *c, ImplicitValueId id, String name, String backing_name, Type *type) {
-	ImplicitValueInfo info = {name, backing_name, type};
-	Entity *value = make_entity_implicit_value(c->allocator, info.name, info.type, id);
-	Entity *prev = scope_insert_entity(c->global_scope, value);
-	GB_ASSERT(prev == NULL);
-	implicit_value_infos[id] = info;
-	c->info.implicit_values[id] = value;
-}
-
-
-void check_global_entity(Checker *c, EntityKind kind) {
-	for_array(i, c->info.entities.entries) {
-		MapDeclInfoEntry *entry = &c->info.entities.entries.e[i];
-		Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
-		if (e->kind == kind) {
-			DeclInfo *d = entry->value;
-
-			add_curr_ast_file(c, d->scope->file);
-
-			if (d->scope == e->scope) {
-				if (kind != Entity_Procedure && str_eq(e->token.string, str_lit("main"))) {
-					if (e->scope->is_init) {
-						error(e->token, "`main` is reserved as the entry point procedure in the initial scope");
-						continue;
-					}
-				} else if (e->scope->is_global && str_eq(e->token.string, str_lit("main"))) {
-					error(e->token, "`main` is reserved as the entry point procedure in the initial scope");
-					continue;
-				}
-
-				Scope *prev_scope = c->context.scope;
-				c->context.scope = d->scope;
-				check_entity_decl(c, e, d, NULL, NULL);
-			}
-		}
-	}
-}
-
-void check_parsed_files(Checker *c) {
-	AstNodeArray import_decls;
-	array_init(&import_decls, heap_allocator());
-
-	MapScope file_scopes; // Key: String (fullpath)
-	map_scope_init(&file_scopes, heap_allocator());
-
-	// Map full filepaths to Scopes
-	for_array(i, c->parser->files) {
-		AstFile *f = &c->parser->files.e[i];
-		Scope *scope = NULL;
-		scope = make_scope(c->global_scope, c->allocator);
-		scope->is_global = f->is_global_scope;
-		scope->is_file   = true;
-		scope->file      = f;
-		if (i == 0) {
-			// NOTE(bill): First file is always the initial file
-			// thus it must contain main
-			scope->is_init = true;
-		}
-
-		if (scope->is_global) {
-			array_add(&c->global_scope->shared, scope);
-		}
-
-		f->scope = scope;
-		f->decl_info = make_declaration_info(c->allocator, f->scope);
-		HashKey key = hash_string(f->tokenizer.fullpath);
-		map_scope_set(&file_scopes, key, scope);
-		map_ast_file_set(&c->info.files, key, f);
-	}
-
-	// Collect Entities
-	for_array(i, c->parser->files) {
-		AstFile *f = &c->parser->files.e[i];
-		add_curr_ast_file(c, f);
-
-		Scope *file_scope = f->scope;
-
-		for_array(decl_index, f->decls) {
-			AstNode *decl = f->decls.e[decl_index];
-			if (!is_ast_node_decl(decl)) {
-				continue;
-			}
-
-			switch (decl->kind) {
-			case_ast_node(bd, BadDecl, decl);
-			case_end;
-			case_ast_node(id, ImportDecl, decl);
-				// NOTE(bill): Handle later
-			case_end;
-			case_ast_node(fsl, ForeignLibrary, decl);
-				// NOTE(bill): ignore
-			case_end;
-
-			case_ast_node(cd, ConstDecl, decl);
-				for_array(i, cd->values) {
-					AstNode *name = cd->names.e[i];
-					AstNode *value = cd->values.e[i];
-					ExactValue v = {ExactValue_Invalid};
-					Entity *e = make_entity_constant(c->allocator, file_scope, name->Ident, NULL, v);
-					e->identifier = name;
-					DeclInfo *di = make_declaration_info(c->allocator, file_scope);
-					di->type_expr = cd->type;
-					di->init_expr = value;
-					add_entity_and_decl_info(c, name, e, di);
-				}
-
-				isize lhs_count = cd->names.count;
-				isize rhs_count = cd->values.count;
-
-				if (rhs_count == 0 && cd->type == NULL) {
-					error(ast_node_token(decl), "Missing type or initial expression");
-				} else if (lhs_count < rhs_count) {
-					error(ast_node_token(decl), "Extra initial expression");
-				}
-			case_end;
-
-			case_ast_node(vd, VarDecl, decl);
-				isize entity_count = vd->names.count;
-				isize entity_index = 0;
-				Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
-				DeclInfo *di = NULL;
-				if (vd->values.count > 0) {
-					di = make_declaration_info(heap_allocator(), file_scope);
-					di->entities = entities;
-					di->entity_count = entity_count;
-					di->type_expr = vd->type;
-					di->init_expr = vd->values.e[0];
-				}
-
-				for_array(i, vd->names) {
-					AstNode *name = vd->names.e[i];
-					AstNode *value = NULL;
-					if (i < vd->values.count) {
-						value = vd->values.e[i];
-					}
-					Entity *e = make_entity_variable(c->allocator, file_scope, name->Ident, NULL);
-					e->identifier = name;
-					entities[entity_index++] = e;
-
-					DeclInfo *d = di;
-					if (d == NULL) {
-						AstNode *init_expr = value;
-						d = make_declaration_info(heap_allocator(), file_scope);
-						d->type_expr = vd->type;
-						d->init_expr = init_expr;
-						d->var_decl_tags = vd->tags;
-					}
-
-					add_entity_and_decl_info(c, name, e, d);
-				}
-			case_end;
-
-			case_ast_node(td, TypeDecl, decl);
-				ast_node(n, Ident, td->name);
-				Entity *e = make_entity_type_name(c->allocator, file_scope, *n, NULL);
-				e->identifier = td->name;
-				DeclInfo *d = make_declaration_info(c->allocator, e->scope);
-				d->type_expr = td->type;
-				add_entity_and_decl_info(c, td->name, e, d);
-			case_end;
-
-			case_ast_node(pd, ProcDecl, decl);
-				ast_node(n, Ident, pd->name);
-				Token token = *n;
-				Entity *e = make_entity_procedure(c->allocator, file_scope, token, NULL);
-				e->identifier = pd->name;
-				DeclInfo *d = make_declaration_info(c->allocator, e->scope);
-				d->proc_decl = decl;
-				add_entity_and_decl_info(c, pd->name, e, d);
-			case_end;
-
-			default:
-				error(ast_node_token(decl), "Only declarations are allowed at file scope");
-				break;
-			}
-		}
-	}
-
-	for_array(i, c->parser->files) {
-		AstFile *f = &c->parser->files.e[i];
-		add_curr_ast_file(c, f);
-
-		Scope *file_scope = f->scope;
-
-		for_array(decl_index, f->decls) {
-			AstNode *decl = f->decls.e[decl_index];
-			if (decl->kind != AstNode_ImportDecl) {
-				continue;
-			}
-			ast_node(id, ImportDecl, decl);
-
-			HashKey key = hash_string(id->fullpath);
-			Scope **found = map_scope_get(&file_scopes, key);
-			GB_ASSERT_MSG(found != NULL, "Unable to find scope for file: %.*s", LIT(id->fullpath));
-			Scope *scope = *found;
-
-			if (scope->is_global) {
-				error(id->token, "Importing a #shared_global_scope is disallowed and unnecessary");
-				continue;
-			}
-
-			bool previously_added = false;
-			for_array(import_index, file_scope->imported) {
-				Scope *prev = file_scope->imported.e[import_index];
-				if (prev == scope) {
-					previously_added = true;
-					break;
-				}
-			}
-
-			if (!previously_added) {
-				array_add(&file_scope->imported, scope);
-			} else {
-				warning(id->token, "Multiple #import of the same file within this scope");
-			}
-
-			if (str_eq(id->import_name.string, str_lit("."))) {
-				// NOTE(bill): Add imported entities to this file's scope
-				for_array(elem_index, scope->elements.entries) {
-					Entity *e = scope->elements.entries.e[elem_index].value;
-					if (e->scope == file_scope) {
-						continue;
-					}
-					// NOTE(bill): Do not add other imported entities
-					if (is_entity_exported(e)) {
-						add_entity(c, file_scope, NULL, e);
-						if (!id->is_load) { // `#import`ed entities don't get exported
-							HashKey key = hash_string(e->token.string);
-							map_entity_set(&file_scope->implicit, key, e);
-						}
-					}
-				}
-			} else {
-				String import_name = id->import_name.string;
-				if (import_name.len == 0) {
-					// NOTE(bill): use file name (without extension) as the identifier
-					// If it is a valid identifier
-					String filename = id->fullpath;
-					isize slash = 0;
-					isize dot = 0;
-					for (isize i = filename.len-1; i >= 0; i--) {
-						u8 c = filename.text[i];
-						if (c == '/' || c == '\\') {
-							break;
-						}
-						slash = i;
-					}
-
-					filename.text += slash;
-					filename.len -= slash;
-
-					dot = filename.len;
-					while (dot --> 0) {
-						u8 c = filename.text[dot];
-						if (c == '.') {
-							break;
-						}
-					}
-
-					filename.len = dot;
-
-					if (is_string_an_identifier(filename)) {
-						import_name = filename;
-					} else {
-						error(ast_node_token(decl),
-						      "File name, %.*s, cannot be as an import name as it is not a valid identifier",
-						      LIT(filename));
-					}
-				}
-
-				if (import_name.len > 0) {
-					id->import_name.string = import_name;
-					Entity *e = make_entity_import_name(c->allocator, file_scope, id->import_name, t_invalid,
-					                                    id->fullpath, id->import_name.string,
-					                                    scope);
-					add_entity(c, file_scope, NULL, e);
-				}
-			}
-		}
-	}
-
-	check_global_entity(c, Entity_TypeName);
-
-	init_preload_types(c);
-	add_implicit_value(c, ImplicitValue_context, str_lit("context"), str_lit("__context"), t_context);
-
-	check_global_entity(c, Entity_Constant);
-	check_global_entity(c, Entity_Procedure);
-	check_global_entity(c, Entity_Variable);
-
-	for (isize i = 1; i < ImplicitValue_Count; i++) {
-		// NOTE(bill): First is invalid
-		Entity *e = c->info.implicit_values[i];
-		GB_ASSERT(e->kind == Entity_ImplicitValue);
-
-		ImplicitValueInfo *ivi = &implicit_value_infos[i];
-		Entity *backing = scope_lookup_entity(e->scope, ivi->backing_name);
-		GB_ASSERT(backing != NULL);
-		e->ImplicitValue.backing = backing;
-	}
-
-
-	// Check procedure bodies
-	for_array(i, c->procs) {
-		ProcedureInfo *pi = &c->procs.e[i];
-		add_curr_ast_file(c, pi->file);
-
-		bool bounds_check    = (pi->tags & ProcTag_bounds_check)    != 0;
-		bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0;
-
-		CheckerContext prev_context = c->context;
-
-		if (bounds_check) {
-			c->context.stmt_state_flags |= StmtStateFlag_bounds_check;
-			c->context.stmt_state_flags &= ~StmtStateFlag_no_bounds_check;
-		} else if (no_bounds_check) {
-			c->context.stmt_state_flags |= StmtStateFlag_no_bounds_check;
-			c->context.stmt_state_flags &= ~StmtStateFlag_bounds_check;
-		}
-
-		check_proc_body(c, pi->token, pi->decl, pi->type, pi->body);
-
-		c->context = prev_context;
-	}
-
-	// Add untyped expression values
-	for_array(i, c->info.untyped.entries) {
-		MapExprInfoEntry *entry = &c->info.untyped.entries.e[i];
-		HashKey key = entry->key;
-		AstNode *expr = cast(AstNode *)cast(uintptr)key.key;
-		ExprInfo *info = &entry->value;
-		if (info != NULL && expr != NULL) {
-			if (is_type_typed(info->type)) {
-				compiler_error("%s (type %s) is typed!", expr_to_string(expr), type_to_string(info->type));
-			}
-			add_type_and_value(&c->info, expr, info->mode, info->type, info->value);
-		}
-	}
-
-	for (isize i = 0; i < gb_count_of(basic_types)-1; i++) {
-		Type *t = &basic_types[i];
-		if (t->Basic.size > 0) {
-			add_type_info_type(c, t);
-		}
-	}
-
-	for (isize i = 0; i < gb_count_of(basic_type_aliases)-1; i++) {
-		Type *t = &basic_type_aliases[i];
-		if (t->Basic.size > 0) {
-			add_type_info_type(c, t);
-		}
-	}
-
-	map_scope_destroy(&file_scopes);
-	array_free(&import_decls);
-}
-
-
-

+ 0 - 545
src/checker/decl.cpp

@@ -1,545 +0,0 @@
-bool check_is_terminating(AstNode *node);
-void check_stmt         (Checker *c, AstNode *node, u32 flags);
-void check_stmt_list    (Checker *c, AstNodeArray stmts, u32 flags);
-void check_type_decl    (Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker);
-void check_const_decl   (Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr);
-void check_proc_decl    (Checker *c, Entity *e, DeclInfo *d);
-void check_var_decl     (Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr);
-
-// NOTE(bill): `content_name` is for debugging and error messages
-Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) {
-	if (operand->mode == Addressing_Invalid ||
-	    operand->type == t_invalid ||
-	    e->type == t_invalid) {
-
-		if (operand->mode == Addressing_Builtin) {
-			gbString expr_str = expr_to_string(operand->expr);
-
-			// TODO(bill): is this a good enough error message?
-			error(ast_node_token(operand->expr),
-			      "Cannot assign builtin procedure `%s` in %.*s",
-			      expr_str,
-			      LIT(context_name));
-
-			operand->mode = Addressing_Invalid;
-
-			gb_string_free(expr_str);
-		}
-
-
-		if (e->type == NULL) {
-			e->type = t_invalid;
-		}
-		return NULL;
-	}
-
-	if (e->type == NULL) {
-		// NOTE(bill): Use the type of the operand
-		Type *t = operand->type;
-		if (is_type_untyped(t)) {
-			if (t == t_invalid || is_type_untyped_nil(t)) {
-				error(e->token, "Use of untyped nil in %.*s", LIT(context_name));
-				e->type = t_invalid;
-				return NULL;
-			}
-			t = default_type(t);
-		}
-		e->type = t;
-	}
-
-	check_assignment(c, operand, e->type, context_name);
-	if (operand->mode == Addressing_Invalid) {
-		return NULL;
-	}
-
-	return e->type;
-}
-
-void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArray inits, String context_name) {
-	if ((lhs == NULL || lhs_count == 0) && inits.count == 0) {
-		return;
-	}
-
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-
-	// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
-	// an extra allocation
-	Array(Operand) operands;
-	array_init_reserve(&operands, c->tmp_allocator, 2*lhs_count);
-
-	for_array(i, inits) {
-		AstNode *rhs = inits.e[i];
-		Operand o = {0};
-		check_multi_expr(c, &o, rhs);
-		if (o.type->kind != Type_Tuple) {
-			array_add(&operands, o);
-		} else {
-			TypeTuple *tuple = &o.type->Tuple;
-			for (isize j = 0; j < tuple->variable_count; j++) {
-				o.type = tuple->variables[j]->type;
-				array_add(&operands, o);
-			}
-		}
-	}
-
-	isize rhs_count = operands.count;
-	for_array(i, operands) {
-		if (operands.e[i].mode == Addressing_Invalid) {
-			rhs_count--;
-		}
-	}
-
-
-	isize max = gb_min(lhs_count, rhs_count);
-	for (isize i = 0; i < max; i++) {
-		check_init_variable(c, lhs[i], &operands.e[i], context_name);
-	}
-
-	if (rhs_count > 0 && lhs_count != rhs_count) {
-		error(lhs[0]->token, "Assignment count mismatch `%td` := `%td`", lhs_count, rhs_count);
-	}
-
-	gb_temp_arena_memory_end(tmp);
-}
-
-
-
-void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, CycleChecker *cycle_checker) {
-	if (e->type != NULL) {
-		return;
-	}
-
-	if (d == NULL) {
-		DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e));
-		if (found) {
-			d = *found;
-		} else {
-			e->type = t_invalid;
-			set_base_type(named_type, t_invalid);
-			return;
-			// GB_PANIC("`%.*s` should been declared!", LIT(e->token.string));
-		}
-	}
-
-	if (e->kind == Entity_Procedure) {
-		check_proc_decl(c, e, d);
-		return;
-	}
-	CheckerContext prev = c->context;
-	c->context.scope = d->scope;
-	c->context.decl  = d;
-
-	switch (e->kind) {
-	case Entity_Constant:
-		check_const_decl(c, e, d->type_expr, d->init_expr);
-		break;
-	case Entity_Variable:
-		check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr);
-		break;
-	case Entity_TypeName:
-		check_type_decl(c, e, d->type_expr, named_type, cycle_checker);
-		break;
-	}
-
-	c->context = prev;
-}
-
-
-
-void check_var_decl_node(Checker *c, AstNode *node) {
-	ast_node(vd, VarDecl, node);
-	isize entity_count = vd->names.count;
-	isize entity_index = 0;
-	Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
-
-	for_array(i, vd->names) {
-		AstNode *name = vd->names.e[i];
-		Entity *entity = NULL;
-		if (name->kind == AstNode_Ident) {
-			Token token = name->Ident;
-			String str = token.string;
-			Entity *found = NULL;
-			// NOTE(bill): Ignore assignments to `_`
-			if (str_ne(str, str_lit("_"))) {
-				found = current_scope_lookup_entity(c->context.scope, str);
-			}
-			if (found == NULL) {
-				entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
-				add_entity_definition(&c->info, name, entity);
-			} else {
-				TokenPos pos = found->token.pos;
-				error(token,
-				      "Redeclaration of `%.*s` in this scope\n"
-				      "\tat %.*s(%td:%td)",
-				      LIT(str), LIT(pos.file), pos.line, pos.column);
-				entity = found;
-			}
-		} else {
-			error(ast_node_token(name), "A variable declaration must be an identifier");
-		}
-		if (entity == NULL) {
-			entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
-		}
-		entities[entity_index++] = entity;
-	}
-
-	Type *init_type = NULL;
-	if (vd->type) {
-		init_type = check_type_extra(c, vd->type, NULL, NULL);
-		if (init_type == NULL)
-			init_type = t_invalid;
-	}
-
-	for (isize i = 0; i < entity_count; i++) {
-		Entity *e = entities[i];
-		GB_ASSERT(e != NULL);
-		if (e->flags & EntityFlag_Visited) {
-			e->type = t_invalid;
-			continue;
-		}
-		e->flags |= EntityFlag_Visited;
-
-		if (e->type == NULL)
-			e->type = init_type;
-	}
-
-	check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
-
-	for_array(i, vd->names) {
-		if (entities[i] != NULL) {
-			add_entity(c, c->context.scope, vd->names.e[i], entities[i]);
-		}
-	}
-
-}
-
-
-
-void check_init_constant(Checker *c, Entity *e, Operand *operand) {
-	if (operand->mode == Addressing_Invalid ||
-	    operand->type == t_invalid ||
-	    e->type == t_invalid) {
-		if (e->type == NULL) {
-			e->type = t_invalid;
-		}
-		return;
-	}
-
-	if (operand->mode != Addressing_Constant) {
-		// TODO(bill): better error
-		error(ast_node_token(operand->expr),
-		      "`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string));
-		if (e->type == NULL) {
-			e->type = t_invalid;
-		}
-		return;
-	}
-	// if (!is_type_constant_type(operand->type)) {
-	// 	gbString type_str = type_to_string(operand->type);
-	// 	defer (gb_string_free(type_str));
-	// 	error(ast_node_token(operand->expr),
-	// 	      "Invalid constant type: `%s`", type_str);
-	// 	if (e->type == NULL) {
-	// 		e->type = t_invalid;
-	// 	}
-	// 	return;
-	// }
-
-	if (e->type == NULL) { // NOTE(bill): type inference
-		e->type = operand->type;
-	}
-
-	check_assignment(c, operand, e->type, str_lit("constant declaration"));
-	if (operand->mode == Addressing_Invalid) {
-		return;
-	}
-
-	e->Constant.value = operand->value;
-}
-
-
-void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr) {
-	GB_ASSERT(e->type == NULL);
-
-	if (e->flags & EntityFlag_Visited) {
-		e->type = t_invalid;
-		return;
-	}
-	e->flags |= EntityFlag_Visited;
-
-	if (type_expr) {
-		Type *t = check_type(c, type_expr);
-		// if (!is_type_constant_type(t)) {
-		// 	gbString str = type_to_string(t);
-		// 	defer (gb_string_free(str));
-		// 	error(ast_node_token(type_expr),
-		// 	      "Invalid constant type `%s`", str);
-		// 	e->type = t_invalid;
-		// 	return;
-		// }
-		e->type = t;
-	}
-
-	Operand operand = {0};
-	if (init_expr) {
-		check_expr(c, &operand, init_expr);
-	}
-	check_init_constant(c, e, &operand);
-}
-
-void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker) {
-	GB_ASSERT(e->type == NULL);
-	Type *named = make_type_named(c->allocator, e->token.string, NULL, e);
-	named->Named.type_name = e;
-	if (def != NULL && def->kind == Type_Named) {
-		def->Named.base = named;
-	}
-	e->type = named;
-
-	CycleChecker local_cycle_checker = {0};
-	if (cycle_checker == NULL) {
-		cycle_checker = &local_cycle_checker;
-	}
-
-	Type *bt = check_type_extra(c, type_expr, named, cycle_checker_add(cycle_checker, e));
-	named->Named.base = bt;
-	named->Named.base = base_type(named->Named.base);
-	if (named->Named.base == t_invalid) {
-		gb_printf("check_type_decl: %s\n", type_to_string(named));
-	}
-
-	cycle_checker_destroy(&local_cycle_checker);
-}
-
-
-bool are_signatures_similar_enough(Type *a_, Type *b_) {
-	GB_ASSERT(a_->kind == Type_Proc);
-	GB_ASSERT(b_->kind == Type_Proc);
-	TypeProc *a = &a_->Proc;
-	TypeProc *b = &b_->Proc;
-
-	if (a->param_count != b->param_count) {
-		return false;
-	}
-	if (a->result_count != b->result_count) {
-		return false;
-	}
-	for (isize i = 0; i < a->param_count; i++) {
-		Type *x = base_type(a->params->Tuple.variables[i]->type);
-		Type *y = base_type(b->params->Tuple.variables[i]->type);
-		if (is_type_pointer(x) && is_type_pointer(y)) {
-			continue;
-		}
-
-		if (!are_types_identical(x, y)) {
-			return false;
-		}
-	}
-	for (isize i = 0; i < a->result_count; i++) {
-		Type *x = base_type(a->results->Tuple.variables[i]->type);
-		Type *y = base_type(b->results->Tuple.variables[i]->type);
-		if (is_type_pointer(x) && is_type_pointer(y)) {
-			continue;
-		}
-
-		if (!are_types_identical(x, y)) {
-			return false;
-		}
-	}
-
-	return true;
-}
-
-void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
-	GB_ASSERT(e->type == NULL);
-
-	Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false);
-	e->type = proc_type;
-	ast_node(pd, ProcDecl, d->proc_decl);
-
-	check_open_scope(c, pd->type);
-	check_procedure_type(c, proc_type, pd->type);
-
-	bool is_foreign      = (pd->tags & ProcTag_foreign)   != 0;
-	bool is_link_name    = (pd->tags & ProcTag_link_name) != 0;
-	bool is_inline       = (pd->tags & ProcTag_inline)    != 0;
-	bool is_no_inline    = (pd->tags & ProcTag_no_inline) != 0;
-
-	if ((d->scope->is_file || d->scope->is_global) &&
-	    str_eq(e->token.string, str_lit("main"))) {
-		if (proc_type != NULL) {
-			TypeProc *pt = &proc_type->Proc;
-			if (pt->param_count != 0 ||
-			    pt->result_count) {
-				gbString str = type_to_string(proc_type);
-				error(e->token,
-				      "Procedure type of `main` was expected to be `proc()`, got %s", str);
-				gb_string_free(str);
-			}
-		}
-	}
-
-	if (is_inline && is_no_inline) {
-		error(ast_node_token(pd->type),
-		      "You cannot apply both `inline` and `no_inline` to a procedure");
-	}
-
-	if (is_foreign && is_link_name) {
-		error(ast_node_token(pd->type),
-		      "You cannot apply both `foreign` and `link_name` to a procedure");
-	}
-
-	if (pd->body != NULL) {
-		if (is_foreign) {
-			error(ast_node_token(pd->body),
-			      "A procedure tagged as `#foreign` cannot have a body");
-		}
-
-		d->scope = c->context.scope;
-
-		GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
-		check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags);
-	}
-
-	if (is_foreign) {
-		MapEntity *fp = &c->info.foreign_procs;
-		AstNodeProcDecl *proc_decl = &d->proc_decl->ProcDecl;
-		String name = proc_decl->name->Ident.string;
-		if (proc_decl->foreign_name.len > 0) {
-			name = proc_decl->foreign_name;
-		}
-		HashKey key = hash_string(name);
-		Entity **found = map_entity_get(fp, key);
-		if (found) {
-			Entity *f = *found;
-			TokenPos pos = f->token.pos;
-			Type *this_type = base_type(e->type);
-			Type *other_type = base_type(f->type);
-			if (!are_signatures_similar_enough(this_type, other_type)) {
-				error(ast_node_token(d->proc_decl),
-				      "Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
-				      "\tat %.*s(%td:%td)",
-				      LIT(name), LIT(pos.file), pos.line, pos.column);
-			}
-		} else {
-			map_entity_set(fp, key, e);
-		}
-	} else if (is_link_name) {
-		MapEntity *fp = &c->info.foreign_procs;
-		AstNodeProcDecl *proc_decl = &d->proc_decl->ProcDecl;
-		String name = proc_decl->link_name;
-
-		HashKey key = hash_string(name);
-		Entity **found = map_entity_get(fp, key);
-		if (found) {
-			Entity *f = *found;
-			TokenPos pos = f->token.pos;
-			error(ast_node_token(d->proc_decl),
-			      "Non unique #link_name for procedure `%.*s`\n"
-			      "\tother at %.*s(%td:%td)",
-			      LIT(name), LIT(pos.file), pos.line, pos.column);
-		} else {
-			map_entity_set(fp, key, e);
-		}
-	}
-
-	check_close_scope(c);
-}
-
-void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
-	GB_ASSERT(e->type == NULL);
-	GB_ASSERT(e->kind == Entity_Variable);
-
-	if (e->flags & EntityFlag_Visited) {
-		e->type = t_invalid;
-		return;
-	}
-	e->flags |= EntityFlag_Visited;
-
-	if (type_expr != NULL)
-		e->type = check_type_extra(c, type_expr, NULL, NULL);
-
-	if (init_expr == NULL) {
-		if (type_expr == NULL)
-			e->type = t_invalid;
-		return;
-	}
-
-	if (entities == NULL || entity_count == 1) {
-		GB_ASSERT(entities == NULL || entities[0] == e);
-		Operand operand = {0};
-		check_expr(c, &operand, init_expr);
-		check_init_variable(c, e, &operand, str_lit("variable declaration"));
-	}
-
-	if (type_expr != NULL) {
-		for (isize i = 0; i < entity_count; i++)
-			entities[i]->type = e->type;
-	}
-
-	AstNodeArray inits;
-	array_init_reserve(&inits, c->allocator, 1);
-	array_add(&inits, init_expr);
-	check_init_variables(c, entities, entity_count, inits, str_lit("variable declaration"));
-}
-
-void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) {
-	GB_ASSERT(body->kind == AstNode_BlockStmt);
-
-	CheckerContext old_context = c->context;
-	c->context.scope = decl->scope;
-	c->context.decl = decl;
-
-	GB_ASSERT(type->kind == Type_Proc);
-	if (type->Proc.param_count > 0) {
-		TypeTuple *params = &type->Proc.params->Tuple;
-		for (isize i = 0; i < params->variable_count; i++) {
-			Entity *e = params->variables[i];
-			GB_ASSERT(e->kind == Entity_Variable);
-			if (!(e->flags & EntityFlag_Anonymous)) {
-				continue;
-			}
-			String name = e->token.string;
-			Type *t = base_type(type_deref(e->type));
-			if (is_type_struct(t) || is_type_raw_union(t)) {
-				Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
-				GB_ASSERT(found != NULL);
-				for_array(i, (*found)->elements.entries) {
-					Entity *f = (*found)->elements.entries.e[i].value;
-					if (f->kind == Entity_Variable) {
-						Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
-						Entity *prev = scope_insert_entity(c->context.scope, uvar);
-						if (prev != NULL) {
-							error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
-							break;
-						}
-					}
-				}
-			} else {
-				error(e->token, "`using` can only be applied to variables of type struct or raw_union");
-				break;
-			}
-		}
-	}
-
-	push_procedure(c, type);
-	{
-		ast_node(bs, BlockStmt, body);
-		// TODO(bill): Check declarations first (except mutable variable declarations)
-		check_stmt_list(c, bs->stmts, 0);
-		if (type->Proc.result_count > 0) {
-			if (!check_is_terminating(body)) {
-				error(bs->close, "Missing return statement at the end of the procedure");
-			}
-		}
-	}
-	pop_procedure(c);
-
-
-	check_scope_usage(c, c->context.scope);
-
-	c->context = old_context;
-}
-
-
-

+ 0 - 193
src/checker/entity.cpp

@@ -1,193 +0,0 @@
-typedef struct Scope Scope;
-typedef struct Checker Checker;
-typedef struct Type Type;
-typedef enum BuiltinProcId BuiltinProcId;
-typedef enum ImplicitValueId ImplicitValueId;
-
-#define ENTITY_KINDS \
-	ENTITY_KIND(Invalid) \
-	ENTITY_KIND(Constant) \
-	ENTITY_KIND(Variable) \
-	ENTITY_KIND(TypeName) \
-	ENTITY_KIND(Procedure) \
-	ENTITY_KIND(Builtin) \
-	ENTITY_KIND(ImportName) \
-	ENTITY_KIND(Nil) \
-	ENTITY_KIND(ImplicitValue) \
-	ENTITY_KIND(Count)
-
-typedef enum EntityKind {
-#define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
-	ENTITY_KINDS
-#undef ENTITY_KIND
-} EntityKind;
-
-String const entity_strings[] = {
-#define ENTITY_KIND(k) {cast(u8 *)#k, gb_size_of(#k)-1},
-	ENTITY_KINDS
-#undef ENTITY_KIND
-};
-
-typedef enum EntityFlag {
-	EntityFlag_Visited    = 1<<0,
-	EntityFlag_Used       = 1<<1,
-	EntityFlag_Anonymous  = 1<<2,
-	EntityFlag_Field      = 1<<3,
-	EntityFlag_Param      = 1<<4,
-	EntityFlag_VectorElem = 1<<5,
-} EntityFlag;
-
-typedef struct Entity Entity;
-struct Entity {
-	EntityKind kind;
-	u32        flags;
-	Token      token;
-	Scope *    scope;
-	Type *     type;
-	AstNode *  identifier; // Can be NULL
-
-	// TODO(bill): Cleanup how `using` works for entities
-	Entity *   using_parent;
-	AstNode *  using_expr;
-
-	union {
-		struct {
-			ExactValue value;
-		} Constant;
-		struct {
-			i32 field_index;
-			i32 field_src_index;
-		} Variable;
-		i32 TypeName;
-		i32 Procedure;
-		struct {
-			BuiltinProcId id;
-		} Builtin;
-		struct {
-			String path;
-			String name;
-			Scope *scope;
-			bool    used;
-		} ImportName;
-		i32 Nil;
-		struct {
-			// TODO(bill): Should this be a user-level construct rather than compiler-level?
-			ImplicitValueId id;
-			Entity *        backing;
-		} ImplicitValue;
-	};
-};
-
-bool is_entity_exported(Entity *e) {
-	if (e->kind == Entity_ImportName) {
-		return false;
-	}
-	// TODO(bill): Do I really want non-exported entities?
-	// TODO(bill): If we do, what should be the rules?
-	// if (e->token.string.len >= 1 &&
-	    // e->token.string.text[0] == '_') {
-		// return false;
-	// }
-	return true;
-}
-
-
-Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
-	Entity *entity = gb_alloc_item(a, Entity);
-	entity->kind   = kind;
-	entity->scope  = scope;
-	entity->token  = token;
-	entity->type   = type;
-	return entity;
-}
-
-Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *type) {
-	Entity *entity = alloc_entity(a, Entity_Variable, scope, token, type);
-	return entity;
-}
-
-Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, Type *type) {
-	GB_ASSERT(parent != NULL);
-	Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type);
-	entity->using_parent = parent;
-	entity->flags |= EntityFlag_Anonymous;
-	return entity;
-}
-
-
-Entity *make_entity_constant(gbAllocator a, Scope *scope, Token token, Type *type, ExactValue value) {
-	Entity *entity = alloc_entity(a, Entity_Constant, scope, token, type);
-	entity->Constant.value = value;
-	return entity;
-}
-
-Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *type) {
-	Entity *entity = alloc_entity(a, Entity_TypeName, scope, token, type);
-	return entity;
-}
-
-Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous) {
-	Entity *entity = make_entity_variable(a, scope, token, type);
-	entity->flags |= EntityFlag_Used;
-	entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
-	entity->flags |= EntityFlag_Param;
-	return entity;
-}
-
-Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) {
-	Entity *entity = make_entity_variable(a, scope, token, type);
-	entity->Variable.field_src_index = field_src_index;
-	entity->Variable.field_index = field_src_index;
-	entity->flags |= EntityFlag_Field;
-	entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
-	return entity;
-}
-
-Entity *make_entity_vector_elem(gbAllocator a, Scope *scope, Token token, Type *type, i32 field_src_index) {
-	Entity *entity = make_entity_variable(a, scope, token, type);
-	entity->Variable.field_src_index = field_src_index;
-	entity->Variable.field_index = field_src_index;
-	entity->flags |= EntityFlag_Field;
-	entity->flags |= EntityFlag_VectorElem;
-	return entity;
-}
-
-Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *signature_type) {
-	Entity *entity = alloc_entity(a, Entity_Procedure, scope, token, signature_type);
-	return entity;
-}
-
-Entity *make_entity_builtin(gbAllocator a, Scope *scope, Token token, Type *type, BuiltinProcId id) {
-	Entity *entity = alloc_entity(a, Entity_Builtin, scope, token, type);
-	entity->Builtin.id = id;
-	return entity;
-}
-
-Entity *make_entity_import_name(gbAllocator a, Scope *scope, Token token, Type *type,
-                                String path, String name, Scope *import_scope) {
-	Entity *entity = alloc_entity(a, Entity_ImportName, scope, token, type);
-	entity->ImportName.path = path;
-	entity->ImportName.name = name;
-	entity->ImportName.scope = import_scope;
-	return entity;
-}
-
-Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
-	Token token = make_token_ident(name);
-	Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type);
-	return entity;
-}
-
-Entity *make_entity_implicit_value(gbAllocator a, String name, Type *type, ImplicitValueId id) {
-	Token token = make_token_ident(name);
-	Entity *entity = alloc_entity(a, Entity_ImplicitValue, NULL, token, type);
-	entity->ImplicitValue.id = id;
-	return entity;
-}
-
-
-Entity *make_entity_dummy_variable(gbAllocator a, Scope *file_scope, Token token) {
-	token.string = str_lit("_");
-	return make_entity_variable(a, file_scope, token, NULL);
-}
-

+ 0 - 4452
src/checker/expr.cpp

@@ -1,4452 +0,0 @@
-void     check_expr                (Checker *c, Operand *operand, AstNode *expression);
-void     check_multi_expr          (Checker *c, Operand *operand, AstNode *expression);
-void     check_expr_or_type        (Checker *c, Operand *operand, AstNode *expression);
-ExprKind check_expr_base           (Checker *c, Operand *operand, AstNode *expression, Type *type_hint);
-Type *   check_type_extra          (Checker *c, AstNode *expression, Type *named_type, CycleChecker *cycle_checker);
-Type *   check_type                (Checker *c, AstNode *expression);
-void     check_type_decl           (Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker);
-Entity * check_selector            (Checker *c, Operand *operand, AstNode *node);
-void     check_not_tuple           (Checker *c, Operand *operand);
-bool     check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value);
-void     convert_to_typed          (Checker *c, Operand *operand, Type *target_type, i32 level);
-gbString expr_to_string            (AstNode *expression);
-void     check_entity_decl         (Checker *c, Entity *e, DeclInfo *decl, Type *named_type, CycleChecker *cycle_checker);
-void     check_proc_body           (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body);
-void     update_expr_type          (Checker *c, AstNode *e, Type *type, bool final);
-
-gb_inline Type *check_type(Checker *c, AstNode *expression) {
-	return check_type_extra(c, expression, NULL, NULL);
-}
-
-
-
-bool check_is_assignable_to_using_subtype(Type *dst, Type *src) {
-	Type *prev_src = src;
-	// Type *prev_dst = dst;
-	src = base_type(type_deref(src));
-	// dst = base_type(type_deref(dst));
-	bool src_is_ptr = src != prev_src;
-	// bool dst_is_ptr = dst != prev_dst;
-
-	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->flags & EntityFlag_Anonymous)) {
-				if (are_types_identical(dst, f->type)) {
-					return true;
-				}
-				if (src_is_ptr && is_type_pointer(dst)) {
-					if (are_types_identical(type_deref(dst), f->type)) {
-						return true;
-					}
-				}
-				bool ok = check_is_assignable_to_using_subtype(dst, f->type);
-				if (ok) {
-					return true;
-				}
-			}
-		}
-	}
-	return false;
-}
-
-
-bool check_is_assignable_to(Checker *c, Operand *operand, Type *type) {
-	if (operand->mode == Addressing_Invalid ||
-	    type == t_invalid) {
-		return true;
-	}
-
-	if (operand->mode == Addressing_Builtin) {
-		return false;
-	}
-
-	Type *s = operand->type;
-
-	if (are_types_identical(s, type)) {
-		return true;
-	}
-
-	Type *src = base_type(s);
-	Type *dst = base_type(type);
-
-	if (is_type_untyped(src)) {
-		switch (dst->kind) {
-		case Type_Basic:
-			if (operand->mode == Addressing_Constant) {
-				return check_value_is_expressible(c, operand->value, dst, NULL);
-			}
-			if (src->kind == Type_Basic && src->Basic.kind == Basic_UntypedBool) {
-				return is_type_boolean(dst);
-			}
-			break;
-		}
-		if (type_has_nil(dst)) {
-			return operand->mode == Addressing_Value && operand->type == t_untyped_nil;
-		}
-	}
-
-	if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src))) {
-		if (is_type_enum(dst) && is_type_enum(src))  {
-			return are_types_identical(s, type);
-		}
-		return true;
-	}
-
-	if (is_type_maybe(dst)) {
-		Type *elem = base_type(dst)->Maybe.elem;
-		return are_types_identical(elem, s);
-	}
-
-	if (is_type_untyped_nil(src)) {
-		return type_has_nil(dst);
-	}
-
-	// ^T <- rawptr
-	// TODO(bill): Should C-style (not C++) pointer cast be allowed?
-	// if (is_type_pointer(dst) && is_type_rawptr(src)) {
-	    // return true;
-	// }
-
-	// rawptr <- ^T
-	if (is_type_rawptr(dst) && is_type_pointer(src)) {
-	    return true;
-	}
-
-
-
-	if (dst->kind == Type_Array && src->kind == Type_Array) {
-		if (are_types_identical(dst->Array.elem, src->Array.elem)) {
-			return dst->Array.count == src->Array.count;
-		}
-	}
-
-	if (dst->kind == Type_Slice && src->kind == Type_Slice) {
-		if (are_types_identical(dst->Slice.elem, src->Slice.elem)) {
-			return true;
-		}
-	}
-
-	if (is_type_union(dst)) {
-		for (isize i = 0; i < dst->Record.field_count; i++) {
-			Entity *f = dst->Record.fields[i];
-			if (are_types_identical(f->type, s)) {
-				return true;
-			}
-		}
-	}
-
-
-	if (dst == t_any) {
-		// NOTE(bill): Anything can cast to `Any`
-		add_type_info_type(c, s);
-		return true;
-	}
-
-	return false;
-}
-
-
-// NOTE(bill): `content_name` is for debugging and error messages
-void check_assignment(Checker *c, Operand *operand, Type *type, String context_name) {
-	check_not_tuple(c, operand);
-	if (operand->mode == Addressing_Invalid) {
-		return;
-	}
-
-	if (is_type_untyped(operand->type)) {
-		Type *target_type = type;
-
-		if (type == NULL || is_type_any(type) || is_type_untyped_nil(type)) {
-			if (type == NULL && base_type(operand->type) == t_untyped_nil) {
-				error(ast_node_token(operand->expr), "Use of untyped nil in %.*s", LIT(context_name));
-				operand->mode = Addressing_Invalid;
-				return;
-			}
-
-			add_type_info_type(c, type);
-			target_type = default_type(operand->type);
-		}
-		convert_to_typed(c, operand, target_type, 0);
-		if (operand->mode == Addressing_Invalid) {
-			return;
-		}
-	}
-
-	if (type != NULL) {
-		if (!check_is_assignable_to(c, operand, type)) {
-			gbString type_str    = type_to_string(type);
-			gbString op_type_str = type_to_string(operand->type);
-			gbString expr_str    = expr_to_string(operand->expr);
-
-			if (operand->mode == Addressing_Builtin) {
-				// TODO(bill): is this a good enough error message?
-				error(ast_node_token(operand->expr),
-				      "Cannot assign builtin procedure `%s` in %.*s",
-				      expr_str,
-				      LIT(context_name));
-			} else {
-				// TODO(bill): is this a good enough error message?
-				error(ast_node_token(operand->expr),
-				      "Cannot assign value `%s` of type `%s` to `%s` in %.*s",
-				      expr_str,
-				      op_type_str,
-				      type_str,
-				      LIT(context_name));
-			}
-			operand->mode = Addressing_Invalid;
-
-			gb_string_free(expr_str);
-			gb_string_free(op_type_str);
-			gb_string_free(type_str);
-			return;
-		}
-	}
-}
-
-
-void populate_using_entity_map(Checker *c, AstNode *node, Type *t, MapEntity *entity_map) {
-	t = base_type(type_deref(t));
-	gbString str = expr_to_string(node);
-
-	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);
-			Entity **found = map_entity_get(entity_map, key);
-			if (found != NULL) {
-				Entity *e = *found;
-				// TODO(bill): Better type error
-				error(e->token, "`%.*s` is already declared in `%s`", LIT(name), str);
-			} else {
-				map_entity_set(entity_map, key, f);
-				add_entity(c, c->context.scope, NULL, f);
-				if (f->flags & EntityFlag_Anonymous) {
-					populate_using_entity_map(c, node, f->type, entity_map);
-				}
-			}
-		}
-	}
-
-	gb_string_free(str);
-}
-
-void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr);
-
-void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
-                  Entity **fields, isize field_count,
-                  Entity **other_fields, isize other_field_count,
-                  CycleChecker *cycle_checker, String context) {
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-
-	MapEntity entity_map = {0};
-	map_entity_init_with_reserve(&entity_map, c->tmp_allocator, 2*(field_count+other_field_count));
-
-	isize other_field_index = 0;
-	Entity *using_index_expr = NULL;
-
-
-	typedef struct {
-		Entity *e;
-		AstNode *t;
-	} Delay;
-	Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, other_field_count);
-	Array(Delay) delayed_type;  array_init_reserve(&delayed_type,  c->tmp_allocator, other_field_count);
-
-	for_array(decl_index, decls) {
-		AstNode *decl = decls.e[decl_index];
-		if (decl->kind == AstNode_ConstDecl) {
-			ast_node(cd, ConstDecl, decl);
-
-			isize entity_count = cd->names.count;
-			isize entity_index = 0;
-			Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
-
-			for_array(i, cd->values) {
-				AstNode *name = cd->names.e[i];
-				AstNode *value = cd->values.e[i];
-
-				GB_ASSERT(name->kind == AstNode_Ident);
-				ExactValue v = {ExactValue_Invalid};
-				Token name_token = name->Ident;
-				Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, NULL, v);
-				entities[entity_index++] = e;
-
-				Delay delay = {e, cd->type};
-				array_add(&delayed_const, delay);
-			}
-
-			isize lhs_count = cd->names.count;
-			isize rhs_count = cd->values.count;
-
-			// TODO(bill): Better error messages or is this good enough?
-			if (rhs_count == 0 && cd->type == NULL) {
-				error(ast_node_token(node), "Missing type or initial expression");
-			} else if (lhs_count < rhs_count) {
-				error(ast_node_token(node), "Extra initial expression");
-			}
-
-			for_array(i, cd->names) {
-				AstNode *name = cd->names.e[i];
-				Entity *e = entities[i];
-				Token name_token = name->Ident;
-				if (str_eq(name_token.string, str_lit("_"))) {
-					other_fields[other_field_index++] = e;
-				} else {
-					HashKey key = hash_string(name_token.string);
-					if (map_entity_get(&entity_map, key) != NULL) {
-						// TODO(bill): Scope checking already checks the declaration
-						error(name_token, "`%.*s` is already declared in this structure", LIT(name_token.string));
-					} else {
-						map_entity_set(&entity_map, key, e);
-						other_fields[other_field_index++] = e;
-					}
-					add_entity(c, c->context.scope, name, e);
-				}
-			}
-		} else if (decl->kind == AstNode_TypeDecl) {
-			ast_node(td, TypeDecl, decl);
-			Token name_token = td->name->Ident;
-
-			Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, NULL);
-			Delay delay = {e, td->type};
-			array_add(&delayed_type, delay);
-
-			if (str_eq(name_token.string, str_lit("_"))) {
-				other_fields[other_field_index++] = e;
-			} else {
-				HashKey key = hash_string(name_token.string);
-				if (map_entity_get(&entity_map, key) != NULL) {
-					// TODO(bill): Scope checking already checks the declaration
-					error(name_token, "`%.*s` is already declared in this structure", LIT(name_token.string));
-				} else {
-					map_entity_set(&entity_map, key, e);
-					other_fields[other_field_index++] = e;
-				}
-				add_entity(c, c->context.scope, td->name, e);
-				add_entity_use(c, td->name, e);
-			}
-		}
-	}
-
-	for_array(i, delayed_type) {
-		check_const_decl(c, delayed_type.e[i].e, delayed_type.e[i].t, NULL);
-	}
-	for_array(i, delayed_const) {
-		check_type_decl(c, delayed_const.e[i].e, delayed_const.e[i].t, NULL, NULL);
-	}
-
-	if (node->kind == AstNode_UnionType) {
-		isize field_index = 0;
-		fields[field_index++] = make_entity_type_name(c->allocator, c->context.scope, empty_token, NULL);
-		for_array(decl_index, decls) {
-			AstNode *decl = decls.e[decl_index];
-			if (decl->kind != AstNode_VarDecl) {
-				continue;
-			}
-
-			ast_node(vd, VarDecl, decl);
-			Type *base_type = check_type_extra(c, vd->type, NULL, cycle_checker);
-
-			for_array(name_index, vd->names) {
-				AstNode *name = vd->names.e[name_index];
-				Token name_token = name->Ident;
-
-				Type *type = make_type_named(c->allocator, name_token.string, base_type, NULL);
-				Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, type);
-				type->Named.type_name = e;
-				add_entity(c, c->context.scope, name, e);
-
-				if (str_eq(name_token.string, str_lit("_"))) {
-					error(name_token, "`_` cannot be used a union subtype");
-					continue;
-				}
-
-				HashKey key = hash_string(name_token.string);
-				if (map_entity_get(&entity_map, key) != NULL) {
-					// TODO(bill): Scope checking already checks the declaration
-					error(name_token, "`%.*s` is already declared in this union", LIT(name_token.string));
-				} else {
-					map_entity_set(&entity_map, key, e);
-					fields[field_index++] = e;
-				}
-				add_entity_use(c, name, e);
-			}
-		}
-	} else {
-		isize field_index = 0;
-		for_array(decl_index, decls) {
-			AstNode *decl = decls.e[decl_index];
-			if (decl->kind != AstNode_VarDecl) {
-				continue;
-			}
-			ast_node(vd, VarDecl, decl);
-
-			Type *type = check_type_extra(c, vd->type, NULL, cycle_checker);
-
-			if (vd->is_using) {
-				if (vd->names.count > 1) {
-					error(ast_node_token(vd->names.e[0]),
-					      "Cannot apply `using` to more than one of the same type");
-				}
-			}
-
-			for_array(name_index, vd->names) {
-				AstNode *name = vd->names.e[name_index];
-				Token name_token = name->Ident;
-
-				Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, vd->is_using, cast(i32)field_index);
-				e->identifier = name;
-				if (str_eq(name_token.string, str_lit("_"))) {
-					fields[field_index++] = e;
-				} else {
-					HashKey key = hash_string(name_token.string);
-					if (map_entity_get(&entity_map, key) != NULL) {
-						// TODO(bill): Scope checking already checks the declaration
-						error(name_token, "`%.*s` is already declared in this type", LIT(name_token.string));
-					} else {
-						map_entity_set(&entity_map, key, e);
-						fields[field_index++] = e;
-						add_entity(c, c->context.scope, name, e);
-					}
-					add_entity_use(c, name, e);
-				}
-			}
-
-
-			if (vd->is_using) {
-				Type *t = base_type(type_deref(type));
-				if (!is_type_struct(t) && !is_type_raw_union(t)) {
-					Token name_token = vd->names.e[0]->Ident;
-					if (is_type_indexable(t)) {
-						bool ok = true;
-						for_array(emi, entity_map.entries) {
-							Entity *e = entity_map.entries.e[emi].value;
-							if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) {
-								if (is_type_indexable(e->type)) {
-									if (e->identifier != vd->names.e[0]) {
-										ok = false;
-										using_index_expr = e;
-										break;
-									}
-								}
-							}
-						}
-						if (ok) {
-							using_index_expr = fields[field_index-1];
-						} else {
-							fields[field_index-1]->flags &= ~EntityFlag_Anonymous;
-							error(name_token, "Previous `using` for an index expression `%.*s`", LIT(name_token.string));
-						}
-					} else {
-						error(name_token, "`using` on a field `%.*s` must be a `struct` or `raw_union`", LIT(name_token.string));
-						continue;
-					}
-				}
-
-				populate_using_entity_map(c, node, type, &entity_map);
-			}
-		}
-	}
-
-	gb_temp_arena_memory_end(tmp);
-}
-
-
-// TODO(bill): Cleanup struct field reordering
-// TODO(bill): Inline sorting procedure?
-gb_global BaseTypeSizes __checker_sizes = {0};
-gb_global gbAllocator   __checker_allocator = {0};
-
-GB_COMPARE_PROC(cmp_struct_entity_size) {
-	// Rule:
-	// Biggest to smallest alignment
-	// if same alignment: biggest to smallest size
-	// if same size: order by source order
-	Entity *x = *(Entity **)a;
-	Entity *y = *(Entity **)b;
-	GB_ASSERT(x != NULL);
-	GB_ASSERT(y != NULL);
-	GB_ASSERT(x->kind == Entity_Variable);
-	GB_ASSERT(y->kind == Entity_Variable);
-	i64 xa = type_align_of(__checker_sizes, __checker_allocator, x->type);
-	i64 ya = type_align_of(__checker_sizes, __checker_allocator, y->type);
-	i64 xs = type_size_of(__checker_sizes, __checker_allocator, x->type);
-	i64 ys = type_size_of(__checker_sizes, __checker_allocator, y->type);
-
-	if (xa == ya) {
-		if (xs == ys) {
-			i32 diff = x->Variable.field_index - y->Variable.field_index;
-			return diff < 0 ? -1 : diff > 0;
-		}
-		return xs > ys ? -1 : xs < ys;
-	}
-	return xa > ya ? -1 : xa < ya;
-}
-
-void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecker *cycle_checker) {
-	GB_ASSERT(is_type_struct(struct_type));
-	ast_node(st, StructType, node);
-
-	isize field_count = 0;
-	isize other_field_count = 0;
-	for_array(decl_index, st->decls) {
-		AstNode *decl = st->decls.e[decl_index];
-		switch (decl->kind) {
-		case_ast_node(vd, VarDecl, decl);
-			field_count += vd->names.count;
-		case_end;
-
-		case_ast_node(cd, ConstDecl, decl);
-			other_field_count += cd->names.count;
-		case_end;
-
-		case_ast_node(td, TypeDecl, decl);
-			other_field_count += 1;
-		case_end;
-		}
-	}
-
-	Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count);
-	Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count);
-
-	check_fields(c, node, st->decls, fields, field_count, other_fields, other_field_count, cycle_checker, str_lit("struct"));
-
-
-	struct_type->Record.struct_is_packed    = st->is_packed;
-	struct_type->Record.struct_is_ordered   = st->is_ordered;
-	struct_type->Record.fields              = fields;
-	struct_type->Record.fields_in_src_order = fields;
-	struct_type->Record.field_count         = field_count;
-	struct_type->Record.other_fields        = other_fields;
-	struct_type->Record.other_field_count   = other_field_count;
-
-
-
-	if (!st->is_packed && !st->is_ordered) {
-		// NOTE(bill): Reorder fields for reduced size/performance
-
-		Entity **reordered_fields = gb_alloc_array(c->allocator, Entity *, field_count);
-		for (isize i = 0; i < field_count; i++) {
-			reordered_fields[i] = struct_type->Record.fields_in_src_order[i];
-		}
-
-		// NOTE(bill): Hacky thing
-		// TODO(bill): Probably make an inline sorting procedure rather than use global variables
-		__checker_sizes = c->sizes;
-		__checker_allocator = c->allocator;
-		// NOTE(bill): compound literal order must match source not layout
-		gb_sort_array(reordered_fields, field_count, cmp_struct_entity_size);
-
-		for (isize i = 0; i < field_count; i++) {
-			reordered_fields[i]->Variable.field_index = i;
-		}
-
-		struct_type->Record.fields = reordered_fields;
-	}
-
-	type_set_offsets(c->sizes, c->allocator, struct_type);
-}
-
-void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) {
-	GB_ASSERT(is_type_union(union_type));
-	ast_node(ut, UnionType, node);
-
-	isize field_count = 1;
-	isize other_field_count = 0;
-	for_array(decl_index, ut->decls) {
-		AstNode *decl = ut->decls.e[decl_index];
-		switch (decl->kind) {
-		case_ast_node(vd, VarDecl, decl);
-			field_count += vd->names.count;
-		case_end;
-
-		case_ast_node(cd, ConstDecl, decl);
-			other_field_count += cd->names.count;
-		case_end;
-
-		case_ast_node(td, TypeDecl, decl);
-			other_field_count += 1;
-		case_end;
-		}
-	}
-
-	Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count);
-	Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count);
-
-	check_fields(c, node, ut->decls, fields, field_count, other_fields, other_field_count, cycle_checker, str_lit("union"));
-
-	union_type->Record.fields            = fields;
-	union_type->Record.field_count       = field_count;
-	union_type->Record.other_fields      = other_fields;
-	union_type->Record.other_field_count = other_field_count;
-}
-
-void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker *cycle_checker) {
-	GB_ASSERT(node->kind == AstNode_RawUnionType);
-	GB_ASSERT(is_type_raw_union(union_type));
-	ast_node(ut, RawUnionType, node);
-
-	isize field_count = 0;
-	isize other_field_count = 0;
-	for_array(decl_index, ut->decls) {
-		AstNode *decl = ut->decls.e[decl_index];
-		switch (decl->kind) {
-		case_ast_node(vd, VarDecl, decl);
-			field_count += vd->names.count;
-		case_end;
-
-		case_ast_node(cd, ConstDecl, decl);
-			other_field_count += cd->names.count;
-		case_end;
-
-		case_ast_node(td, TypeDecl, decl);
-			other_field_count += 1;
-		case_end;
-		}
-	}
-
-	Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count);
-	Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count);
-
-	check_fields(c, node, ut->decls, fields, field_count, other_fields, other_field_count, cycle_checker, str_lit("raw union"));
-
-	union_type->Record.fields = fields;
-	union_type->Record.field_count = field_count;
-	union_type->Record.other_fields = other_fields;
-	union_type->Record.other_field_count = other_field_count;
-}
-
-GB_COMPARE_PROC(cmp_enum_order) {
-	// Rule:
-	// Biggest to smallest alignment
-	// if same alignment: biggest to smallest size
-	// if same size: order by source order
-	Entity *x = *(Entity **)a;
-	Entity *y = *(Entity **)b;
-	GB_ASSERT(x != NULL);
-	GB_ASSERT(y != NULL);
-	GB_ASSERT(x->kind == Entity_Constant);
-	GB_ASSERT(y->kind == Entity_Constant);
-	GB_ASSERT(x->Constant.value.kind == ExactValue_Integer);
-	GB_ASSERT(y->Constant.value.kind == ExactValue_Integer);
-	i64 i = x->Constant.value.value_integer;
-	i64 j = y->Constant.value.value_integer;
-
-	return i < j ? -1 : i > j;
-}
-
-
-
-void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *node) {
-	GB_ASSERT(node->kind == AstNode_EnumType);
-	GB_ASSERT(is_type_enum(enum_type));
-	ast_node(et, EnumType, node);
-
-
-
-	Type *base_type = t_int;
-	if (et->base_type != NULL) {
-		base_type = check_type(c, et->base_type);
-	}
-
-	if (base_type == NULL || !is_type_integer(base_type)) {
-		error(et->token, "Base type for enumeration must be an integer");
-		return;
-	} else
-	if (base_type == NULL) {
-		base_type = t_int;
-	}
-	enum_type->Record.enum_base = base_type;
-
-	Entity **fields = gb_alloc_array(c->allocator, Entity *, et->fields.count);
-	isize field_index = 0;
-	ExactValue iota = make_exact_value_integer(-1);
-	i64 min_value = 0;
-	i64 max_value = 0;
-
-	Type *constant_type = enum_type;
-	if (named_type != NULL) {
-		constant_type = named_type;
-	}
-
-
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-
-	MapEntity entity_map = {0};
-	map_entity_init_with_reserve(&entity_map, c->tmp_allocator, 2*(et->fields.count));
-
-	Entity *blank_entity = make_entity_constant(c->allocator, c->context.scope, blank_token, constant_type, make_exact_value_integer(0));;
-
-	for_array(i, et->fields) {
-		AstNode *field = et->fields.e[i];
-
-		ast_node(f, FieldValue, field);
-		Token name_token = f->field->Ident;
-
-		if (str_eq(name_token.string, str_lit("count"))) {
-			error(name_token, "`count` is a reserved identifier for enumerations");
-			fields[field_index++] = blank_entity;
-			continue;
-		} else if (str_eq(name_token.string, str_lit("min_value"))) {
-			error(name_token, "`min_value` is a reserved identifier for enumerations");
-			fields[field_index++] = blank_entity;
-			continue;
-		} else if (str_eq(name_token.string, str_lit("max_value"))) {
-			error(name_token, "`max_value` is a reserved identifier for enumerations");
-			fields[field_index++] = blank_entity;
-			continue;
-		}
-
-		Operand o = {0};
-		if (f->value != NULL) {
-			check_expr(c, &o, f->value);
-			if (o.mode != Addressing_Constant) {
-				error(ast_node_token(f->value), "Enumeration value must be a constant integer");
-				o.mode = Addressing_Invalid;
-			}
-			if (o.mode != Addressing_Invalid) {
-				check_assignment(c, &o, constant_type, str_lit("enumeration"));
-			}
-			if (o.mode != Addressing_Invalid) {
-				iota = o.value;
-			} else {
-				Token add_token = {Token_Add};
-				iota = exact_binary_operator_value(add_token, iota, make_exact_value_integer(1));
-			}
-		} else {
-			Token add_token = {Token_Add};
-			iota = exact_binary_operator_value(add_token, iota, make_exact_value_integer(1));
-		}
-
-
-		Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, constant_type, iota);
-		if (min_value > iota.value_integer) {
-			min_value = iota.value_integer;
-		}
-		if (max_value < iota.value_integer) {
-			max_value = iota.value_integer;
-		}
-
-		HashKey key = hash_string(name_token.string);
-		if (map_entity_get(&entity_map, key)) {
-			// TODO(bill): Scope checking already checks the declaration
-			error(name_token, "`%.*s` is already declared in this enumeration", LIT(name_token.string));
-		} else {
-			map_entity_set(&entity_map, key, e);
-			add_entity(c, c->context.scope, NULL, e);
-			fields[field_index++] = e;
-		}
-		add_entity_use(c, f->field, e);
-	}
-
-	GB_ASSERT(field_index <= et->fields.count);
-
-	gb_sort_array(fields, field_index, cmp_enum_order);
-
-	enum_type->Record.other_fields = fields;
-	enum_type->Record.other_field_count = field_index;
-
-	enum_type->Record.enum_count = make_entity_constant(c->allocator, NULL,
-		make_token_ident(str_lit("count")), t_int, make_exact_value_integer(enum_type->Record.other_field_count));
-	enum_type->Record.min_value  = make_entity_constant(c->allocator, NULL,
-		make_token_ident(str_lit("min_value")), constant_type, make_exact_value_integer(min_value));
-	enum_type->Record.max_value  = make_entity_constant(c->allocator, NULL,
-		make_token_ident(str_lit("max_value")), constant_type, make_exact_value_integer(max_value));
-
-	gb_temp_arena_memory_end(tmp);
-}
-
-Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_variadic_) {
-	if (params.count == 0) {
-		return NULL;
-	}
-
-	bool is_variadic = false;
-
-	Type *tuple = make_type_tuple(c->allocator);
-
-	isize variable_count = 0;
-	for_array(i, params) {
-		AstNode *field = params.e[i];
-		ast_node(p, Parameter, field);
-		variable_count += p->names.count;
-	}
-
-	Entity **variables = gb_alloc_array(c->allocator, Entity *, variable_count);
-	isize variable_index = 0;
-	for_array(i, params) {
-		ast_node(p, Parameter, params.e[i]);
-		AstNode *type_expr = p->type;
-		if (type_expr) {
-			if (type_expr->kind == AstNode_Ellipsis) {
-				type_expr = type_expr->Ellipsis.expr;
-				if (i+1 == params.count) {
-					is_variadic = true;
-				} else {
-					error(ast_node_token(params.e[i]), "Invalid AST: Invalid variadic parameter");
-				}
-			}
-
-			Type *type = check_type(c, type_expr);
-			for_array(j, p->names) {
-				AstNode *name = p->names.e[j];
-				if (name->kind == AstNode_Ident) {
-					Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, p->is_using);
-					add_entity(c, scope, name, param);
-					variables[variable_index++] = param;
-				} else {
-					error(ast_node_token(name), "Invalid AST: Invalid parameter");
-				}
-			}
-		}
-	}
-
-	variable_count = variable_index;
-
-	if (is_variadic) {
-		GB_ASSERT(params.count > 0);
-		// NOTE(bill): Change last variadic parameter to be a slice
-		// Custom Calling convention for variadic parameters
-		Entity *end = variables[variable_count-1];
-		end->type = make_type_slice(c->allocator, end->type);
-	}
-
-	tuple->Tuple.variables = variables;
-	tuple->Tuple.variable_count = variable_count;
-
-	if (is_variadic_) *is_variadic_ = is_variadic;
-
-	return tuple;
-}
-
-Type *check_get_results(Checker *c, Scope *scope, AstNodeArray results) {
-	if (results.count == 0) {
-		return NULL;
-	}
-	Type *tuple = make_type_tuple(c->allocator);
-
-	Entity **variables = gb_alloc_array(c->allocator, Entity *, results.count);
-	isize variable_index = 0;
-	for_array(i, results) {
-		AstNode *item = results.e[i];
-		Type *type = check_type(c, item);
-		Token token = ast_node_token(item);
-		token.string = str_lit(""); // NOTE(bill): results are not named
-		// TODO(bill): Should I have named results?
-		Entity *param = make_entity_param(c->allocator, scope, token, type, false);
-		// NOTE(bill): No need to record
-		variables[variable_index++] = param;
-	}
-	tuple->Tuple.variables = variables;
-	tuple->Tuple.variable_count = results.count;
-
-	return tuple;
-}
-
-
-void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
-	ast_node(pt, ProcType, proc_type_node);
-
-	bool variadic = false;
-	Type *params  = check_get_params(c, c->context.scope, pt->params, &variadic);
-	Type *results = check_get_results(c, c->context.scope, pt->results);
-
-	isize param_count = 0;
-	isize result_count = 0;
-	if (params)  param_count  = params ->Tuple.variable_count;
-	if (results) result_count = results->Tuple.variable_count;
-
-
-	type->Proc.scope            = c->context.scope;
-	type->Proc.params           = params;
-	type->Proc.param_count      = param_count;
-	type->Proc.results          = results;
-	type->Proc.result_count     = result_count;
-	type->Proc.variadic         = variadic;
-	// type->Proc.implicit_context = implicit_context;
-}
-
-
-void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, CycleChecker *cycle_checker) {
-	GB_ASSERT(n->kind == AstNode_Ident);
-	o->mode = Addressing_Invalid;
-	o->expr = n;
-	Entity *e = scope_lookup_entity(c->context.scope, n->Ident.string);
-	if (e == NULL) {
-		if (str_eq(n->Ident.string, str_lit("_"))) {
-			error(n->Ident, "`_` cannot be used as a value type");
-		} else {
-			error(n->Ident, "Undeclared name: %.*s", LIT(n->Ident.string));
-		}
-		o->type = t_invalid;
-		o->mode = Addressing_Invalid;
-		if (named_type != NULL) {
-			set_base_type(named_type, t_invalid);
-		}
-		return;
-	}
-	add_entity_use(c, n, e);
-
-	// CycleChecker local_cycle_checker = {0};
-	// if (cycle_checker == NULL) {
-	// 	cycle_checker = &local_cycle_checker;
-	// }
-	// defer (cycle_checker_destroy(&local_cycle_checker));
-
-	check_entity_decl(c, e, NULL, named_type, cycle_checker);
-
-	if (e->type == NULL) {
-		compiler_error("Compiler error: How did this happen? type: %s; identifier: %.*s\n", type_to_string(e->type), LIT(n->Ident.string));
-		return;
-	}
-
-	Type *type = e->type;
-
-	switch (e->kind) {
-	case Entity_Constant:
-		if (type == t_invalid) {
-			o->type = t_invalid;
-			return;
-		}
-		o->value = e->Constant.value;
-		GB_ASSERT(o->value.kind != ExactValue_Invalid);
-		o->mode = Addressing_Constant;
-		break;
-
-	case Entity_Variable:
-		e->flags |= EntityFlag_Used;
-		if (type == t_invalid) {
-			o->type = t_invalid;
-			return;
-		}
-	#if 0
-		if (e->Variable.param) {
-			o->mode = Addressing_Value;
-		} else {
-			o->mode = Addressing_Variable;
-		}
-	#else
-		o->mode = Addressing_Variable;
-	#endif
-		break;
-
-	case Entity_TypeName: {
-		o->mode = Addressing_Type;
-#if 0
-	// TODO(bill): Fix cyclical dependancy checker
-		if (cycle_checker != NULL) {
-			for_array(i, cycle_checker->path) {
-				Entity *prev = cycle_checker->path[i];
-				if (prev == e) {
-					error(e->token, "Illegal declaration cycle for %.*s", LIT(e->token.string));
-					for (isize j = i; j < gb_array_count(cycle_checker->path); j++) {
-						Entity *ref = cycle_checker->path[j];
-						error(ref->token, "\t%.*s refers to", LIT(ref->token.string));
-					}
-					error(e->token, "\t%.*s", LIT(e->token.string));
-					type = t_invalid;
-					break;
-				}
-			}
-		}
-#endif
-	} break;
-
-	case Entity_Procedure:
-		o->mode = Addressing_Value;
-		break;
-
-	case Entity_Builtin:
-		o->builtin_id = e->Builtin.id;
-		o->mode = Addressing_Builtin;
-		break;
-
-	case Entity_ImportName:
-		error(ast_node_token(n), "Use of import `%.*s` not in selector", LIT(e->ImportName.name));
-		return;
-
-	case Entity_Nil:
-		o->mode = Addressing_Value;
-		break;
-
-	case Entity_ImplicitValue:
-		o->mode = Addressing_Value;
-		break;
-
-	default:
-		compiler_error("Compiler error: Unknown EntityKind");
-		break;
-	}
-
-	o->type = type;
-}
-
-i64 check_array_count(Checker *c, AstNode *e) {
-	if (e == NULL) {
-		return 0;
-	}
-	Operand o = {0};
-	check_expr(c, &o, e);
-	if (o.mode != Addressing_Constant) {
-		if (o.mode != Addressing_Invalid) {
-			error(ast_node_token(e), "Array count must be a constant");
-		}
-		return 0;
-	}
-	if (is_type_untyped(o.type) || is_type_integer(o.type)) {
-		if (o.value.kind == ExactValue_Integer) {
-			i64 count = o.value.value_integer;
-			if (count >= 0) {
-				return count;
-			}
-			error(ast_node_token(e), "Invalid array count");
-			return 0;
-		}
-	}
-
-	error(ast_node_token(e), "Array count must be an integer");
-	return 0;
-}
-
-Type *check_type_extra(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_checker) {
-	ExactValue null_value = {ExactValue_Invalid};
-	Type *type = NULL;
-	gbString err_str = NULL;
-
-	switch (e->kind) {
-	case_ast_node(i, Ident, e);
-		Operand o = {0};
-		check_identifier(c, &o, e, named_type, cycle_checker);
-
-		switch (o.mode) {
-		case Addressing_Invalid:
-			break;
-		case Addressing_Type: {
-			type = o.type;
-			goto end;
-		} break;
-		case Addressing_NoValue:
-			err_str = expr_to_string(e);
-			error(ast_node_token(e), "`%s` used as a type", err_str);
-			break;
-		default:
-			err_str = expr_to_string(e);
-			error(ast_node_token(e), "`%s` used as a type when not a type", err_str);
-			break;
-		}
-	case_end;
-
-	case_ast_node(se, SelectorExpr, e);
-		Operand o = {0};
-		check_selector(c, &o, e);
-
-		switch (o.mode) {
-		case Addressing_Invalid:
-			break;
-		case Addressing_Type:
-			GB_ASSERT(o.type != NULL);
-			type = o.type;
-			goto end;
-		case Addressing_NoValue:
-			err_str = expr_to_string(e);
-			error(ast_node_token(e), "`%s` used as a type", err_str);
-			break;
-		default:
-			err_str = expr_to_string(e);
-			error(ast_node_token(e), "`%s` is not a type", err_str);
-			break;
-		}
-	case_end;
-
-	case_ast_node(pe, ParenExpr, e);
-		type = check_type_extra(c, pe->expr, named_type, cycle_checker);
-		goto end;
-	case_end;
-
-	case_ast_node(ue, UnaryExpr, e);
-		if (ue->op.kind == Token_Pointer) {
-			type = make_type_pointer(c->allocator, check_type(c, ue->expr));
-			goto end;
-		} else if (ue->op.kind == Token_Maybe) {
-			type = make_type_maybe(c->allocator, check_type(c, ue->expr));
-			goto end;
-		}
-	case_end;
-
-	case_ast_node(pt, PointerType, e);
-		Type *elem = check_type(c, pt->type);
-		type = make_type_pointer(c->allocator, elem);
-		goto end;
-	case_end;
-
-	case_ast_node(mt, MaybeType, e);
-		Type *elem = check_type(c, mt->type);
-		type = make_type_maybe(c->allocator, elem);
-		goto end;
-	case_end;
-
-	case_ast_node(at, ArrayType, e);
-		if (at->count != NULL) {
-			Type *elem = check_type_extra(c, at->elem, NULL, cycle_checker);
-			type = make_type_array(c->allocator, elem, check_array_count(c, at->count));
-		} else {
-			Type *elem = check_type(c, at->elem);
-			type = make_type_slice(c->allocator, elem);
-		}
-		goto end;
-	case_end;
-
-
-	case_ast_node(vt, VectorType, e);
-		Type *elem = check_type(c, vt->elem);
-		Type *be = base_type(elem);
-		i64 count = check_array_count(c, vt->count);
-		if (!is_type_boolean(be) && !is_type_numeric(be)) {
-			err_str = type_to_string(elem);
-			error(ast_node_token(vt->elem), "Vector element type must be numerical or a boolean. Got `%s`", err_str);
-		}
-		type = make_type_vector(c->allocator, elem, count);
-		goto end;
-	case_end;
-
-	case_ast_node(st, StructType, e);
-		type = make_type_struct(c->allocator);
-		set_base_type(named_type, type);
-		check_open_scope(c, e);
-		check_struct_type(c, type, e, cycle_checker);
-		check_close_scope(c);
-		type->Record.node = e;
-		goto end;
-	case_end;
-
-	case_ast_node(ut, UnionType, e);
-		type = make_type_union(c->allocator);
-		set_base_type(named_type, type);
-		check_open_scope(c, e);
-		check_union_type(c, type, e, cycle_checker);
-		check_close_scope(c);
-		type->Record.node = e;
-		goto end;
-	case_end;
-
-	case_ast_node(rut, RawUnionType, e);
-		type = make_type_raw_union(c->allocator);
-		set_base_type(named_type, type);
-		check_open_scope(c, e);
-		check_raw_union_type(c, type, e, cycle_checker);
-		check_close_scope(c);
-		type->Record.node = e;
-		goto end;
-	case_end;
-
-	case_ast_node(et, EnumType, e);
-		type = make_type_enum(c->allocator);
-		set_base_type(named_type, type);
-		check_open_scope(c, e);
-		check_enum_type(c, type, named_type, e);
-		check_close_scope(c);
-		type->Record.node = e;
-		goto end;
-	case_end;
-
-	case_ast_node(pt, ProcType, e);
-		type = alloc_type(c->allocator, Type_Proc);
-		set_base_type(named_type, type);
-		check_open_scope(c, e);
-		check_procedure_type(c, type, e);
-		check_close_scope(c);
-		goto end;
-	case_end;
-
-	case_ast_node(ce, CallExpr, e);
-		Operand o = {0};
-		check_expr_or_type(c, &o, e);
-		if (o.mode == Addressing_Type) {
-			type = o.type;
-			goto end;
-		}
-	case_end;
-	}
-	err_str = expr_to_string(e);
-	error(ast_node_token(e), "`%s` is not a type", err_str);
-
-	type = t_invalid;
-end:
-	gb_string_free(err_str);
-
-	if (type == NULL) {
-		type = t_invalid;
-	}
-
-	set_base_type(named_type, type);
-	GB_ASSERT(is_type_typed(type));
-
-	add_type_and_value(&c->info, e, Addressing_Type, type, null_value);
-
-
-	return type;
-}
-
-
-bool check_unary_op(Checker *c, Operand *o, Token op) {
-	// TODO(bill): Handle errors correctly
-	Type *type = base_type(base_vector_type(o->type));
-	gbString str = NULL;
-	switch (op.kind) {
-	case Token_Add:
-	case Token_Sub:
-		if (!is_type_numeric(type)) {
-			str = expr_to_string(o->expr);
-			error(op, "Operator `%.*s` is not allowed with `%s`", LIT(op.string), str);
-			gb_string_free(str);
-		}
-		break;
-
-	case Token_Xor:
-		if (!is_type_integer(type)) {
-			error(op, "Operator `%.*s` is only allowed with integers", LIT(op.string));
-		}
-		break;
-
-	case Token_Not:
-		if (!is_type_boolean(type)) {
-			str = expr_to_string(o->expr);
-			error(op, "Operator `%.*s` is only allowed on boolean expression", LIT(op.string));
-			gb_string_free(str);
-		}
-		break;
-
-	default:
-		error(op, "Unknown operator `%.*s`", LIT(op.string));
-		return false;
-	}
-
-	return true;
-}
-
-bool check_binary_op(Checker *c, Operand *o, Token op) {
-	// TODO(bill): Handle errors correctly
-	Type *type = base_type(base_vector_type(o->type));
-	switch (op.kind) {
-	case Token_Sub:
-	case Token_SubEq:
-		if (!is_type_numeric(type) && !is_type_pointer(type)) {
-			error(op, "Operator `%.*s` is only allowed with numeric or pointer expressions", LIT(op.string));
-			return false;
-		}
-		if (is_type_pointer(type)) {
-			o->type = t_int;
-		}
-		if (base_type(type) == t_rawptr) {
-			gbString str = type_to_string(type);
-			error(ast_node_token(o->expr), "Invalid pointer type for pointer arithmetic: `%s`", str);
-			gb_string_free(str);
-			return false;
-		}
-		break;
-
-	case Token_Add:
-	case Token_Mul:
-	case Token_Quo:
-	case Token_AddEq:
-	case Token_MulEq:
-	case Token_QuoEq:
-		if (!is_type_numeric(type)) {
-			error(op, "Operator `%.*s` is only allowed with numeric expressions", LIT(op.string));
-			return false;
-		}
-		break;
-
-	case Token_And:
-	case Token_Or:
-	case Token_AndEq:
-	case Token_OrEq:
-		if (!is_type_integer(type) && !is_type_boolean(type)) {
-			error(op, "Operator `%.*s` is only allowed with integers or booleans", LIT(op.string));
-			return false;
-		}
-		break;
-
-	case Token_Mod:
-	case Token_Xor:
-	case Token_AndNot:
-	case Token_ModEq:
-	case Token_XorEq:
-	case Token_AndNotEq:
-		if (!is_type_integer(type)) {
-			error(op, "Operator `%.*s` is only allowed with integers", LIT(op.string));
-			return false;
-		}
-		break;
-
-	case Token_CmpAnd:
-	case Token_CmpOr:
-
-	case Token_CmpAndEq:
-	case Token_CmpOrEq:
-		if (!is_type_boolean(type)) {
-			error(op, "Operator `%.*s` is only allowed with boolean expressions", LIT(op.string));
-			return false;
-		}
-		break;
-
-	default:
-		error(op, "Unknown operator `%.*s`", LIT(op.string));
-		return false;
-	}
-
-	return true;
-
-}
-bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value) {
-	if (in_value.kind == ExactValue_Invalid) {
-		// NOTE(bill): There's already been an error
-		return true;
-	}
-
-	if (is_type_boolean(type)) {
-		return in_value.kind == ExactValue_Bool;
-	} else if (is_type_string(type)) {
-		return in_value.kind == ExactValue_String;
-	} else if (is_type_integer(type)) {
-		ExactValue v = exact_value_to_integer(in_value);
-		if (v.kind != ExactValue_Integer) {
-			return false;
-		}
-		if (out_value) *out_value = v;
-		i64 i = v.value_integer;
-		u64 u = *cast(u64 *)&i;
-		i64 s = 8*type_size_of(c->sizes, c->allocator, type);
-		u64 umax = ~0ull;
-		if (s < 64) {
-			umax = (1ull << s) - 1ull;
-		} else {
-			// TODO(bill): I NEED A PROPER BIG NUMBER LIBRARY THAT CAN SUPPORT 128 bit integers and floats
-			s = 64;
-		}
-		i64 imax = (1ll << (s-1ll));
-
-
-		switch (type->Basic.kind) {
-		case Basic_i8:
-		case Basic_i16:
-		case Basic_i32:
-		case Basic_i64:
-		case Basic_i128:
-		case Basic_int:
-			return gb_is_between(i, -imax, imax-1);
-
-		case Basic_u8:
-		case Basic_u16:
-		case Basic_u32:
-		case Basic_u64:
-		case Basic_u128:
-		case Basic_uint:
-			return !(u < 0 || u > umax);
-
-		case Basic_UntypedInteger:
-			return true;
-
-		default: GB_PANIC("Compiler error: Unknown integer type!"); break;
-		}
-	} else if (is_type_float(type)) {
-		ExactValue v = exact_value_to_float(in_value);
-		if (v.kind != ExactValue_Float) {
-			return false;
-		}
-
-		switch (type->Basic.kind) {
-		// case Basic_f16:
-		case Basic_f32:
-		case Basic_f64:
-		// case Basic_f128:
-			if (out_value) *out_value = v;
-			return true;
-
-		case Basic_UntypedFloat:
-			return true;
-		}
-	} else if (is_type_pointer(type)) {
-		if (in_value.kind == ExactValue_Pointer) {
-			return true;
-		}
-		if (in_value.kind == ExactValue_Integer) {
-			return true;
-		}
-		if (out_value) *out_value = in_value;
-	}
-
-
-	return false;
-}
-
-void check_is_expressible(Checker *c, Operand *o, Type *type) {
-	GB_ASSERT(type->kind == Type_Basic);
-	GB_ASSERT(o->mode == Addressing_Constant);
-	if (!check_value_is_expressible(c, o->value, type, &o->value)) {
-		gbString a = expr_to_string(o->expr);
-		gbString b = type_to_string(type);
-		if (is_type_numeric(o->type) && is_type_numeric(type)) {
-			if (!is_type_integer(o->type) && is_type_integer(type)) {
-				error(ast_node_token(o->expr), "`%s` truncated to `%s`", a, b);
-			} else {
-				error(ast_node_token(o->expr), "`%s = %lld` overflows `%s`", a, o->value.value_integer, b);
-			}
-		} else {
-			error(ast_node_token(o->expr), "Cannot convert `%s`  to `%s`", a, b);
-		}
-
-		gb_string_free(b);
-		gb_string_free(a);
-		o->mode = Addressing_Invalid;
-	}
-}
-
-bool check_is_expr_vector_index(Checker *c, AstNode *expr) {
-	// HACK(bill): Handle this correctly. Maybe with a custom AddressingMode
-	expr = unparen_expr(expr);
-	if (expr->kind == AstNode_IndexExpr) {
-		ast_node(ie, IndexExpr, expr);
-		Type *t = type_deref(type_of_expr(&c->info, ie->expr));
-		if (t != NULL) {
-			return is_type_vector(t);
-		}
-	}
-	return false;
-}
-
-bool check_is_vector_elem(Checker *c, AstNode *expr) {
-	// HACK(bill): Handle this correctly. Maybe with a custom AddressingMode
-	expr = unparen_expr(expr);
-	if (expr->kind == AstNode_SelectorExpr) {
-		ast_node(se, SelectorExpr, expr);
-		Type *t = type_deref(type_of_expr(&c->info, se->expr));
-		if (t != NULL && is_type_vector(t)) {
-			return true;
-		}
-	}
-	return false;
-}
-
-void check_unary_expr(Checker *c, Operand *o, Token op, AstNode *node) {
-	switch (op.kind) {
-	case Token_Pointer: { // Pointer address
-		if (o->mode != Addressing_Variable ||
-		    check_is_expr_vector_index(c, o->expr) ||
-		    check_is_vector_elem(c, o->expr)) {
-			ast_node(ue, UnaryExpr, node);
-			gbString str = expr_to_string(ue->expr);
-			error(op, "Cannot take the pointer address of `%s`", str);
-			gb_string_free(str);
-			o->mode = Addressing_Invalid;
-			return;
-		}
-		o->mode = Addressing_Value;
-		o->type = make_type_pointer(c->allocator, o->type);
-		return;
-	}
-
-	case Token_Maybe: { // Make maybe
-		Type *t = default_type(o->type);
-		bool is_value =
-			o->mode == Addressing_Variable ||
-			o->mode == Addressing_Value ||
-			o->mode == Addressing_Constant;
-
-		if (!is_value || is_type_untyped(t)) {
-			ast_node(ue, UnaryExpr, node);
-			gbString str = expr_to_string(ue->expr);
-			error(op, "Cannot convert `%s` to a maybe", str);
-			gb_string_free(str);
-			o->mode = Addressing_Invalid;
-			return;
-		}
-		o->mode = Addressing_Value;
-		o->type = make_type_maybe(c->allocator, t);
-		return;
-	}
-	}
-
-	if (!check_unary_op(c, o, op)) {
-		o->mode = Addressing_Invalid;
-		return;
-	}
-
-	if (o->mode == Addressing_Constant) {
-		Type *type = base_type(o->type);
-		if (type->kind != Type_Basic) {
-			gbString xt = type_to_string(o->type);
-			gbString err_str = expr_to_string(node);
-			error(op, "Invalid type, `%s`, for constant unary expression `%s`", xt, err_str);
-			gb_string_free(err_str);
-			gb_string_free(xt);
-			o->mode = Addressing_Invalid;
-			return;
-		}
-
-
-		i32 precision = 0;
-		if (is_type_unsigned(type)) {
-			precision = cast(i32)(8 * type_size_of(c->sizes, c->allocator, type));
-		}
-		o->value = exact_unary_operator_value(op, o->value, precision);
-
-		if (is_type_typed(type)) {
-			if (node != NULL) {
-				o->expr = node;
-			}
-			check_is_expressible(c, o, type);
-		}
-		return;
-	}
-
-	o->mode = Addressing_Value;
-}
-
-void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-
-	gbString err_str = NULL;
-
-	if (check_is_assignable_to(c, x, y->type) ||
-	    check_is_assignable_to(c, y, x->type)) {
-		Type *err_type = x->type;
-		bool defined = false;
-		switch (op.kind) {
-		case Token_CmpEq:
-		case Token_NotEq:
-			defined = is_type_comparable(x->type);
-			break;
-		case Token_Lt:
-		case Token_Gt:
-		case Token_LtEq:
-		case Token_GtEq: {
-			defined = is_type_ordered(x->type);
-		} break;
-		}
-
-		// CLEANUP(bill) NOTE(bill): there is an auto assignment to `any` which needs to be checked
-		if (is_type_any(x->type) && !is_type_any(y->type)) {
-			err_type = x->type;
-			defined = false;
-		} else if (is_type_any(y->type) && !is_type_any(x->type)) {
-			err_type = y->type;
-			defined = false;
-		}
-
-		if (!defined) {
-			gbString type_string = type_to_string(err_type);
-			err_str = gb_string_make(c->tmp_allocator,
-			                         gb_bprintf("operator `%.*s` not defined for type `%s`", LIT(op.string), type_string));
-			gb_string_free(type_string);
-		}
-	} else {
-		gbString xt = type_to_string(x->type);
-		gbString yt = type_to_string(y->type);
-		err_str = gb_string_make(c->tmp_allocator,
-		                         gb_bprintf("mismatched types `%s` and `%s`", xt, yt));
-		gb_string_free(yt);
-		gb_string_free(xt);
-	}
-
-	if (err_str != NULL) {
-		error(ast_node_token(x->expr), "Cannot compare expression, %s", err_str);
-		x->type = t_untyped_bool;
-	} else {
-		if (x->mode == Addressing_Constant &&
-		    y->mode == Addressing_Constant) {
-			x->value = make_exact_value_bool(compare_exact_values(op, x->value, y->value));
-		} else {
-			x->mode = Addressing_Value;
-
-			update_expr_type(c, x->expr, default_type(x->type), true);
-			update_expr_type(c, y->expr, default_type(y->type), true);
-		}
-
-		if (is_type_vector(base_type(y->type))) {
-			x->type = make_type_vector(c->allocator, t_bool, base_type(y->type)->Vector.count);
-		} else {
-			x->type = t_untyped_bool;
-		}
-	}
-
-	if (err_str != NULL) {
-		gb_string_free(err_str);
-	};
-
-	gb_temp_arena_memory_end(tmp);
-}
-
-void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
-	GB_ASSERT(node->kind == AstNode_BinaryExpr);
-	ast_node(be, BinaryExpr, node);
-
-	ExactValue x_val = {0};
-	if (x->mode == Addressing_Constant) {
-		x_val = exact_value_to_integer(x->value);
-	}
-
-	bool x_is_untyped = is_type_untyped(x->type);
-	if (!(is_type_integer(x->type) || (x_is_untyped && x_val.kind == ExactValue_Integer))) {
-		gbString err_str = expr_to_string(x->expr);
-		error(ast_node_token(node),
-		      "Shifted operand `%s` must be an integer", err_str);
-		gb_string_free(err_str);
-		x->mode = Addressing_Invalid;
-		return;
-	}
-
-	if (is_type_unsigned(y->type)) {
-
-	} else if (is_type_untyped(y->type)) {
-		convert_to_typed(c, y, t_untyped_integer, 0);
-		if (y->mode == Addressing_Invalid) {
-			x->mode = Addressing_Invalid;
-			return;
-		}
-	} else {
-		gbString err_str = expr_to_string(y->expr);
-		error(ast_node_token(node),
-		      "Shift amount `%s` must be an unsigned integer", err_str);
-		gb_string_free(err_str);
-		x->mode = Addressing_Invalid;
-		return;
-	}
-
-
-	if (x->mode == Addressing_Constant) {
-		if (y->mode == Addressing_Constant) {
-			ExactValue y_val = exact_value_to_integer(y->value);
-			if (y_val.kind != ExactValue_Integer) {
-				gbString err_str = expr_to_string(y->expr);
-				error(ast_node_token(node),
-				      "Shift amount `%s` must be an unsigned integer", err_str);
-				gb_string_free(err_str);
-				x->mode = Addressing_Invalid;
-				return;
-			}
-
-			u64 amount = cast(u64)y_val.value_integer;
-			if (amount > 1074) {
-				gbString err_str = expr_to_string(y->expr);
-				error(ast_node_token(node),
-				      "Shift amount too large: `%s`", err_str);
-				gb_string_free(err_str);
-				x->mode = Addressing_Invalid;
-				return;
-			}
-
-			if (!is_type_integer(x->type)) {
-				// NOTE(bill): It could be an untyped float but still representable
-				// as an integer
-				x->type = t_untyped_integer;
-			}
-
-			x->value = exact_value_shift(be->op, x_val, make_exact_value_integer(amount));
-
-			if (is_type_typed(x->type)) {
-				check_is_expressible(c, x, base_type(x->type));
-			}
-			return;
-		}
-
-		if (x_is_untyped) {
-			ExprInfo *info = map_expr_info_get(&c->info.untyped, hash_pointer(x->expr));
-			if (info != NULL) {
-				info->is_lhs = true;
-			}
-			x->mode = Addressing_Value;
-			return;
-		}
-	}
-
-	if (y->mode == Addressing_Constant && y->value.value_integer < 0) {
-		gbString err_str = expr_to_string(y->expr);
-		error(ast_node_token(node),
-		      "Shift amount cannot be negative: `%s`", err_str);
-		gb_string_free(err_str);
-	}
-
-	x->mode = Addressing_Value;
-}
-
-bool check_is_castable_to(Checker *c, Operand *operand, Type *y) {
-	if (check_is_assignable_to(c, operand, y)) {
-		return true;
-	}
-
-	Type *x = operand->type;
-	Type *xb = base_type(x);
-	Type *yb = base_type(y);
-	if (are_types_identical(xb, yb)) {
-		return true;
-	}
-	xb = get_enum_base_type(x);
-	yb = get_enum_base_type(y);
-
-
-	// Cast between booleans and integers
-	if (is_type_boolean(xb) || is_type_integer(xb)) {
-		if (is_type_boolean(yb) || is_type_integer(yb)) {
-			return true;
-		}
-	}
-
-	// Cast between numbers
-	if (is_type_integer(xb) || is_type_float(xb)) {
-		if (is_type_integer(yb) || is_type_float(yb)) {
-			return true;
-		}
-	}
-
-	// Cast between pointers
-	if (is_type_pointer(xb) && is_type_pointer(yb)) {
-		return true;
-	}
-
-	// (u)int <-> pointer
-	if (is_type_int_or_uint(xb) && is_type_rawptr(yb)) {
-		return true;
-	}
-	if (is_type_rawptr(xb) && is_type_int_or_uint(yb)) {
-		return true;
-	}
-
-	// []byte/[]u8 <-> string
-	if (is_type_u8_slice(xb) && is_type_string(yb)) {
-		return true;
-	}
-	if (is_type_string(xb) && is_type_u8_slice(yb)) {
-		if (is_type_typed(xb)) {
-			return true;
-		}
-	}
-
-	// proc <-> proc
-	if (is_type_proc(xb) && is_type_proc(yb)) {
-		return true;
-	}
-
-	// proc -> rawptr
-	if (is_type_proc(xb) && is_type_rawptr(yb)) {
-		return true;
-	}
-
-	return false;
-}
-
-String check_down_cast_name(Type *dst_, Type *src_) {
-	String result = {0};
-	Type *dst = type_deref(dst_);
-	Type *src = type_deref(src_);
-	Type *dst_s = base_type(dst);
-	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->flags & EntityFlag_Field);
-		if (f->flags & EntityFlag_Anonymous) {
-			if (are_types_identical(f->type, src_)) {
-				return f->token.string;
-			}
-			if (are_types_identical(type_deref(f->type), src_)) {
-				return f->token.string;
-			}
-
-			if (!is_type_pointer(f->type)) {
-				result = check_down_cast_name(f->type, src_);
-				if (result.len > 0) {
-					return result;
-				}
-			}
-		}
-	}
-
-	return result;
-}
-
-Operand check_ptr_addition(Checker *c, TokenKind op, Operand *ptr, Operand *offset, AstNode *node) {
-	GB_ASSERT(node->kind == AstNode_BinaryExpr);
-	ast_node(be, BinaryExpr, node);
-	GB_ASSERT(is_type_pointer(ptr->type));
-	GB_ASSERT(is_type_integer(offset->type));
-	GB_ASSERT(op == Token_Add || op == Token_Sub);
-
-	Operand operand = {0};
-	operand.mode = Addressing_Value;
-	operand.type = ptr->type;
-	operand.expr = node;
-
-	if (base_type(ptr->type) == t_rawptr) {
-		gbString str = type_to_string(ptr->type);
-		error(ast_node_token(node), "Invalid pointer type for pointer arithmetic: `%s`", str);
-		gb_string_free(str);
-		operand.mode = Addressing_Invalid;
-		return operand;
-	}
-
-
-	if (ptr->mode == Addressing_Constant && offset->mode == Addressing_Constant) {
-		i64 elem_size = type_size_of(c->sizes, c->allocator, ptr->type);
-		i64 ptr_val = ptr->value.value_pointer;
-		i64 offset_val = exact_value_to_integer(offset->value).value_integer;
-		i64 new_ptr_val = ptr_val;
-		if (op == Token_Add) {
-			new_ptr_val += elem_size*offset_val;
-		} else {
-			new_ptr_val -= elem_size*offset_val;
-		}
-		operand.mode = Addressing_Constant;
-		operand.value = make_exact_value_pointer(new_ptr_val);
-	}
-
-	return operand;
-}
-
-void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
-	GB_ASSERT(node->kind == AstNode_BinaryExpr);
-	Operand y_ = {0}, *y = &y_;
-
-	ast_node(be, BinaryExpr, node);
-
-	if (be->op.kind == Token_as) {
-		check_expr(c, x, be->left);
-		Type *type = check_type(c, be->right);
-		if (x->mode == Addressing_Invalid) {
-			return;
-		}
-
-		bool is_const_expr = x->mode == Addressing_Constant;
-		bool can_convert = false;
-
-		Type *bt = base_type(type);
-		if (is_const_expr && is_type_constant_type(bt)) {
-			if (bt->kind == Type_Basic) {
-				if (check_value_is_expressible(c, x->value, bt, &x->value)) {
-					can_convert = true;
-				}
-			}
-		} else if (check_is_castable_to(c, x, type)) {
-			if (x->mode != Addressing_Constant) {
-				x->mode = Addressing_Value;
-			}
-			can_convert = true;
-		}
-
-		if (!can_convert) {
-			gbString expr_str = expr_to_string(x->expr);
-			gbString to_type  = type_to_string(type);
-			gbString from_type = type_to_string(x->type);
-			error(ast_node_token(x->expr), "Cannot cast `%s` as `%s` from `%s`", expr_str, to_type, from_type);
-			gb_string_free(from_type);
-			gb_string_free(to_type);
-			gb_string_free(expr_str);
-
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		if (is_type_untyped(x->type)) {
-			Type *final_type = type;
-			if (is_const_expr && !is_type_constant_type(type)) {
-				final_type = default_type(x->type);
-			}
-			update_expr_type(c, x->expr, final_type, true);
-		}
-
-		x->type = type;
-		return;
-	} else if (be->op.kind == Token_transmute) {
-		check_expr(c, x, be->left);
-		Type *type = check_type(c, be->right);
-		if (x->mode == Addressing_Invalid) {
-			return;
-		}
-
-		if (x->mode == Addressing_Constant) {
-			gbString expr_str = expr_to_string(x->expr);
-			error(ast_node_token(x->expr), "Cannot transmute constant expression: `%s`", expr_str);
-			gb_string_free(expr_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		if (is_type_untyped(x->type)) {
-			gbString expr_str = expr_to_string(x->expr);
-			error(ast_node_token(x->expr), "Cannot transmute untyped expression: `%s`", expr_str);
-			gb_string_free(expr_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		i64 srcz = type_size_of(c->sizes, c->allocator, x->type);
-		i64 dstz = type_size_of(c->sizes, c->allocator, type);
-		if (srcz != dstz) {
-			gbString expr_str = expr_to_string(x->expr);
-			gbString type_str = type_to_string(type);
-			error(ast_node_token(x->expr), "Cannot transmute `%s` to `%s`, %lld vs %lld bytes", expr_str, type_str, srcz, dstz);
-			gb_string_free(type_str);
-			gb_string_free(expr_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		x->type = type;
-
-		return;
-	} else if (be->op.kind == Token_down_cast) {
-		check_expr(c, x, be->left);
-		Type *type = check_type(c, be->right);
-		if (x->mode == Addressing_Invalid) {
-			return;
-		}
-
-		if (x->mode == Addressing_Constant) {
-			gbString expr_str = expr_to_string(node);
-			error(ast_node_token(node), "Cannot `down_cast` a constant expression: `%s`", expr_str);
-			gb_string_free(expr_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		if (is_type_untyped(x->type)) {
-			gbString expr_str = expr_to_string(node);
-			error(ast_node_token(node), "Cannot `down_cast` an untyped expression: `%s`", expr_str);
-			gb_string_free(expr_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		if (!(is_type_pointer(x->type) && is_type_pointer(type))) {
-			gbString expr_str = expr_to_string(node);
-			error(ast_node_token(node), "Can only `down_cast` pointers: `%s`", expr_str);
-			gb_string_free(expr_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		Type *src = type_deref(x->type);
-		Type *dst = type_deref(type);
-		Type *bsrc = base_type(src);
-		Type *bdst = base_type(dst);
-
-		if (!(is_type_struct(bsrc) || is_type_raw_union(bsrc))) {
-			gbString expr_str = expr_to_string(node);
-			error(ast_node_token(node), "Can only `down_cast` pointer from structs or unions: `%s`", expr_str);
-			gb_string_free(expr_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		if (!(is_type_struct(bdst) || is_type_raw_union(bdst))) {
-			gbString expr_str = expr_to_string(node);
-			error(ast_node_token(node), "Can only `down_cast` pointer to structs or unions: `%s`", expr_str);
-			gb_string_free(expr_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		String param_name = check_down_cast_name(dst, src);
-		if (param_name.len == 0) {
-			gbString expr_str = expr_to_string(node);
-			error(ast_node_token(node), "Illegal `down_cast`: `%s`", expr_str);
-			gb_string_free(expr_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		x->mode = Addressing_Value;
-		x->type = type;
-		return;
-	} else if (be->op.kind == Token_union_cast) {
-		check_expr(c, x, be->left);
-		Type *type = check_type(c, be->right);
-		if (x->mode == Addressing_Invalid) {
-			return;
-		}
-
-		if (x->mode == Addressing_Constant) {
-			gbString expr_str = expr_to_string(node);
-			error(ast_node_token(node), "Cannot `union_cast` a constant expression: `%s`", expr_str);
-			gb_string_free(expr_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		if (is_type_untyped(x->type)) {
-			gbString expr_str = expr_to_string(node);
-			error(ast_node_token(node), "Cannot `union_cast` an untyped expression: `%s`", expr_str);
-			gb_string_free(expr_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		bool src_is_ptr = is_type_pointer(x->type);
-		bool dst_is_ptr = is_type_pointer(type);
-		Type *src = type_deref(x->type);
-		Type *dst = type_deref(type);
-		Type *bsrc = base_type(src);
-		Type *bdst = base_type(dst);
-
-		if (src_is_ptr != dst_is_ptr) {
-			gbString src_type_str = type_to_string(x->type);
-			gbString dst_type_str = type_to_string(type);
-			error(ast_node_token(node), "Invalid `union_cast` types: `%s` and `%s`", src_type_str, dst_type_str);
-			gb_string_free(dst_type_str);
-			gb_string_free(src_type_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		if (!is_type_union(src)) {
-			error(ast_node_token(node), "`union_cast` can only operate on unions");
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		bool ok = false;
-		for (isize i = 1; i < bsrc->Record.field_count; i++) {
-			Entity *f = bsrc->Record.fields[i];
-			if (are_types_identical(f->type, dst)) {
-				ok = true;
-				break;
-			}
-		}
-
-		if (!ok) {
-			gbString expr_str = expr_to_string(node);
-			gbString dst_type_str = type_to_string(type);
-			error(ast_node_token(node), "Cannot `union_cast` `%s` to `%s`", expr_str, dst_type_str);
-			gb_string_free(dst_type_str);
-			gb_string_free(expr_str);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		Entity **variables = gb_alloc_array(c->allocator, Entity *, 2);
-		Token tok = make_token_ident(str_lit(""));
-		variables[0] = make_entity_param(c->allocator, NULL, tok, type, false);
-		variables[1] = make_entity_param(c->allocator, NULL, tok, t_bool, false);
-
-		Type *tuple = make_type_tuple(c->allocator);
-		tuple->Tuple.variables = variables;
-		tuple->Tuple.variable_count = 2;
-
-		x->type = tuple;
-		x->mode = Addressing_Value;
-		return;
-	}
-
-	check_expr(c, x, be->left);
-	check_expr(c, y, be->right);
-	if (x->mode == Addressing_Invalid) {
-		return;
-	}
-	if (y->mode == Addressing_Invalid) {
-		x->mode = Addressing_Invalid;
-		x->expr = y->expr;
-		return;
-	}
-
-	Token op = be->op;
-
-	if (token_is_shift(op)) {
-		check_shift(c, x, y, node);
-		return;
-	}
-
-	if (op.kind == Token_Add || op.kind == Token_Sub) {
-		if (is_type_pointer(x->type) && is_type_integer(y->type)) {
-			*x = check_ptr_addition(c, op.kind, x, y, node);
-			return;
-		} else if (is_type_integer(x->type) && is_type_pointer(y->type)) {
-			if (op.kind == Token_Sub) {
-				gbString lhs = expr_to_string(x->expr);
-				gbString rhs = expr_to_string(y->expr);
-				error(ast_node_token(node), "Invalid pointer arithmetic, did you mean `%s %.*s %s`?", rhs, LIT(op.string), lhs);
-				gb_string_free(rhs);
-				gb_string_free(lhs);
-				x->mode = Addressing_Invalid;
-				return;
-			}
-			*x = check_ptr_addition(c, op.kind, y, x, node);
-			return;
-		}
-	}
-
-
-	convert_to_typed(c, x, y->type, 0);
-	if (x->mode == Addressing_Invalid) {
-		return;
-	}
-	convert_to_typed(c, y, x->type, 0);
-	if (y->mode == Addressing_Invalid) {
-		x->mode = Addressing_Invalid;
-		return;
-	}
-
-	if (token_is_comparison(op)) {
-		check_comparison(c, x, y, op);
-		return;
-	}
-
-	if (!are_types_identical(x->type, y->type)) {
-		if (x->type != t_invalid &&
-		    y->type != t_invalid) {
-			gbString xt = type_to_string(x->type);
-			gbString yt = type_to_string(y->type);
-			gbString expr_str = expr_to_string(x->expr);
-			error(op, "Mismatched types in binary expression `%s` : `%s` vs `%s`", expr_str, xt, yt);
-			gb_string_free(expr_str);
-			gb_string_free(yt);
-			gb_string_free(xt);
-		}
-		x->mode = Addressing_Invalid;
-		return;
-	}
-
-	if (!check_binary_op(c, x, op)) {
-		x->mode = Addressing_Invalid;
-		return;
-	}
-
-	switch (op.kind) {
-	case Token_Quo:
-	case Token_Mod:
-	case Token_QuoEq:
-	case Token_ModEq:
-		if ((x->mode == Addressing_Constant || is_type_integer(x->type)) &&
-		    y->mode == Addressing_Constant) {
-			bool fail = false;
-			switch (y->value.kind) {
-			case ExactValue_Integer:
-				if (y->value.value_integer == 0) {
-					fail = true;
-				}
-				break;
-			case ExactValue_Float:
-				if (y->value.value_float == 0.0) {
-					fail = true;
-				}
-				break;
-			}
-
-			if (fail) {
-				error(ast_node_token(y->expr), "Division by zero not allowed");
-				x->mode = Addressing_Invalid;
-				return;
-			}
-		}
-	}
-
-	if (x->mode == Addressing_Constant &&
-	    y->mode == Addressing_Constant) {
-		ExactValue a = x->value;
-		ExactValue b = y->value;
-
-		Type *type = base_type(x->type);
-		if (is_type_pointer(type)) {
-			GB_ASSERT(op.kind == Token_Sub);
-			i64 bytes = a.value_pointer - b.value_pointer;
-			i64 diff = bytes/type_size_of(c->sizes, c->allocator, type);
-			x->value = make_exact_value_pointer(diff);
-			return;
-		}
-
-		if (type->kind != Type_Basic) {
-			gbString xt = type_to_string(x->type);
-			gbString err_str = expr_to_string(node);
-			error(op, "Invalid type, `%s`, for constant binary expression `%s`", xt, err_str);
-			gb_string_free(err_str);
-			gb_string_free(xt);
-			x->mode = Addressing_Invalid;
-			return;
-		}
-
-		if (op.kind == Token_Quo && is_type_integer(type)) {
-			op.kind = Token_QuoEq; // NOTE(bill): Hack to get division of integers
-		}
-		x->value = exact_binary_operator_value(op, a, b);
-		if (is_type_typed(type)) {
-			if (node != NULL) {
-				x->expr = node;
-			}
-			check_is_expressible(c, x, type);
-		}
-		return;
-	}
-
-	x->mode = Addressing_Value;
-}
-
-
-void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) {
-	HashKey key = hash_pointer(e);
-	ExprInfo *found = map_expr_info_get(&c->info.untyped, key);
-	if (found == NULL) {
-		return;
-	}
-
-	switch (e->kind) {
-	case_ast_node(ue, UnaryExpr, e);
-		if (found->value.kind != ExactValue_Invalid) {
-			break;
-		}
-		update_expr_type(c, ue->expr, type, final);
-	case_end;
-
-	case_ast_node(be, BinaryExpr, e);
-		if (found->value.kind != ExactValue_Invalid) {
-			break;
-		}
-		if (!token_is_comparison(be->op)) {
-			if (token_is_shift(be->op)) {
-				update_expr_type(c, be->left,  type, final);
-			} else {
-				update_expr_type(c, be->left,  type, final);
-				update_expr_type(c, be->right, type, final);
-			}
-		}
-	case_end;
-	}
-
-	if (!final && is_type_untyped(type)) {
-		found->type = base_type(type);
-		map_expr_info_set(&c->info.untyped, key, *found);
-	} else {
-		ExprInfo old = *found;
-		map_expr_info_remove(&c->info.untyped, key);
-
-		if (old.is_lhs && !is_type_integer(type)) {
-			gbString expr_str = expr_to_string(e);
-			gbString type_str = type_to_string(type);
-			error(ast_node_token(e), "Shifted operand %s must be an integer, got %s", expr_str, type_str);
-			gb_string_free(type_str);
-			gb_string_free(expr_str);
-			return;
-		}
-
-		add_type_and_value(&c->info, e, found->mode, type, found->value);
-	}
-}
-
-void update_expr_value(Checker *c, AstNode *e, ExactValue value) {
-	ExprInfo *found = map_expr_info_get(&c->info.untyped, hash_pointer(e));
-	if (found) {
-		found->value = value;
-	}
-}
-
-void convert_untyped_error(Checker *c, Operand *operand, Type *target_type) {
-	gbString expr_str = expr_to_string(operand->expr);
-	gbString type_str = type_to_string(target_type);
-	char *extra_text = "";
-
-	if (operand->mode == Addressing_Constant) {
-		if (operand->value.value_integer == 0) {
-			if (str_ne(make_string_c(expr_str), str_lit("nil"))) { // HACK NOTE(bill): Just in case
-				// NOTE(bill): Doesn't matter what the type is as it's still zero in the union
-				extra_text = " - Did you want `nil`?";
-			}
-		}
-	}
-	error(ast_node_token(operand->expr), "Cannot convert `%s` to `%s`%s", expr_str, type_str, extra_text);
-
-	gb_string_free(type_str);
-	gb_string_free(expr_str);
-	operand->mode = Addressing_Invalid;
-}
-
-// NOTE(bill): Set initial level to 0
-void convert_to_typed(Checker *c, Operand *operand, Type *target_type, i32 level) {
-	GB_ASSERT_NOT_NULL(target_type);
-	if (operand->mode == Addressing_Invalid ||
-	    is_type_typed(operand->type) ||
-	    target_type == t_invalid) {
-		return;
-	}
-
-	if (is_type_untyped(target_type)) {
-		Type *x = operand->type;
-		Type *y = target_type;
-		if (is_type_numeric(x) && is_type_numeric(y)) {
-			if (x < y) {
-				operand->type = target_type;
-				update_expr_type(c, operand->expr, target_type, false);
-			}
-		} else if (x != y) {
-			convert_untyped_error(c, operand, target_type);
-		}
-		return;
-	}
-
-	Type *t = get_enum_base_type(base_type(target_type));
-	switch (t->kind) {
-	case Type_Basic:
-		if (operand->mode == Addressing_Constant) {
-			check_is_expressible(c, operand, t);
-			if (operand->mode == Addressing_Invalid) {
-				return;
-			}
-			update_expr_value(c, operand->expr, operand->value);
-		} else {
-			switch (operand->type->Basic.kind) {
-			case Basic_UntypedBool:
-				if (!is_type_boolean(target_type)) {
-					convert_untyped_error(c, operand, target_type);
-					return;
-				}
-				break;
-			case Basic_UntypedInteger:
-			case Basic_UntypedFloat:
-			case Basic_UntypedRune:
-				if (!is_type_numeric(target_type)) {
-					convert_untyped_error(c, operand, target_type);
-					return;
-				}
-				break;
-
-			case Basic_UntypedNil:
-				if (!type_has_nil(target_type)) {
-					convert_untyped_error(c, operand, target_type);
-					return;
-				}
-				break;
-			}
-		}
-		break;
-
-	case Type_Maybe:
-		if (is_type_untyped_nil(operand->type)) {
-			// Okay
-		} else if (level == 0) {
-			convert_to_typed(c, operand, t->Maybe.elem, level+1);
-			return;
-		}
-
-	default:
-		if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) {
-			convert_untyped_error(c, operand, target_type);
-			return;
-		}
-		break;
-	}
-
-
-
-	operand->type = target_type;
-}
-
-bool check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *value) {
-	Operand operand = {Addressing_Invalid};
-	check_expr(c, &operand, index_value);
-	if (operand.mode == Addressing_Invalid) {
-		if (value) *value = 0;
-		return false;
-	}
-
-	convert_to_typed(c, &operand, t_int, 0);
-	if (operand.mode == Addressing_Invalid) {
-		if (value) *value = 0;
-		return false;
-	}
-
-	if (!is_type_integer(get_enum_base_type(operand.type))) {
-		gbString expr_str = expr_to_string(operand.expr);
-		error(ast_node_token(operand.expr),
-		            "Index `%s` must be an integer", expr_str);
-		gb_string_free(expr_str);
-		if (value) *value = 0;
-		return false;
-	}
-
-	if (operand.mode == Addressing_Constant &&
-	    (c->context.stmt_state_flags & StmtStateFlag_bounds_check) != 0) {
-		i64 i = exact_value_to_integer(operand.value).value_integer;
-		if (i < 0) {
-			gbString expr_str = expr_to_string(operand.expr);
-			error(ast_node_token(operand.expr),
-			            "Index `%s` cannot be a negative value", expr_str);
-			gb_string_free(expr_str);
-			if (value) *value = 0;
-			return false;
-		}
-
-		if (max_count >= 0) { // NOTE(bill): Do array bound checking
-			if (value) *value = i;
-			if (i >= max_count) {
-				gbString expr_str = expr_to_string(operand.expr);
-				error(ast_node_token(operand.expr),
-				            "Index `%s` is out of bounds range 0..<%lld", expr_str, max_count);
-				gb_string_free(expr_str);
-				return false;
-			}
-
-			return true;
-		}
-	}
-
-	// NOTE(bill): It's alright :D
-	if (value) *value = -1;
-	return true;
-}
-
-Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
-	ast_node(se, SelectorExpr, node);
-
-	bool check_op_expr = true;
-	Entity *expr_entity = NULL;
-	Entity *entity = NULL;
-	Selection sel = {0}; // NOTE(bill): Not used if it's an import name
-
-	AstNode *op_expr  = se->expr;
-	AstNode *selector = unparen_expr(se->selector);
-	if (selector == NULL) {
-		goto error;
-	}
-
-	GB_ASSERT(selector->kind == AstNode_Ident);
-
-
-	if (op_expr->kind == AstNode_Ident) {
-		String name = op_expr->Ident.string;
-		Entity *e = scope_lookup_entity(c->context.scope, name);
-		add_entity_use(c, op_expr, e);
-		expr_entity = e;
-		if (e != NULL && e->kind == Entity_ImportName) {
-			String sel_name = selector->Ident.string;
-			check_op_expr = false;
-			entity = scope_lookup_entity(e->ImportName.scope, sel_name);
-			if (entity == NULL) {
-				error(ast_node_token(op_expr), "`%.*s` is not declared by `%.*s`", LIT(sel_name), LIT(name));
-				goto error;
-			}
-			if (entity->type == NULL) { // Not setup yet
-				check_entity_decl(c, entity, NULL, NULL, NULL);
-			}
-			GB_ASSERT(entity->type != NULL);
-			bool is_not_exported = !is_entity_exported(entity);
-
-			// TODO(bill): Fix this for `#import "file.odin" as .`
-			if (is_not_exported) {
-				Entity **found = map_entity_get(&e->ImportName.scope->implicit, hash_string(sel_name));
-				if (!found && e->ImportName.scope != entity->scope) {
-					is_not_exported = false;
-				}
-			}
-
-			if (is_not_exported) {
-				gbString sel_str = expr_to_string(selector);
-				error(ast_node_token(op_expr), "`%s` is not exported by `%.*s`", sel_str, LIT(name));
-				gb_string_free(sel_str);
-				// NOTE(bill): Not really an error so don't goto error
-			}
-
-			add_entity_use(c, selector, entity);
-		}
-	}
-	if (check_op_expr) {
-		check_expr_base(c, operand, op_expr, NULL);
-		if (operand->mode == Addressing_Invalid) {
-			goto error;
-		}
-	}
-
-
-	if (entity == NULL) {
-		sel = lookup_field(c->allocator, operand->type, selector->Ident.string, operand->mode == Addressing_Type);
-		entity = sel.entity;
-	}
-	if (entity == NULL) {
-		gbString op_str   = expr_to_string(op_expr);
-		gbString type_str = type_to_string(operand->type);
-		gbString sel_str  = expr_to_string(selector);
-		error(ast_node_token(op_expr), "`%s` (`%s`) has no field `%s`", op_str, type_str, sel_str);
-		gb_string_free(sel_str);
-		gb_string_free(type_str);
-		gb_string_free(op_str);
-		goto error;
-	}
-
-	if (expr_entity != NULL && expr_entity->kind == Entity_Constant && entity->kind != Entity_Constant) {
-		gbString op_str   = expr_to_string(op_expr);
-		gbString type_str = type_to_string(operand->type);
-		gbString sel_str  = expr_to_string(selector);
-		error(ast_node_token(op_expr), "Cannot access non-constant field `%s` from `%s`", sel_str, op_str);
-		gb_string_free(sel_str);
-		gb_string_free(type_str);
-		gb_string_free(op_str);
-		goto error;
-	}
-
-
-	add_entity_use(c, selector, entity);
-
-	switch (entity->kind) {
-	case Entity_Constant:
-		operand->mode = Addressing_Constant;
-		operand->value = entity->Constant.value;
-		break;
-	case Entity_Variable:
-		// TODO(bill): This is the rule I need?
-		if (sel.indirect || operand->mode != Addressing_Value) {
-			operand->mode = Addressing_Variable;
-		}
-		break;
-	case Entity_TypeName:
-		operand->mode = Addressing_Type;
-		break;
-	case Entity_Procedure:
-		operand->mode = Addressing_Value;
-		break;
-	case Entity_Builtin:
-		operand->mode = Addressing_Builtin;
-		operand->builtin_id = entity->Builtin.id;
-		break;
-
-	// NOTE(bill): These cases should never be hit but are here for sanity reasons
-	case Entity_Nil:
-		operand->mode = Addressing_Value;
-		break;
-	case Entity_ImplicitValue:
-		operand->mode = Addressing_Value;
-		break;
-	}
-
-	operand->type = entity->type;
-	operand->expr = node;
-
-	return entity;
-
-error:
-	operand->mode = Addressing_Invalid;
-	operand->expr = node;
-	return NULL;
-}
-
-bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) {
-	GB_ASSERT(call->kind == AstNode_CallExpr);
-	ast_node(ce, CallExpr, call);
-	BuiltinProc *bp = &builtin_procs[id];
-	{
-		char *err = NULL;
-		if (ce->args.count < bp->arg_count) {
-			err = "Too few";
-		} else if (ce->args.count > bp->arg_count && !bp->variadic) {
-			err = "Too many";
-		}
-
-		if (err) {
-			ast_node(proc, Ident, ce->proc);
-			error(ce->close, "`%s` arguments for `%.*s`, expected %td, got %td",
-			      err, LIT(proc->string),
-			      bp->arg_count, ce->args.count);
-			return false;
-		}
-	}
-
-	switch (id) {
-	case BuiltinProc_new:
-	case BuiltinProc_new_slice:
-	case BuiltinProc_size_of:
-	case BuiltinProc_align_of:
-	case BuiltinProc_offset_of:
-	case BuiltinProc_type_info:
-		// NOTE(bill): The first arg may be a Type, this will be checked case by case
-		break;
-	default:
-		check_multi_expr(c, operand, ce->args.e[0]);
-	}
-
-	switch (id) {
-	case BuiltinProc_new: {
-		// new :: proc(Type) -> ^Type
-		Operand op = {0};
-		check_expr_or_type(c, &op, ce->args.e[0]);
-		Type *type = op.type;
-		if ((op.mode != Addressing_Type && type == NULL) || type == t_invalid) {
-			error(ast_node_token(ce->args.e[0]), "Expected a type for `new`");
-			return false;
-		}
-		operand->mode = Addressing_Value;
-		operand->type = make_type_pointer(c->allocator, type);
-	} break;
-	case BuiltinProc_new_slice: {
-		// new_slice :: proc(Type, len: int[, cap: int]) -> []Type
-		Operand op = {0};
-		check_expr_or_type(c, &op, ce->args.e[0]);
-		Type *type = op.type;
-		if ((op.mode != Addressing_Type && type == NULL) || type == t_invalid) {
-			error(ast_node_token(ce->args.e[0]), "Expected a type for `new_slice`");
-			return false;
-		}
-
-		AstNode *len = ce->args.e[1];
-		AstNode *cap = NULL;
-		if (ce->args.count > 2) {
-			cap = ce->args.e[2];
-		}
-
-		check_expr(c, &op, len);
-		if (op.mode == Addressing_Invalid) {
-			return false;
-		}
-		if (!is_type_integer(op.type)) {
-			gbString type_str = type_to_string(operand->type);
-			error(ast_node_token(call),
-			      "Length for `new_slice` must be an integer, got `%s`",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
-		}
-
-		if (cap != NULL) {
-			check_expr(c, &op, cap);
-			if (op.mode == Addressing_Invalid) {
-				return false;
-			}
-			if (!is_type_integer(op.type)) {
-				gbString type_str = type_to_string(operand->type);
-				error(ast_node_token(call),
-				      "Capacity for `new_slice` must be an integer, got `%s`",
-				      type_str);
-				gb_string_free(type_str);
-				return false;
-			}
-			if (ce->args.count > 3) {
-				error(ast_node_token(call),
-				      "Too many arguments to `new_slice`, expected either 2 or 3");
-				return false;
-			}
-		}
-
-		operand->mode = Addressing_Value;
-		operand->type = make_type_slice(c->allocator, type);
-	} break;
-
-	case BuiltinProc_size_of: {
-		// size_of :: proc(Type) -> untyped int
-		Type *type = check_type(c, ce->args.e[0]);
-		if (type == NULL || type == t_invalid) {
-			error(ast_node_token(ce->args.e[0]), "Expected a type for `size_of`");
-			return false;
-		}
-
-		operand->mode = Addressing_Constant;
-		operand->value = make_exact_value_integer(type_size_of(c->sizes, c->allocator, type));
-		operand->type = t_untyped_integer;
-
-	} break;
-
-	case BuiltinProc_size_of_val:
-		// size_of_val :: proc(val: Type) -> untyped int
-		check_assignment(c, operand, NULL, str_lit("argument of `size_of_val`"));
-		if (operand->mode == Addressing_Invalid) {
-			return false;
-		}
-
-		operand->mode = Addressing_Constant;
-		operand->value = make_exact_value_integer(type_size_of(c->sizes, c->allocator, operand->type));
-		operand->type = t_untyped_integer;
-		break;
-
-	case BuiltinProc_align_of: {
-		// align_of :: proc(Type) -> untyped int
-		Type *type = check_type(c, ce->args.e[0]);
-		if (type == NULL || type == t_invalid) {
-			error(ast_node_token(ce->args.e[0]), "Expected a type for `align_of`");
-			return false;
-		}
-		operand->mode = Addressing_Constant;
-		operand->value = make_exact_value_integer(type_align_of(c->sizes, c->allocator, type));
-		operand->type = t_untyped_integer;
-	} break;
-
-	case BuiltinProc_align_of_val:
-		// align_of_val :: proc(val: Type) -> untyped int
-		check_assignment(c, operand, NULL, str_lit("argument of `align_of_val`"));
-		if (operand->mode == Addressing_Invalid) {
-			return false;
-		}
-
-		operand->mode = Addressing_Constant;
-		operand->value = make_exact_value_integer(type_align_of(c->sizes, c->allocator, operand->type));
-		operand->type = t_untyped_integer;
-		break;
-
-	case BuiltinProc_offset_of: {
-		// offset_of :: proc(Type, field) -> untyped int
-		Operand op = {0};
-		Type *bt = check_type(c, ce->args.e[0]);
-		Type *type = base_type(bt);
-		if (type == NULL || type == t_invalid) {
-			error(ast_node_token(ce->args.e[0]), "Expected a type for `offset_of`");
-			return false;
-		}
-
-		AstNode *field_arg = unparen_expr(ce->args.e[1]);
-		if (field_arg == NULL ||
-		    field_arg->kind != AstNode_Ident) {
-			error(ast_node_token(field_arg), "Expected an identifier for field argument");
-			return false;
-		}
-		if (is_type_array(type) || is_type_vector(type)) {
-			error(ast_node_token(field_arg), "Invalid type for `offset_of`");
-			return false;
-		}
-
-
-		ast_node(arg, Ident, field_arg);
-		Selection sel = lookup_field(c->allocator, type, arg->string, operand->mode == Addressing_Type);
-		if (sel.entity == NULL) {
-			gbString type_str = type_to_string(bt);
-			error(ast_node_token(ce->args.e[0]),
-			      "`%s` has no field named `%.*s`", type_str, LIT(arg->string));
-			gb_string_free(type_str);
-			return false;
-		}
-		if (sel.indirect) {
-			gbString type_str = type_to_string(bt);
-			error(ast_node_token(ce->args.e[0]),
-			      "Field `%.*s` is embedded via a pointer in `%s`", LIT(arg->string), type_str);
-			gb_string_free(type_str);
-			return false;
-		}
-
-		operand->mode = Addressing_Constant;
-		operand->value = make_exact_value_integer(type_offset_of_from_selection(c->sizes, c->allocator, type, sel));
-		operand->type  = t_untyped_integer;
-	} break;
-
-	case BuiltinProc_offset_of_val: {
-		// offset_of_val :: proc(val: expression) -> untyped int
-		AstNode *arg = unparen_expr(ce->args.e[0]);
-		if (arg->kind != AstNode_SelectorExpr) {
-			gbString str = expr_to_string(arg);
-			error(ast_node_token(arg), "`%s` is not a selector expression", str);
-			return false;
-		}
-		ast_node(s, SelectorExpr, arg);
-
-		check_expr(c, operand, s->expr);
-		if (operand->mode == Addressing_Invalid) {
-			return false;
-		}
-
-		Type *type = operand->type;
-		if (base_type(type)->kind == Type_Pointer) {
-			Type *p = base_type(type);
-			if (is_type_struct(p)) {
-				type = p->Pointer.elem;
-			}
-		}
-		if (is_type_array(type) || is_type_vector(type)) {
-			error(ast_node_token(arg), "Invalid type for `offset_of_val`");
-			return false;
-		}
-
-		ast_node(i, Ident, s->selector);
-		Selection sel = lookup_field(c->allocator, type, i->string, operand->mode == Addressing_Type);
-		if (sel.entity == NULL) {
-			gbString type_str = type_to_string(type);
-			error(ast_node_token(arg),
-			      "`%s` has no field named `%.*s`", type_str, LIT(i->string));
-			return false;
-		}
-		if (sel.indirect) {
-			gbString type_str = type_to_string(type);
-			error(ast_node_token(ce->args.e[0]),
-			      "Field `%.*s` is embedded via a pointer in `%s`", LIT(i->string), type_str);
-			gb_string_free(type_str);
-			return false;
-		}
-
-
-		operand->mode = Addressing_Constant;
-		// IMPORTANT TODO(bill): Fix for anonymous fields
-		operand->value = make_exact_value_integer(type_offset_of_from_selection(c->sizes, c->allocator, type, sel));
-		operand->type  = t_untyped_integer;
-	} break;
-
-	case BuiltinProc_type_of_val:
-		// type_of_val :: proc(val: Type) -> type(Type)
-		check_assignment(c, operand, NULL, str_lit("argument of `type_of_val`"));
-		if (operand->mode == Addressing_Invalid || operand->mode == Addressing_Builtin) {
-			return false;
-		}
-		operand->mode = Addressing_Type;
-		break;
-
-
-	case BuiltinProc_type_info: {
-		// type_info :: proc(Type) -> ^Type_Info
-		AstNode *expr = ce->args.e[0];
-		Type *type = check_type(c, expr);
-		if (type == NULL || type == t_invalid) {
-			error(ast_node_token(expr), "Invalid argument to `type_info`");
-			return false;
-		}
-
-		add_type_info_type(c, type);
-
-		operand->mode = Addressing_Value;
-		operand->type = t_type_info_ptr;
-	} break;
-
-	case BuiltinProc_type_info_of_val: {
-		// type_info_of_val :: proc(val: Type) -> ^Type_Info
-		AstNode *expr = ce->args.e[0];
-
-		check_assignment(c, operand, NULL, str_lit("argument of `type_info_of_val`"));
-		if (operand->mode == Addressing_Invalid || operand->mode == Addressing_Builtin)
-			return false;
-		add_type_info_type(c, operand->type);
-
-		operand->mode = Addressing_Value;
-		operand->type = t_type_info_ptr;
-	} break;
-
-
-
-	case BuiltinProc_compile_assert:
-		// compile_assert :: proc(cond: bool)
-
-		if (!is_type_boolean(operand->type) && operand->mode != Addressing_Constant) {
-			gbString str = expr_to_string(ce->args.e[0]);
-			error(ast_node_token(call), "`%s` is not a constant boolean", str);
-			gb_string_free(str);
-			return false;
-		}
-		if (!operand->value.value_bool) {
-			gbString str = expr_to_string(ce->args.e[0]);
-			error(ast_node_token(call), "Compile time assertion: `%s`", str);
-			gb_string_free(str);
-		}
-		break;
-
-	case BuiltinProc_assert:
-		// assert :: proc(cond: bool)
-
-		if (!is_type_boolean(operand->type)) {
-			gbString str = expr_to_string(ce->args.e[0]);
-			error(ast_node_token(call), "`%s` is not a boolean", str);
-			gb_string_free(str);
-			return false;
-		}
-
-		operand->mode = Addressing_NoValue;
-		break;
-
-	case BuiltinProc_panic:
-		// panic :: proc(msg: string)
-
-		if (!is_type_string(operand->type)) {
-			gbString str = expr_to_string(ce->args.e[0]);
-			error(ast_node_token(call), "`%s` is not a string", str);
-			gb_string_free(str);
-			return false;
-		}
-
-		operand->mode = Addressing_NoValue;
-		break;
-
-	case BuiltinProc_copy: {
-		// copy :: proc(x, y: []Type) -> int
-		Type *dest_type = NULL, *src_type = NULL;
-
-		Type *d = base_type(operand->type);
-		if (d->kind == Type_Slice) {
-			dest_type = d->Slice.elem;
-		}
-		Operand op = {0};
-		check_expr(c, &op, ce->args.e[1]);
-		if (op.mode == Addressing_Invalid) {
-			return false;
-		}
-		Type *s = base_type(op.type);
-		if (s->kind == Type_Slice) {
-			src_type = s->Slice.elem;
-		}
-
-		if (dest_type == NULL || src_type == NULL) {
-			error(ast_node_token(call), "`copy` only expects slices as arguments");
-			return false;
-		}
-
-		if (!are_types_identical(dest_type, src_type)) {
-			gbString d_arg = expr_to_string(ce->args.e[0]);
-			gbString s_arg = expr_to_string(ce->args.e[1]);
-			gbString d_str = type_to_string(dest_type);
-			gbString s_str = type_to_string(src_type);
-			error(ast_node_token(call),
-			      "Arguments to `copy`, %s, %s, have different elem types: %s vs %s",
-			      d_arg, s_arg, d_str, s_str);
-			gb_string_free(s_str);
-			gb_string_free(d_str);
-			gb_string_free(s_arg);
-			gb_string_free(d_arg);
-			return false;
-		}
-
-		operand->type = t_int; // Returns number of elems copied
-		operand->mode = Addressing_Value;
-	} break;
-
-	case BuiltinProc_append: {
-		// append :: proc(x : ^[]Type, y : Type) -> bool
-		Type *x_type = NULL, *y_type = NULL;
-		x_type = base_type(operand->type);
-
-		Operand op = {0};
-		check_expr(c, &op, ce->args.e[1]);
-		if (op.mode == Addressing_Invalid) {
-			return false;
-		}
-		y_type = base_type(op.type);
-
-		if (!(is_type_pointer(x_type) && is_type_slice(x_type->Pointer.elem))) {
-			error(ast_node_token(call), "First argument to `append` must be a pointer to a slice");
-			return false;
-		}
-
-		Type *elem_type = x_type->Pointer.elem->Slice.elem;
-		if (!check_is_assignable_to(c, &op, elem_type)) {
-			gbString d_arg = expr_to_string(ce->args.e[0]);
-			gbString s_arg = expr_to_string(ce->args.e[1]);
-			gbString d_str = type_to_string(elem_type);
-			gbString s_str = type_to_string(y_type);
-			error(ast_node_token(call),
-			      "Arguments to `append`, %s, %s, have different element types: %s vs %s",
-			      d_arg, s_arg, d_str, s_str);
-			gb_string_free(s_str);
-			gb_string_free(d_str);
-			gb_string_free(s_arg);
-			gb_string_free(d_arg);
-			return false;
-		}
-
-		operand->type = t_bool; // Returns if it was successful
-		operand->mode = Addressing_Value;
-	} break;
-
-	case BuiltinProc_swizzle: {
-		// swizzle :: proc(v: {N}T, T...) -> {M}T
-		Type *vector_type = base_type(operand->type);
-		if (!is_type_vector(vector_type)) {
-			gbString type_str = type_to_string(operand->type);
-			error(ast_node_token(call),
-			      "You can only `swizzle` a vector, got `%s`",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
-		}
-
-		isize max_count = vector_type->Vector.count;
-		isize arg_count = 0;
-		for_array(i, ce->args) {
-			if (i == 0) {
-				continue;
-			}
-			AstNode *arg = ce->args.e[i];
-			Operand op = {0};
-			check_expr(c, &op, arg);
-			if (op.mode == Addressing_Invalid) {
-				return false;
-			}
-			Type *arg_type = base_type(op.type);
-			if (!is_type_integer(arg_type) || op.mode != Addressing_Constant) {
-				error(ast_node_token(op.expr), "Indices to `swizzle` must be constant integers");
-				return false;
-			}
-
-			if (op.value.value_integer < 0) {
-				error(ast_node_token(op.expr), "Negative `swizzle` index");
-				return false;
-			}
-
-			if (max_count <= op.value.value_integer) {
-				error(ast_node_token(op.expr), "`swizzle` index exceeds vector length");
-				return false;
-			}
-
-			arg_count++;
-		}
-
-		if (arg_count > max_count) {
-			error(ast_node_token(call), "Too many `swizzle` indices, %td > %td", arg_count, max_count);
-			return false;
-		}
-
-		Type *elem_type = vector_type->Vector.elem;
-		operand->type = make_type_vector(c->allocator, elem_type, arg_count);
-		operand->mode = Addressing_Value;
-	} break;
-
-#if 0
-	case BuiltinProc_ptr_offset: {
-		// ptr_offset :: proc(ptr: ^T, offset: int) -> ^T
-		// ^T cannot be rawptr
-		Type *ptr_type = base_type(operand->type);
-		if (!is_type_pointer(ptr_type)) {
-			gbString type_str = type_to_string(operand->type);
-			defer (gb_string_free(type_str));
-			error(ast_node_token(call),
-			      "Expected a pointer to `ptr_offset`, got `%s`",
-			      type_str);
-			return false;
-		}
-
-		if (ptr_type == t_rawptr) {
-			error(ast_node_token(call),
-			      "`rawptr` cannot have pointer arithmetic");
-			return false;
-		}
-
-		AstNode *offset = ce->args.e[1];
-		Operand op = {0};
-		check_expr(c, &op, offset);
-		if (op.mode == Addressing_Invalid)
-			return false;
-		Type *offset_type = base_type(op.type);
-		if (!is_type_integer(offset_type)) {
-			error(ast_node_token(op.expr), "Pointer offsets for `ptr_offset` must be an integer");
-			return false;
-		}
-
-		if (operand->mode == Addressing_Constant &&
-		    op.mode == Addressing_Constant) {
-			i64 ptr = operand->value.value_pointer;
-			i64 elem_size = type_size_of(c->sizes, c->allocator, ptr_type->Pointer.elem);
-			ptr += elem_size * op.value.value_integer;
-			operand->value.value_pointer = ptr;
-		} else {
-			operand->mode = Addressing_Value;
-		}
-
-	} break;
-
-	case BuiltinProc_ptr_sub: {
-		// ptr_sub :: proc(a, b: ^T) -> int
-		// ^T cannot be rawptr
-		Type *ptr_type = base_type(operand->type);
-		if (!is_type_pointer(ptr_type)) {
-			gbString type_str = type_to_string(operand->type);
-			defer (gb_string_free(type_str));
-			error(ast_node_token(call),
-			      "Expected a pointer to `ptr_add`, got `%s`",
-			      type_str);
-			return false;
-		}
-
-		if (ptr_type == t_rawptr) {
-			error(ast_node_token(call),
-			      "`rawptr` cannot have pointer arithmetic");
-			return false;
-		}
-		AstNode *offset = ce->args[1];
-		Operand op = {0};
-		check_expr(c, &op, offset);
-		if (op.mode == Addressing_Invalid)
-			return false;
-		if (!is_type_pointer(op.type)) {
-			gbString type_str = type_to_string(operand->type);
-			defer (gb_string_free(type_str));
-			error(ast_node_token(call),
-			      "Expected a pointer to `ptr_add`, got `%s`",
-			      type_str);
-			return false;
-		}
-
-		if (base_type(op.type) == t_rawptr) {
-			error(ast_node_token(call),
-			      "`rawptr` cannot have pointer arithmetic");
-			return false;
-		}
-
-		if (!are_types_identical(operand->type, op.type)) {
-			gbString a = type_to_string(operand->type);
-			gbString b = type_to_string(op.type);
-			defer (gb_string_free(a));
-			defer (gb_string_free(b));
-			error(ast_node_token(op.expr),
-			      "`ptr_sub` requires to pointer of the same type. Got `%s` and `%s`.", a, b);
-			return false;
-		}
-
-		operand->type = t_int;
-
-		if (operand->mode == Addressing_Constant &&
-		    op.mode == Addressing_Constant) {
-			u8 *ptr_a = cast(u8 *)operand->value.value_pointer;
-			u8 *ptr_b = cast(u8 *)op.value.value_pointer;
-			isize elem_size = type_size_of(c->sizes, c->allocator, ptr_type->Pointer.elem);
-			operand->value = make_exact_value_integer((ptr_a - ptr_b) / elem_size);
-		} else {
-			operand->mode = Addressing_Value;
-		}
-	} break;
-#endif
-
-	case BuiltinProc_slice_ptr: {
-		// slice_ptr :: proc(a: ^T, len: int[, cap: int]) -> []T
-		// ^T cannot be rawptr
-		Type *ptr_type = base_type(operand->type);
-		if (!is_type_pointer(ptr_type)) {
-			gbString type_str = type_to_string(operand->type);
-			error(ast_node_token(call),
-			      "Expected a pointer to `slice_ptr`, got `%s`",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
-		}
-
-		if (ptr_type == t_rawptr) {
-			error(ast_node_token(call),
-			      "`rawptr` cannot have pointer arithmetic");
-			return false;
-		}
-
-		AstNode *len = ce->args.e[1];
-		AstNode *cap = NULL;
-		if (ce->args.count > 2) {
-			cap = ce->args.e[2];
-		}
-
-		Operand op = {0};
-		check_expr(c, &op, len);
-		if (op.mode == Addressing_Invalid)
-			return false;
-		if (!is_type_integer(op.type)) {
-			gbString type_str = type_to_string(operand->type);
-			error(ast_node_token(call),
-			      "Length for `slice_ptr` must be an integer, got `%s`",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
-		}
-
-		if (cap != NULL) {
-			check_expr(c, &op, cap);
-			if (op.mode == Addressing_Invalid)
-				return false;
-			if (!is_type_integer(op.type)) {
-				gbString type_str = type_to_string(operand->type);
-				error(ast_node_token(call),
-				      "Capacity for `slice_ptr` must be an integer, got `%s`",
-				      type_str);
-				gb_string_free(type_str);
-				return false;
-			}
-			if (ce->args.count > 3) {
-				error(ast_node_token(call),
-				      "Too many arguments to `slice_ptr`, expected either 2 or 3");
-				return false;
-			}
-		}
-
-		operand->type = make_type_slice(c->allocator, ptr_type->Pointer.elem);
-		operand->mode = Addressing_Value;
-	} break;
-
-	case BuiltinProc_min: {
-		// min :: proc(a, b: comparable) -> comparable
-		Type *type = base_type(operand->type);
-		if (!is_type_comparable(type) || !is_type_numeric(type)) {
-			gbString type_str = type_to_string(operand->type);
-			error(ast_node_token(call),
-			      "Expected a comparable numeric type to `min`, got `%s`",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
-		}
-
-		AstNode *other_arg = ce->args.e[1];
-		Operand a = *operand;
-		Operand b = {0};
-		check_expr(c, &b, other_arg);
-		if (b.mode == Addressing_Invalid) {
-			return false;
-		}
-		if (!is_type_comparable(b.type) || !is_type_numeric(type)) {
-			gbString type_str = type_to_string(b.type);
-			error(ast_node_token(call),
-			      "Expected a comparable numeric type to `min`, got `%s`",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
-		}
-
-		if (a.mode == Addressing_Constant &&
-		    b.mode == Addressing_Constant) {
-			ExactValue x = a.value;
-			ExactValue y = b.value;
-			Token lt = {Token_Lt};
-
-			operand->mode = Addressing_Constant;
-			if (compare_exact_values(lt, x, y)) {
-				operand->value = x;
-				operand->type = a.type;
-			} else {
-				operand->value = y;
-				operand->type = b.type;
-			}
-		} else {
-			operand->mode = Addressing_Value;
-			operand->type = type;
-
-			convert_to_typed(c, &a, b.type, 0);
-			if (a.mode == Addressing_Invalid) {
-				return false;
-			}
-			convert_to_typed(c, &b, a.type, 0);
-			if (b.mode == Addressing_Invalid) {
-				return false;
-			}
-
-			if (!are_types_identical(operand->type, b.type)) {
-				gbString type_a = type_to_string(a.type);
-				gbString type_b = type_to_string(b.type);
-				error(ast_node_token(call),
-				      "Mismatched types to `min`, `%s` vs `%s`",
-				      type_a, type_b);
-				gb_string_free(type_b);
-				gb_string_free(type_a);
-				return false;
-			}
-		}
-
-	} break;
-
-	case BuiltinProc_max: {
-		// min :: proc(a, b: comparable) -> comparable
-		Type *type = base_type(operand->type);
-		if (!is_type_comparable(type) || !is_type_numeric(type)) {
-			gbString type_str = type_to_string(operand->type);
-			error(ast_node_token(call),
-			      "Expected a comparable numeric type to `max`, got `%s`",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
-		}
-
-		AstNode *other_arg = ce->args.e[1];
-		Operand a = *operand;
-		Operand b = {0};
-		check_expr(c, &b, other_arg);
-		if (b.mode == Addressing_Invalid) {
-			return false;
-		}
-		if (!is_type_comparable(b.type) || !is_type_numeric(type)) {
-			gbString type_str = type_to_string(b.type);
-			error(ast_node_token(call),
-			      "Expected a comparable numeric type to `max`, got `%s`",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
-		}
-
-		if (a.mode == Addressing_Constant &&
-		    b.mode == Addressing_Constant) {
-			ExactValue x = a.value;
-			ExactValue y = b.value;
-			Token gt = {Token_Gt};
-
-			operand->mode = Addressing_Constant;
-			if (compare_exact_values(gt, x, y)) {
-				operand->value = x;
-				operand->type = a.type;
-			} else {
-				operand->value = y;
-				operand->type = b.type;
-			}
-		} else {
-			operand->mode = Addressing_Value;
-			operand->type = type;
-
-			convert_to_typed(c, &a, b.type, 0);
-			if (a.mode == Addressing_Invalid) {
-				return false;
-			}
-			convert_to_typed(c, &b, a.type, 0);
-			if (b.mode == Addressing_Invalid) {
-				return false;
-			}
-
-			if (!are_types_identical(operand->type, b.type)) {
-				gbString type_a = type_to_string(a.type);
-				gbString type_b = type_to_string(b.type);
-				error(ast_node_token(call),
-				      "Mismatched types to `max`, `%s` vs `%s`",
-				      type_a, type_b);
-				gb_string_free(type_b);
-				gb_string_free(type_a);
-				return false;
-			}
-		}
-
-	} break;
-
-	case BuiltinProc_abs: {
-		// abs :: proc(n: numeric) -> numeric
-		Type *type = base_type(operand->type);
-		if (!is_type_numeric(type)) {
-			gbString type_str = type_to_string(operand->type);
-			error(ast_node_token(call),
-			      "Expected a numeric type to `abs`, got `%s`",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
-		}
-
-		if (operand->mode == Addressing_Constant) {
-			switch (operand->value.kind) {
-			case ExactValue_Integer:
-				operand->value.value_integer = gb_abs(operand->value.value_integer);
-				break;
-			case ExactValue_Float:
-				operand->value.value_float = gb_abs(operand->value.value_float);
-				break;
-			default:
-				GB_PANIC("Invalid numeric constant");
-				break;
-			}
-		} else {
-			operand->mode = Addressing_Value;
-		}
-
-		operand->type = type;
-	} break;
-
-	case BuiltinProc_enum_to_string: {
-		Type *type = base_type(operand->type);
-		if (!is_type_enum(type)) {
-			gbString type_str = type_to_string(operand->type);
-			gb_string_free(type_str);
-			error(ast_node_token(call),
-			      "Expected an enum to `enum_to_string`, got `%s`",
-			      type_str);
-			return false;
-		}
-
-		if (operand->mode == Addressing_Constant) {
-			ExactValue value = make_exact_value_string(str_lit(""));
-			if (operand->value.kind == ExactValue_Integer) {
-				i64 index = operand->value.value_integer;
-				for (isize i = 0; i < type->Record.other_field_count; i++) {
-					Entity *f = type->Record.other_fields[i];
-					if (f->kind == Entity_Constant && f->Constant.value.kind == ExactValue_Integer) {
-						i64 fv = f->Constant.value.value_integer;
-						if (index == fv) {
-							value = make_exact_value_string(f->token.string);
-							break;
-						}
-					}
-				}
-			}
-
-			operand->value = value;
-			operand->type = t_string;
-			return true;
-		}
-
-		add_type_info_type(c, operand->type);
-
-		operand->mode = Addressing_Value;
-		operand->type = t_string;
-	} break;
-	}
-
-	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);
-	ast_node(ce, CallExpr, call);
-
-	isize param_count = 0;
-	bool variadic = proc_type->Proc.variadic;
-	bool vari_expand = (ce->ellipsis.pos.line != 0);
-
-	if (proc_type->Proc.params != NULL) {
-		param_count = proc_type->Proc.params->Tuple.variable_count;
-		if (variadic) {
-			param_count--;
-		}
-	}
-
-	if (vari_expand && !variadic) {
-		error(ce->ellipsis,
-		      "Cannot use `..` in call to a non-variadic procedure: `%.*s`",
-		      LIT(ce->proc->Ident.string));
-		return;
-	}
-
-	if (ce->args.count == 0 && param_count == 0) {
-		return;
-	}
-
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-
-	Array(Operand) operands;
-	array_init_reserve(&operands, c->tmp_allocator, 2*param_count);
-
-	for_array(i, ce->args) {
-		Operand o = {0};
-		check_multi_expr(c, &o, ce->args.e[i]);
-		if (o.type->kind != Type_Tuple) {
-			array_add(&operands, o);
-		} else {
-			TypeTuple *tuple = &o.type->Tuple;
-			if (variadic && i >= param_count) {
-				error(ast_node_token(ce->args.e[i]),
-				      "`..` in a variadic procedure cannot be applied to a %td-valued expression", tuple->variable_count);
-				operand->mode = Addressing_Invalid;
-				goto end;
-			}
-			for (isize j = 0; j < tuple->variable_count; j++) {
-				o.type = tuple->variables[j]->type;
-				array_add(&operands, o);
-			}
-		}
-	}
-
-	i32 error_code = 0;
-	if (operands.count < param_count) {
-		error_code = -1;
-	} else if (!variadic && operands.count > param_count) {
-		error_code = +1;
-	}
-	if (error_code != 0) {
-		char *err_fmt = "Too many arguments for `%s`, expected %td arguments";
-		if (error_code < 0) {
-			err_fmt = "Too few arguments for `%s`, expected %td arguments";
-		}
-
-		gbString proc_str = expr_to_string(ce->proc);
-		error(ast_node_token(call), err_fmt, proc_str, param_count);
-		gb_string_free(proc_str);
-		operand->mode = Addressing_Invalid;
-		goto end;
-	}
-
-	GB_ASSERT(proc_type->Proc.params != NULL);
-	Entity **sig_params = proc_type->Proc.params->Tuple.variables;
-	isize operand_index = 0;
-	for (; operand_index < param_count; operand_index++) {
-		Type *arg_type = sig_params[operand_index]->type;
-		Operand o = operands.e[operand_index];
-		if (variadic) {
-			o = operands.e[operand_index];
-		}
-		check_assignment(c, &o, arg_type, str_lit("argument"));
-	}
-
-	if (variadic) {
-		bool variadic_expand = false;
-		Type *slice = sig_params[param_count]->type;
-		GB_ASSERT(is_type_slice(slice));
-		Type *elem = base_type(slice)->Slice.elem;
-		Type *t = elem;
-		for (; operand_index < operands.count; operand_index++) {
-			Operand o = operands.e[operand_index];
-			if (vari_expand) {
-				variadic_expand = true;
-				t = slice;
-				if (operand_index != param_count) {
-					error(ast_node_token(o.expr),
-					      "`..` in a variadic procedure can only have one variadic argument at the end");
-					break;
-				}
-			}
-			check_assignment(c, &o, t, str_lit("argument"));
-		}
-	}
-end:
-	gb_temp_arena_memory_end(tmp);
-}
-
-
-Entity *find_using_index_expr(Type *t) {
-	t = base_type(t);
-	if (t->kind != Type_Record) {
-		return NULL;
-	}
-
-	for (isize i = 0; i < t->Record.field_count; i++) {
-		Entity *f = t->Record.fields[i];
-		if (f->kind == Entity_Variable &&
-		    f->flags & (EntityFlag_Anonymous|EntityFlag_Field)) {
-			if (is_type_indexable(f->type)) {
-				return f;
-			}
-			Entity *res = find_using_index_expr(f->type);
-			if (res != NULL) {
-				return res;
-			}
-		}
-	}
-	return NULL;
-}
-
-ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
-	GB_ASSERT(call->kind == AstNode_CallExpr);
-	ast_node(ce, CallExpr, call);
-	check_expr_or_type(c, operand, ce->proc);
-
-	if (operand->mode == Addressing_Invalid) {
-		for_array(i, ce->args) {
-			check_expr_base(c, operand, ce->args.e[i], NULL);
-		}
-		operand->mode = Addressing_Invalid;
-		operand->expr = call;
-		return Expr_Stmt;
-	}
-
-
-	if (operand->mode == Addressing_Builtin) {
-		i32 id = operand->builtin_id;
-		if (!check_builtin_procedure(c, operand, call, id)) {
-			operand->mode = Addressing_Invalid;
-		}
-		operand->expr = call;
-		return builtin_procs[id].kind;
-	}
-
-	Type *proc_type = base_type(operand->type);
-	if (proc_type == NULL || proc_type->kind != Type_Proc) {
-		AstNode *e = operand->expr;
-		gbString str = expr_to_string(e);
-		error(ast_node_token(e), "Cannot call a non-procedure: `%s`", str);
-		gb_string_free(str);
-
-		operand->mode = Addressing_Invalid;
-		operand->expr = call;
-
-		return Expr_Stmt;
-	}
-
-	check_call_arguments(c, operand, proc_type, call);
-
-	switch (proc_type->Proc.result_count) {
-	case 0:
-		operand->mode = Addressing_NoValue;
-		break;
-	case 1:
-		operand->mode = Addressing_Value;
-		operand->type = proc_type->Proc.results->Tuple.variables[0]->type;
-		break;
-	default:
-		operand->mode = Addressing_Value;
-		operand->type = proc_type->Proc.results;
-		break;
-	}
-
-	operand->expr = call;
-	return Expr_Stmt;
-}
-
-void check_expr_with_type_hint(Checker *c, Operand *o, AstNode *e, Type *t) {
-	check_expr_base(c, o, e, t);
-	check_not_tuple(c, o);
-	char *err_str = NULL;
-	switch (o->mode) {
-	case Addressing_NoValue:
-		err_str = "used as a value";
-		break;
-	case Addressing_Type:
-		err_str = "is not an expression";
-		break;
-	case Addressing_Builtin:
-		err_str = "must be called";
-		break;
-	}
-	if (err_str != NULL) {
-		gbString str = expr_to_string(e);
-		error(ast_node_token(e), "`%s` %s", str, err_str);
-		gb_string_free(str);
-		o->mode = Addressing_Invalid;
-	}
-}
-
-bool check_set_index_data(Operand *o, Type *t, i64 *max_count) {
-	t = base_type(type_deref(t));
-
-	switch (t->kind) {
-	case Type_Basic:
-		if (is_type_string(t)) {
-			if (o->mode == Addressing_Constant) {
-				*max_count = o->value.value_string.len;
-			}
-			if (o->mode != Addressing_Variable) {
-				o->mode = Addressing_Value;
-			}
-			o->type = t_u8;
-			return true;
-		}
-		break;
-
-	case Type_Array:
-		*max_count = t->Array.count;
-		if (o->mode != Addressing_Variable) {
-			o->mode = Addressing_Value;
-		}
-		o->type = t->Array.elem;
-		return true;
-
-	case Type_Vector:
-		*max_count = t->Vector.count;
-		if (o->mode != Addressing_Variable) {
-			o->mode = Addressing_Value;
-		}
-		o->type = t->Vector.elem;
-		return true;
-
-
-	case Type_Slice:
-		o->type = t->Slice.elem;
-		o->mode = Addressing_Variable;
-		return true;
-	}
-
-	return false;
-}
-
-ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint) {
-	ExprKind kind = Expr_Stmt;
-
-	o->mode = Addressing_Invalid;
-	o->type = t_invalid;
-
-	switch (node->kind) {
-	default:
-		goto error;
-		break;
-
-	case_ast_node(be, BadExpr, node)
-		goto error;
-	case_end;
-
-	case_ast_node(i, Ident, node);
-		check_identifier(c, o, node, type_hint, NULL);
-	case_end;
-
-	case_ast_node(bl, BasicLit, node);
-		Type *t = t_invalid;
-		switch (bl->kind) {
-		case Token_Integer: t = t_untyped_integer; break;
-		case Token_Float:   t = t_untyped_float;   break;
-		case Token_String:  t = t_untyped_string;  break;
-		case Token_Rune:    t = t_untyped_rune;    break;
-		default:            GB_PANIC("Unknown literal"); break;
-		}
-		o->mode  = Addressing_Constant;
-		o->type  = t;
-		o->value = make_exact_value_from_basic_literal(*bl);
-	case_end;
-
-	case_ast_node(pl, ProcLit, node);
-		check_open_scope(c, pl->type);
-		c->context.decl = make_declaration_info(c->allocator, c->context.scope);
-		Type *proc_type = check_type(c, pl->type);
-		if (proc_type != NULL) {
-			check_proc_body(c, empty_token, c->context.decl, proc_type, pl->body);
-			o->mode = Addressing_Value;
-			o->type = proc_type;
-			check_close_scope(c);
-		} else {
-			gbString str = expr_to_string(node);
-			error(ast_node_token(node), "Invalid procedure literal `%s`", str);
-			gb_string_free(str);
-			check_close_scope(c);
-			goto error;
-		}
-	case_end;
-
-	case_ast_node(cl, CompoundLit, node);
-		Type *type = type_hint;
-		bool ellipsis_array = false;
-		bool is_constant = true;
-		if (cl->type != NULL) {
-			type = NULL;
-
-			// [..]Type
-			if (cl->type->kind == AstNode_ArrayType && cl->type->ArrayType.count != NULL) {
-				if (cl->type->ArrayType.count->kind == AstNode_Ellipsis) {
-					type = make_type_array(c->allocator, check_type(c, cl->type->ArrayType.elem), -1);
-					ellipsis_array = true;
-				}
-			}
-
-			if (type == NULL) {
-				type = check_type(c, cl->type);
-			}
-		}
-
-		if (type == NULL) {
-			error(ast_node_token(node), "Missing type in compound literal");
-			goto error;
-		}
-
-		Type *t = base_type(type);
-		switch (t->kind) {
-		case Type_Record: {
-			if (!is_type_struct(t)) {
-				if (cl->elems.count != 0) {
-					error(ast_node_token(node), "Illegal compound literal");
-				}
-				break;
-			}
-			if (cl->elems.count == 0) {
-				break; // NOTE(bill): No need to init
-			}
-			{ // Checker values
-				isize field_count = t->Record.field_count;
-				if (cl->elems.e[0]->kind == AstNode_FieldValue) {
-					bool *fields_visited = gb_alloc_array(c->allocator, bool, field_count);
-
-					for_array(i, cl->elems) {
-						AstNode *elem = cl->elems.e[i];
-						if (elem->kind != AstNode_FieldValue) {
-							error(ast_node_token(elem),
-							      "Mixture of `field = value` and value elements in a structure literal is not allowed");
-							continue;
-						}
-						ast_node(fv, FieldValue, elem);
-						if (fv->field->kind != AstNode_Ident) {
-							gbString expr_str = expr_to_string(fv->field);
-							error(ast_node_token(elem),
-							      "Invalid field name `%s` in structure literal", expr_str);
-							gb_string_free(expr_str);
-							continue;
-						}
-						String name = fv->field->Ident.string;
-
-						Selection sel = lookup_field(c->allocator, type, name, o->mode == Addressing_Type);
-						if (sel.entity == NULL) {
-							error(ast_node_token(elem),
-							      "Unknown field `%.*s` in structure literal", LIT(name));
-							continue;
-						}
-
-						if (sel.index.count > 1) {
-							error(ast_node_token(elem),
-							      "Cannot assign to an anonymous field `%.*s` in a structure literal (at the moment)", LIT(name));
-							continue;
-						}
-
-						Entity *field = t->Record.fields[sel.index.e[0]];
-						add_entity_use(c, fv->field, field);
-
-						if (fields_visited[sel.index.e[0]]) {
-							error(ast_node_token(elem),
-							      "Duplicate field `%.*s` in structure literal", LIT(name));
-							continue;
-						}
-
-						fields_visited[sel.index.e[0]] = true;
-						check_expr(c, o, fv->value);
-
-						if (base_type(field->type) == t_any) {
-							is_constant = false;
-						}
-						if (is_constant) {
-							is_constant = o->mode == Addressing_Constant;
-						}
-
-
-						check_assignment(c, o, field->type, str_lit("structure literal"));
-					}
-				} else {
-					for_array(index, cl->elems) {
-						AstNode *elem = cl->elems.e[index];
-						if (elem->kind == AstNode_FieldValue) {
-							error(ast_node_token(elem),
-							      "Mixture of `field = value` and value elements in a structure literal is not allowed");
-							continue;
-						}
-						Entity *field = t->Record.fields_in_src_order[index];
-
-						check_expr(c, o, elem);
-						if (index >= field_count) {
-							error(ast_node_token(o->expr), "Too many values in structure literal, expected %td", field_count);
-							break;
-						}
-
-						if (base_type(field->type) == t_any) {
-							is_constant = false;
-						}
-						if (is_constant) {
-							is_constant = o->mode == Addressing_Constant;
-						}
-
-						check_assignment(c, o, field->type, str_lit("structure literal"));
-					}
-					if (cl->elems.count < field_count) {
-						error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
-					}
-				}
-			}
-
-		} break;
-
-		case Type_Slice:
-		case Type_Array:
-		case Type_Vector:
-		{
-			Type *elem_type = NULL;
-			String context_name = {0};
-			if (t->kind == Type_Slice) {
-				elem_type = t->Slice.elem;
-				context_name = str_lit("slice literal");
-			} else if (t->kind == Type_Vector) {
-				elem_type = t->Vector.elem;
-				context_name = str_lit("vector literal");
-			} else {
-				elem_type = t->Array.elem;
-				context_name = str_lit("array literal");
-			}
-
-
-			i64 max = 0;
-			isize index = 0;
-			isize elem_count = cl->elems.count;
-
-			if (base_type(elem_type) == t_any) {
-				is_constant = false;
-			}
-
-			for (; index < elem_count; index++) {
-				AstNode *e = cl->elems.e[index];
-				if (e->kind == AstNode_FieldValue) {
-					error(ast_node_token(e),
-					      "`field = value` is only allowed in struct literals");
-					continue;
-				}
-
-				if (t->kind == Type_Array &&
-				    t->Array.count >= 0 &&
-				    index >= t->Array.count) {
-					error(ast_node_token(e), "Index %lld is out of bounds (>= %lld) for array literal", index, t->Array.count);
-				}
-				if (t->kind == Type_Vector &&
-				    t->Vector.count >= 0 &&
-				    index >= t->Vector.count) {
-					error(ast_node_token(e), "Index %lld is out of bounds (>= %lld) for vector literal", index, t->Vector.count);
-				}
-
-				Operand operand = {0};
-				check_expr_with_type_hint(c, &operand, e, elem_type);
-				check_assignment(c, &operand, elem_type, context_name);
-
-				if (is_constant) {
-					is_constant = operand.mode == Addressing_Constant;
-				}
-			}
-			if (max < index) {
-				max = index;
-			}
-
-			if (t->kind == Type_Vector) {
-				if (t->Vector.count > 1 && gb_is_between(index, 2, t->Vector.count-1)) {
-					error(ast_node_token(cl->elems.e[0]),
-					      "Expected either 1 (broadcast) or %td elements in vector literal, got %td", t->Vector.count, index);
-				}
-			}
-
-			if (t->kind == Type_Array && ellipsis_array) {
-				t->Array.count = max;
-			}
-		} break;
-
-		default: {
-			gbString str = type_to_string(type);
-			error(ast_node_token(node), "Invalid compound literal type `%s`", str);
-			gb_string_free(str);
-			goto error;
-		} break;
-		}
-
-		if (is_constant) {
-			o->mode = Addressing_Constant;
-			o->value = make_exact_value_compound(node);
-		} else {
-			o->mode = Addressing_Value;
-		}
-		o->type = type;
-	case_end;
-
-	case_ast_node(pe, ParenExpr, node);
-		kind = check_expr_base(c, o, pe->expr, type_hint);
-		o->expr = node;
-	case_end;
-
-
-	case_ast_node(te, TagExpr, node);
-		// TODO(bill): Tag expressions
-		error(ast_node_token(node), "Tag expressions are not supported yet");
-		kind = check_expr_base(c, o, te->expr, type_hint);
-		o->expr = node;
-	case_end;
-
-	case_ast_node(re, RunExpr, node);
-		// TODO(bill): Tag expressions
-		kind = check_expr_base(c, o, re->expr, type_hint);
-		o->expr = node;
-	case_end;
-
-
-	case_ast_node(ue, UnaryExpr, node);
-		check_expr(c, o, ue->expr);
-		if (o->mode == Addressing_Invalid) {
-			goto error;
-		}
-		check_unary_expr(c, o, ue->op, node);
-		if (o->mode == Addressing_Invalid) {
-			goto error;
-		}
-	case_end;
-
-
-	case_ast_node(be, BinaryExpr, node);
-		check_binary_expr(c, o, node);
-		if (o->mode == Addressing_Invalid) {
-			goto error;
-		}
-	case_end;
-
-
-
-	case_ast_node(se, SelectorExpr, node);
-		check_selector(c, o, node);
-	case_end;
-
-
-	case_ast_node(ie, IndexExpr, node);
-		check_expr(c, o, ie->expr);
-		if (o->mode == Addressing_Invalid) {
-			goto error;
-		}
-
-		Type *t = base_type(type_deref(o->type));
-		bool is_const = o->mode == Addressing_Constant;
-
-		i64 max_count = -1;
-		bool valid = check_set_index_data(o, t, &max_count);
-
-		if (is_const) {
-			valid = false;
-		}
-
-		if (!valid && (is_type_struct(t) || is_type_raw_union(t))) {
-			Entity *found = find_using_index_expr(t);
-			if (found != NULL) {
-				valid = check_set_index_data(o, found->type, &max_count);
-			}
-		}
-
-		if (!valid) {
-			gbString str = expr_to_string(o->expr);
-			if (is_const) {
-				error(ast_node_token(o->expr), "Cannot index a constant `%s`", str);
-			} else {
-				error(ast_node_token(o->expr), "Cannot index `%s`", str);
-			}
-			gb_string_free(str);
-			goto error;
-		}
-
-		if (ie->index == NULL) {
-			gbString str = expr_to_string(o->expr);
-			error(ast_node_token(o->expr), "Missing index for `%s`", str);
-			gb_string_free(str);
-			goto error;
-		}
-
-		i64 index = 0;
-		bool ok = check_index_value(c, ie->index, max_count, &index);
-
-	case_end;
-
-
-
-	case_ast_node(se, SliceExpr, node);
-		check_expr(c, o, se->expr);
-		if (o->mode == Addressing_Invalid) {
-			goto error;
-		}
-
-		bool valid = false;
-		i64 max_count = -1;
-		Type *t = base_type(type_deref(o->type));
-		switch (t->kind) {
-		case Type_Basic:
-			if (is_type_string(t)) {
-				valid = true;
-				if (o->mode == Addressing_Constant) {
-					max_count = o->value.value_string.len;
-				}
-				if (se->max != NULL) {
-					error(ast_node_token(se->max), "Max (3rd) index not needed in substring expression");
-				}
-				o->type = t_string;
-			}
-			break;
-
-		case Type_Array:
-			valid = true;
-			max_count = t->Array.count;
-			if (o->mode != Addressing_Variable) {
-				gbString str = expr_to_string(node);
-				error(ast_node_token(node), "Cannot slice array `%s`, value is not addressable", str);
-				gb_string_free(str);
-				goto error;
-			}
-			o->type = make_type_slice(c->allocator, t->Array.elem);
-			break;
-
-		case Type_Slice:
-			valid = true;
-			break;
-		}
-
-		if (!valid) {
-			gbString str = expr_to_string(o->expr);
-			error(ast_node_token(o->expr), "Cannot slice `%s`", str);
-			gb_string_free(str);
-			goto error;
-		}
-
-		o->mode = Addressing_Value;
-
-		i64 indices[3] = {0};
-		AstNode *nodes[3] = {se->low, se->high, se->max};
-		for (isize i = 0; i < gb_count_of(nodes); i++) {
-			i64 index = max_count;
-			if (nodes[i] != NULL) {
-				i64 capacity = -1;
-				if (max_count >= 0)
-					capacity = max_count;
-				i64 j = 0;
-				if (check_index_value(c, nodes[i], capacity, &j)) {
-					index = j;
-				}
-			} else if (i == 0) {
-				index = 0;
-			}
-			indices[i] = index;
-		}
-
-		for (isize i = 0; i < gb_count_of(indices); i++) {
-			i64 a = indices[i];
-			for (isize j = i+1; j < gb_count_of(indices); j++) {
-				i64 b = indices[j];
-				if (a > b && b >= 0) {
-					error(se->close, "Invalid slice indices: [%td > %td]", a, b);
-				}
-			}
-		}
-
-	case_end;
-
-
-	case_ast_node(ce, CallExpr, node);
-		return check_call_expr(c, o, node);
-	case_end;
-
-	case_ast_node(de, DerefExpr, node);
-		check_expr_or_type(c, o, de->expr);
-		if (o->mode == Addressing_Invalid) {
-			goto error;
-		} else {
-			Type *t = base_type(o->type);
-			if (t->kind == Type_Pointer) {
-				o->mode = Addressing_Variable;
-				o->type = t->Pointer.elem;
- 			} else {
- 				gbString str = expr_to_string(o->expr);
- 				error(ast_node_token(o->expr), "Cannot dereference `%s`", str);
- 				gb_string_free(str);
- 				goto error;
- 			}
-		}
-	case_end;
-
-	case_ast_node(de, DemaybeExpr, node);
-		check_expr_or_type(c, o, de->expr);
-		if (o->mode == Addressing_Invalid) {
-			goto error;
-		} else {
-			Type *t = base_type(o->type);
-			if (t->kind == Type_Maybe) {
-				Entity **variables = gb_alloc_array(c->allocator, Entity *, 2);
-				Type *elem = t->Maybe.elem;
-				Token tok = make_token_ident(str_lit(""));
-				variables[0] = make_entity_param(c->allocator, NULL, tok, elem, false);
-				variables[1] = make_entity_param(c->allocator, NULL, tok, t_bool, false);
-
-				Type *tuple = make_type_tuple(c->allocator);
-				tuple->Tuple.variables = variables;
-				tuple->Tuple.variable_count = 2;
-
-				o->type = tuple;
-				o->mode = Addressing_Variable;
- 			} else {
- 				gbString str = expr_to_string(o->expr);
- 				error(ast_node_token(o->expr), "Cannot demaybe `%s`", str);
- 				gb_string_free(str);
- 				goto error;
- 			}
-		}
-	case_end;
-
-	case AstNode_ProcType:
-	case AstNode_PointerType:
-	case AstNode_MaybeType:
-	case AstNode_ArrayType:
-	case AstNode_VectorType:
-	case AstNode_StructType:
-	case AstNode_RawUnionType:
-		o->mode = Addressing_Type;
-		o->type = check_type(c, node);
-		break;
-	}
-
-	kind = Expr_Expr;
-	o->expr = node;
-	return kind;
-
-error:
-	o->mode = Addressing_Invalid;
-	o->expr = node;
-	return kind;
-}
-
-ExprKind check_expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint) {
-	ExprKind kind = check__expr_base(c, o, node, type_hint);
-	Type *type = NULL;
-	ExactValue value = {ExactValue_Invalid};
-	switch (o->mode) {
-	case Addressing_Invalid:
-		type = t_invalid;
-		break;
-	case Addressing_NoValue:
-		type = NULL;
-		break;
-	case Addressing_Constant:
-		type = o->type;
-		value = o->value;
-		break;
-	default:
-		type = o->type;
-		break;
-	}
-
-	if (type != NULL && is_type_untyped(type)) {
-		add_untyped(&c->info, node, false, o->mode, type, value);
-	} else {
-		add_type_and_value(&c->info, node, o->mode, type, value);
-	}
-	return kind;
-}
-
-
-void check_multi_expr(Checker *c, Operand *o, AstNode *e) {
-	gbString err_str = NULL;
-	check_expr_base(c, o, e, NULL);
-	switch (o->mode) {
-	default:
-		return; // NOTE(bill): Valid
-
-	case Addressing_NoValue:
-		err_str = expr_to_string(e);
-		error(ast_node_token(e), "`%s` used as value", err_str);
-		break;
-	case Addressing_Type:
-		err_str = expr_to_string(e);
-		error(ast_node_token(e), "`%s` is not an expression", err_str);
-		break;
-	}
-	gb_string_free(err_str);
-	o->mode = Addressing_Invalid;
-}
-
-void check_not_tuple(Checker *c, Operand *o) {
-	if (o->mode == Addressing_Value) {
-		// NOTE(bill): Tuples are not first class thus never named
-		if (o->type->kind == Type_Tuple) {
-			isize count = o->type->Tuple.variable_count;
-			GB_ASSERT(count != 1);
-			error(ast_node_token(o->expr),
-			      "%td-valued tuple found where single value expected", count);
-			o->mode = Addressing_Invalid;
-		}
-	}
-}
-
-void check_expr(Checker *c, Operand *o, AstNode *e) {
-	check_multi_expr(c, o, e);
-	check_not_tuple(c, o);
-}
-
-
-void check_expr_or_type(Checker *c, Operand *o, AstNode *e) {
-	check_expr_base(c, o, e, NULL);
-	check_not_tuple(c, o);
-	if (o->mode == Addressing_NoValue) {
-		gbString str = expr_to_string(o->expr);
-		error(ast_node_token(o->expr),
-		      "`%s` used as value or type", str);
-		o->mode = Addressing_Invalid;
-		gb_string_free(str);
-	}
-}
-
-
-gbString write_expr_to_string(gbString str, AstNode *node);
-
-gbString write_params_to_string(gbString str, AstNodeArray params, char *sep) {
-	for_array(i, params) {
-		ast_node(p, Parameter, params.e[i]);
-		if (i > 0) {
-			str = gb_string_appendc(str, sep);
-		}
-
-		str = write_expr_to_string(str, params.e[i]);
-	}
-	return str;
-}
-
-gbString string_append_token(gbString str, Token token) {
-	if (token.string.len > 0) {
-		return gb_string_append_length(str, token.string.text, token.string.len);
-	}
-	return str;
-}
-
-
-gbString write_expr_to_string(gbString str, AstNode *node) {
-	if (node == NULL)
-		return str;
-
-	if (is_ast_node_stmt(node)) {
-		GB_ASSERT("stmt passed to write_expr_to_string");
-	}
-
-	switch (node->kind) {
-	default:
-		str = gb_string_appendc(str, "(BadExpr)");
-		break;
-
-	case_ast_node(i, Ident, node);
-		str = string_append_token(str, *i);
-	case_end;
-
-	case_ast_node(bl, BasicLit, node);
-		str = string_append_token(str, *bl);
-	case_end;
-
-	case_ast_node(pl, ProcLit, node);
-		str = write_expr_to_string(str, pl->type);
-	case_end;
-
-	case_ast_node(cl, CompoundLit, node);
-		str = write_expr_to_string(str, cl->type);
-		str = gb_string_appendc(str, "{");
-		for_array(i, cl->elems) {
-			if (i > 0) {
-				str = gb_string_appendc(str, ", ");
-			}
-			str = write_expr_to_string(str, cl->elems.e[i]);
-		}
-		str = gb_string_appendc(str, "}");
-	case_end;
-
-	case_ast_node(te, TagExpr, node);
-		str = gb_string_appendc(str, "#");
-		str = string_append_token(str, te->name);
-		str = write_expr_to_string(str, te->expr);
-	case_end;
-
-	case_ast_node(ue, UnaryExpr, node);
-		str = string_append_token(str, ue->op);
-		str = write_expr_to_string(str, ue->expr);
-	case_end;
-
-	case_ast_node(de, DerefExpr, node);
-		str = write_expr_to_string(str, de->expr);
-		str = gb_string_appendc(str, "^");
-	case_end;
-
-	case_ast_node(de, DemaybeExpr, node);
-		str = write_expr_to_string(str, de->expr);
-		str = gb_string_appendc(str, "?");
-	case_end;
-
-	case_ast_node(be, BinaryExpr, node);
-		str = write_expr_to_string(str, be->left);
-		str = gb_string_appendc(str, " ");
-		str = string_append_token(str, be->op);
-		str = gb_string_appendc(str, " ");
-		str = write_expr_to_string(str, be->right);
-	case_end;
-
-	case_ast_node(pe, ParenExpr, node);
-		str = gb_string_appendc(str, "(");
-		str = write_expr_to_string(str, pe->expr);
-		str = gb_string_appendc(str, ")");
-	case_end;
-
-	case_ast_node(se, SelectorExpr, node);
-		str = write_expr_to_string(str, se->expr);
-		str = gb_string_appendc(str, ".");
-		str = write_expr_to_string(str, se->selector);
-	case_end;
-
-	case_ast_node(ie, IndexExpr, node);
-		str = write_expr_to_string(str, ie->expr);
-		str = gb_string_appendc(str, "[");
-		str = write_expr_to_string(str, ie->index);
-		str = gb_string_appendc(str, "]");
-	case_end;
-
-	case_ast_node(se, SliceExpr, node);
-		str = write_expr_to_string(str, se->expr);
-		str = gb_string_appendc(str, "[");
-		str = write_expr_to_string(str, se->low);
-		str = gb_string_appendc(str, ":");
-		str = write_expr_to_string(str, se->high);
-		if (se->triple_indexed) {
-			str = gb_string_appendc(str, ":");
-			str = write_expr_to_string(str, se->max);
-		}
-		str = gb_string_appendc(str, "]");
-	case_end;
-
-	case_ast_node(e, Ellipsis, node);
-		str = gb_string_appendc(str, "..");
-	case_end;
-
-	case_ast_node(fv, FieldValue, node);
-		str = write_expr_to_string(str, fv->field);
-		str = gb_string_appendc(str, " = ");
-		str = write_expr_to_string(str, fv->value);
-	case_end;
-
-	case_ast_node(pt, PointerType, node);
-		str = gb_string_appendc(str, "^");
-		str = write_expr_to_string(str, pt->type);
-	case_end;
-
-	case_ast_node(mt, MaybeType, node);
-		str = gb_string_appendc(str, "?");
-		str = write_expr_to_string(str, mt->type);
-	case_end;
-
-	case_ast_node(at, ArrayType, node);
-		str = gb_string_appendc(str, "[");
-		str = write_expr_to_string(str, at->count);
-		str = gb_string_appendc(str, "]");
-		str = write_expr_to_string(str, at->elem);
-	case_end;
-
-	case_ast_node(vt, VectorType, node);
-		str = gb_string_appendc(str, "{");
-		str = write_expr_to_string(str, vt->count);
-		str = gb_string_appendc(str, "}");
-		str = write_expr_to_string(str, vt->elem);
-	case_end;
-
-	case_ast_node(p, Parameter, node);
-		if (p->is_using) {
-			str = gb_string_appendc(str, "using ");
-		}
-		for_array(i, p->names) {
-			AstNode *name = p->names.e[i];
-			if (i > 0)
-				str = gb_string_appendc(str, ", ");
-			str = write_expr_to_string(str, name);
-		}
-
-		str = gb_string_appendc(str, ": ");
-		str = write_expr_to_string(str, p->type);
-	case_end;
-
-	case_ast_node(ce, CallExpr, node);
-		str = write_expr_to_string(str, ce->proc);
-		str = gb_string_appendc(str, "(");
-
-		for_array(i, ce->args) {
-			AstNode *arg = ce->args.e[i];
-			if (i > 0) {
-				str = gb_string_appendc(str, ", ");
-			}
-			str = write_expr_to_string(str, arg);
-		}
-		str = gb_string_appendc(str, ")");
-	case_end;
-
-	case_ast_node(pt, ProcType, node);
-		str = gb_string_appendc(str, "proc(");
-		str = write_params_to_string(str, pt->params, ", ");
-		str = gb_string_appendc(str, ")");
-	case_end;
-
-	case_ast_node(st, StructType, node);
-		str = gb_string_appendc(str, "struct ");
-		if (st->is_packed)  str = gb_string_appendc(str, "#packed ");
-		if (st->is_ordered) str = gb_string_appendc(str, "#ordered ");
-		for_array(i, st->decls) {
-			if (i > 0) {
-				str = gb_string_appendc(str, "; ");
-			}
-			str = write_expr_to_string(str, st->decls.e[i]);
-		}
-		// str = write_params_to_string(str, st->decl_list, ", ");
-		str = gb_string_appendc(str, "}");
-	case_end;
-
-	case_ast_node(st, RawUnionType, node);
-		str = gb_string_appendc(str, "raw_union {");
-		for_array(i, st->decls) {
-			if (i > 0) {
-				str = gb_string_appendc(str, "; ");
-			}
-			str = write_expr_to_string(str, st->decls.e[i]);
-		}
-		// str = write_params_to_string(str, st->decl_list, ", ");
-		str = gb_string_appendc(str, "}");
-	case_end;
-
-	case_ast_node(st, UnionType, node);
-		str = gb_string_appendc(str, "union {");
-		for_array(i, st->decls) {
-			if (i > 0) {
-				str = gb_string_appendc(str, "; ");
-			}
-			str = write_expr_to_string(str, st->decls.e[i]);
-		}
-		// str = write_params_to_string(str, st->decl_list, ", ");
-		str = gb_string_appendc(str, "}");
-	case_end;
-
-	case_ast_node(et, EnumType, node);
-		str = gb_string_appendc(str, "enum ");
-		if (et->base_type != NULL) {
-			str = write_expr_to_string(str, et->base_type);
-			str = gb_string_appendc(str, " ");
-		}
-		str = gb_string_appendc(str, "{");
-		str = gb_string_appendc(str, "}");
-	case_end;
-	}
-
-	return str;
-}
-
-gbString expr_to_string(AstNode *expression) {
-	return write_expr_to_string(gb_string_make(heap_allocator(), ""), expression);
-}

+ 0 - 1130
src/checker/stmt.cpp

@@ -1,1130 +0,0 @@
-bool check_is_terminating(AstNode *node);
-bool check_has_break     (AstNode *stmt, bool implicit);
-void check_stmt          (Checker *c, AstNode *node, u32 flags);
-
-
-// Statements and Declarations
-typedef enum StmtFlag {
-	Stmt_BreakAllowed       = GB_BIT(0),
-	Stmt_ContinueAllowed    = GB_BIT(1),
-	Stmt_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough
-} StmtFlag;
-
-
-
-void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
-	if (stmts.count == 0) {
-		return;
-	}
-
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-
-	typedef struct {
-		Entity *e;
-		DeclInfo *d;
-	} Delay;
-	Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, stmts.count);
-	Array(Delay) delayed_type;  array_init_reserve(&delayed_type,  c->tmp_allocator, stmts.count);
-
-	for_array(i, stmts) {
-		AstNode *node = stmts.e[i];
-		switch (node->kind) {
-		case_ast_node(cd, ConstDecl, node);
-			for_array(i, cd->values) {
-				AstNode *name = cd->names.e[i];
-				AstNode *value = cd->values.e[i];
-				ExactValue v = {ExactValue_Invalid};
-
-				Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
-				e->identifier = name;
-
-				DeclInfo *d = make_declaration_info(c->allocator, e->scope);
-				d->type_expr = cd->type;
-				d->init_expr = value;
-
-				add_entity_and_decl_info(c, name, e, d);
-
-				Delay delay = {e, d};
-				array_add(&delayed_const, delay);
-			}
-
-			isize lhs_count = cd->names.count;
-			isize rhs_count = cd->values.count;
-
-			if (rhs_count == 0 && cd->type == NULL) {
-				error(ast_node_token(node), "Missing type or initial expression");
-			} else if (lhs_count < rhs_count) {
-				error(ast_node_token(node), "Extra initial expression");
-			}
-		case_end;
-
-		case_ast_node(td, TypeDecl, node);
-			Entity *e = make_entity_type_name(c->allocator, c->context.scope, td->name->Ident, NULL);
-			e->identifier = td->name;
-
-			DeclInfo *d = make_declaration_info(c->allocator, e->scope);
-			d->type_expr = td->type;
-
-			add_entity_and_decl_info(c, td->name, e, d);
-
-			Delay delay = {e, d};
-			array_add(&delayed_type, delay);
-		case_end;
-		}
-	}
-
-	for_array(i, delayed_type) {
-		check_entity_decl(c, delayed_type.e[i].e, delayed_type.e[i].d, NULL, NULL);
-	}
-	for_array(i, delayed_const) {
-		check_entity_decl(c, delayed_const.e[i].e, delayed_const.e[i].d, NULL, NULL);
-	}
-
-	bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
-	u32 f = flags & (~Stmt_FallthroughAllowed);
-
-	for_array(i, stmts) {
-		AstNode *n = stmts.e[i];
-		if (n->kind == AstNode_EmptyStmt) {
-			continue;
-		}
-		u32 new_flags = f;
-		if (ft_ok && i+1 == stmts.count) {
-			new_flags |= Stmt_FallthroughAllowed;
-		}
-		check_stmt(c, n, new_flags);
-	}
-
-	gb_temp_arena_memory_end(tmp);
-}
-
-bool check_is_terminating_list(AstNodeArray stmts) {
-
-	// Iterate backwards
-	for (isize n = stmts.count-1; n >= 0; n--) {
-		AstNode *stmt = stmts.e[n];
-		if (stmt->kind != AstNode_EmptyStmt) {
-			return check_is_terminating(stmt);
-		}
-	}
-
-	return false;
-}
-
-bool check_has_break_list(AstNodeArray stmts, bool implicit) {
-	for_array(i, stmts) {
-		AstNode *stmt = stmts.e[i];
-		if (check_has_break(stmt, implicit)) {
-			return true;
-		}
-	}
-	return false;
-}
-
-
-bool check_has_break(AstNode *stmt, bool 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.stmts, 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
-bool 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(bs->stmts);
-	case_end;
-
-	case_ast_node(es, ExprStmt, node);
-		return check_is_terminating(es->expr);
-	case_end;
-
-	case_ast_node(is, IfStmt, node);
-		if (is->else_stmt != NULL) {
-			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 && !check_has_break(fs->body, true)) {
-			return true;
-		}
-	case_end;
-
-	case_ast_node(ms, MatchStmt, node);
-		bool has_default = false;
-		for_array(i, ms->body->BlockStmt.stmts) {
-			AstNode *clause = ms->body->BlockStmt.stmts.e[i];
-			ast_node(cc, CaseClause, clause);
-			if (cc->list.count == 0) {
-				has_default = true;
-			}
-			if (!check_is_terminating_list(cc->stmts) ||
-			    check_has_break_list(cc->stmts, true)) {
-				return false;
-			}
-		}
-		return has_default;
-	case_end;
-
-	case_ast_node(ms, TypeMatchStmt, node);
-		bool has_default = false;
-		for_array(i, ms->body->BlockStmt.stmts) {
-			AstNode *clause = ms->body->BlockStmt.stmts.e[i];
-			ast_node(cc, CaseClause, clause);
-			if (cc->list.count == 0) {
-				has_default = true;
-			}
-			if (!check_is_terminating_list(cc->stmts) ||
-			    check_has_break_list(cc->stmts, true)) {
-				return false;
-			}
-		}
-		return has_default;
-	case_end;
-
-	case_ast_node(pa, PushAllocator, node);
-		return check_is_terminating(pa->body);
-	case_end;
-	case_ast_node(pc, PushContext, node);
-		return check_is_terminating(pc->body);
-	case_end;
-	}
-
-	return false;
-}
-
-Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
-	if (op_a->mode == Addressing_Invalid ||
-	    op_a->type == t_invalid) {
-		return NULL;
-	}
-
-	AstNode *node = unparen_expr(lhs);
-
-	// NOTE(bill): Ignore assignments to `_`
-	if (node->kind == AstNode_Ident &&
-	    str_eq(node->Ident.string, str_lit("_"))) {
-		add_entity_definition(&c->info, node, NULL);
-		check_assignment(c, op_a, NULL, str_lit("assignment to `_` identifier"));
-		if (op_a->mode == Addressing_Invalid)
-			return NULL;
-		return op_a->type;
-	}
-
-	Entity *e = NULL;
-	bool used = false;
-	if (node->kind == AstNode_Ident) {
-		ast_node(i, Ident, node);
-		e = scope_lookup_entity(c->context.scope, i->string);
-		if (e != NULL && e->kind == Entity_Variable) {
-			used = (e->flags & EntityFlag_Used) != 0; // TODO(bill): Make backup just in case
-		}
-	}
-
-
-	Operand op_b = {Addressing_Invalid};
-	check_expr(c, &op_b, lhs);
-	if (e) {
-		e->flags |= EntityFlag_Used*used;
-	}
-
-	if (op_b.mode == Addressing_Invalid ||
-	    op_b.type == t_invalid) {
-		return NULL;
-	}
-
-	switch (op_b.mode) {
-	case Addressing_Invalid:
-		return NULL;
-	case Addressing_Variable:
-		break;
-	default: {
-		if (op_b.expr->kind == AstNode_SelectorExpr) {
-			// NOTE(bill): Extra error checks
-			Operand op_c = {Addressing_Invalid};
-			ast_node(se, SelectorExpr, op_b.expr);
-			check_expr(c, &op_c, se->expr);
-		}
-
-		gbString str = expr_to_string(op_b.expr);
-		switch (op_b.mode) {
-		case Addressing_Value:
-			error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
-			break;
-		default:
-			error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
-			break;
-		}
-		gb_string_free(str);
-	} break;
-	}
-
-	check_assignment(c, op_a, op_b.type, str_lit("assignment"));
-	if (op_a->mode == Addressing_Invalid) {
-		return NULL;
-	}
-
-	return op_a->type;
-}
-
-bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) {
-	if (is_type_pointer(type)) {
-		*is_union_ptr = is_type_union(type_deref(type));
-		return *is_union_ptr;
-	}
-	if (is_type_any(type)) {
-		*is_any = true;
-		return *is_any;
-	}
-	return false;
-}
-
-void check_stmt_internal(Checker *c, AstNode *node, u32 flags);
-void check_stmt(Checker *c, AstNode *node, u32 flags) {
-	u32 prev_stmt_state_flags = c->context.stmt_state_flags;
-
-	if (node->stmt_state_flags != 0) {
-		u32 in = node->stmt_state_flags;
-		u32 out = c->context.stmt_state_flags;
-
-		if (in & StmtStateFlag_bounds_check) {
-			out |= StmtStateFlag_bounds_check;
-			out &= ~StmtStateFlag_no_bounds_check;
-		} else if (in & StmtStateFlag_no_bounds_check) {
-			out |= StmtStateFlag_no_bounds_check;
-			out &= ~StmtStateFlag_bounds_check;
-		}
-
-		c->context.stmt_state_flags = out;
-	}
-
-	check_stmt_internal(c, node, flags);
-
-	c->context.stmt_state_flags = prev_stmt_state_flags;
-}
-
-typedef struct TypeAndToken {
-	Type *type;
-	Token token;
-} TypeAndToken;
-
-#define MAP_TYPE TypeAndToken
-#define MAP_FUNC map_type_and_token_
-#define MAP_NAME MapTypeAndToken
-#include "../map.c"
-
-void check_stmt_internal(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;
-	case_ast_node(_, BadDecl,   node); case_end;
-
-	case_ast_node(es, ExprStmt, node)
-		Operand operand = {Addressing_Invalid};
-		ExprKind kind = check_expr_base(c, &operand, es->expr, NULL);
-		switch (operand.mode) {
-		case Addressing_Type:
-			error(ast_node_token(node), "Is not an expression");
-			break;
-		case Addressing_NoValue:
-			return;
-		default: {
-			if (kind == Expr_Stmt) {
-				return;
-			}
-			if (operand.expr->kind == AstNode_CallExpr) {
-				return;
-			}
-			gbString expr_str = expr_to_string(operand.expr);
-			error(ast_node_token(node), "Expression is not used: `%s`", expr_str);
-			gb_string_free(expr_str);
-		} break;
-		}
-	case_end;
-
-	case_ast_node(ts, TagStmt, node);
-		// TODO(bill): Tag Statements
-		error(ast_node_token(node), "Tag statements are not supported yet");
-		check_stmt(c, ts->stmt, flags);
-	case_end;
-
-	case_ast_node(ids, IncDecStmt, node);
-		Token op = ids->op;
-		switch (ids->op.kind) {
-		case Token_Increment:
-			op.kind = Token_Add;
-			op.string.len = 1;
-			break;
-		case Token_Decrement:
-			op.kind = Token_Sub;
-			op.string.len = 1;
-			break;
-		default:
-			error(ids->op, "Unknown inc/dec operation %.*s", LIT(ids->op.string));
-			return;
-		}
-
-		Operand operand = {Addressing_Invalid};
-		check_expr(c, &operand, ids->expr);
-		if (operand.mode == Addressing_Invalid)
-			return;
-		if (!is_type_numeric(operand.type)) {
-			error(ids->op, "Non numeric type");
-			return;
-		}
-
-		AstNode basic_lit = {AstNode_BasicLit};
-		ast_node(bl, BasicLit, &basic_lit);
-		*bl = ids->op;
-		bl->kind = Token_Integer;
-		bl->string = str_lit("1");
-
-		AstNode binary_expr = {AstNode_BinaryExpr};
-		ast_node(be, BinaryExpr, &binary_expr);
-		be->op = op;
-		be->left = ids->expr;
-		be->right = &basic_lit;
-		check_binary_expr(c, &operand, &binary_expr);
-	case_end;
-
-	case_ast_node(as, AssignStmt, node);
-		switch (as->op.kind) {
-		case Token_Eq: {
-			// a, b, c = 1, 2, 3;  // Multisided
-			if (as->lhs.count == 0) {
-				error(as->op, "Missing lhs in assignment statement");
-				return;
-			}
-
-			gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-
-			// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
-			// an extra allocation
-			Array(Operand) operands;
-			array_init_reserve(&operands, c->tmp_allocator, 2 * as->lhs.count);
-
-			for_array(i, as->rhs) {
-				AstNode *rhs = as->rhs.e[i];
-				Operand o = {0};
-				check_multi_expr(c, &o, rhs);
-				if (o.type->kind != Type_Tuple) {
-					array_add(&operands, o);
-				} else {
-					TypeTuple *tuple = &o.type->Tuple;
-					for (isize j = 0; j < tuple->variable_count; j++) {
-						o.type = tuple->variables[j]->type;
-						array_add(&operands, o);
-					}
-				}
-			}
-
-			isize lhs_count = as->lhs.count;
-			isize rhs_count = operands.count;
-
-			isize operand_count = gb_min(as->lhs.count, operands.count);
-			for (isize i = 0; i < operand_count; i++) {
-				AstNode *lhs = as->lhs.e[i];
-				check_assignment_variable(c, &operands.e[i], lhs);
-			}
-			if (lhs_count != rhs_count) {
-				error(ast_node_token(as->lhs.e[0]), "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
-			}
-
-			gb_temp_arena_memory_end(tmp);
-		} break;
-
-		default: {
-			// a += 1; // Single-sided
-			Token op = as->op;
-			if (as->lhs.count != 1 || as->rhs.count != 1) {
-				error(op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string));
-				return;
-			}
-			if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) {
-				error(op, "Unknown Assignment operation `%.*s`", LIT(op.string));
-				return;
-			}
-			// TODO(bill): Check if valid assignment operator
-			Operand operand = {Addressing_Invalid};
-			AstNode binary_expr = {AstNode_BinaryExpr};
-			ast_node(be, BinaryExpr, &binary_expr);
-			be->op = op;
-			be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add));
-			 // NOTE(bill): Only use the first one will be used
-			be->left  = as->lhs.e[0];
-			be->right = as->rhs.e[0];
-
-			check_binary_expr(c, &operand, &binary_expr);
-			if (operand.mode == Addressing_Invalid) {
-				return;
-			}
-			// NOTE(bill): Only use the first one will be used
-			check_assignment_variable(c, &operand, as->lhs.e[0]);
-		} break;
-		}
-	case_end;
-
-	case_ast_node(bs, BlockStmt, node);
-		check_open_scope(c, node);
-		check_stmt_list(c, bs->stmts, mod_flags);
-		check_close_scope(c);
-	case_end;
-
-	case_ast_node(is, IfStmt, node);
-		check_open_scope(c, node);
-
-		if (is->init != NULL) {
-			check_stmt(c, is->init, 0);
-		}
-
-		Operand operand = {Addressing_Invalid};
-		check_expr(c, &operand, is->cond);
-		if (operand.mode != Addressing_Invalid &&
-		    !is_type_boolean(operand.type)) {
-			error(ast_node_token(is->cond),
-			            "Non-boolean condition in `if` statement");
-		}
-
-		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, mod_flags);
-				break;
-			default:
-				error(ast_node_token(is->else_stmt),
-				            "Invalid `else` statement in `if` statement");
-				break;
-			}
-		}
-
-		check_close_scope(c);
-	case_end;
-
-	case_ast_node(rs, ReturnStmt, node);
-		GB_ASSERT(c->proc_stack.count > 0);
-
-		if (c->in_defer) {
-			error(rs->token, "You cannot `return` within a defer statement");
-			// TODO(bill): Should I break here?
-			break;
-		}
-
-
-		Type *proc_type = c->proc_stack.e[c->proc_stack.count-1];
-		isize result_count = 0;
-		if (proc_type->Proc.results) {
-			result_count = proc_type->Proc.results->Tuple.variable_count;
-		}
-
-		if (result_count > 0) {
-			Entity **variables = NULL;
-			if (proc_type->Proc.results != NULL) {
-				TypeTuple *tuple = &proc_type->Proc.results->Tuple;
-				variables = tuple->variables;
-			}
-			if (rs->results.count == 0) {
-				error(ast_node_token(node), "Expected %td return values, got 0", result_count);
-			} else {
-				check_init_variables(c, variables, result_count,
-				                     rs->results, str_lit("return statement"));
-			}
-		} else if (rs->results.count > 0) {
-			error(ast_node_token(rs->results.e[0]), "No return values expected");
-		}
-	case_end;
-
-	case_ast_node(fs, ForStmt, node);
-		u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
-		check_open_scope(c, node);
-
-		if (fs->init != NULL) {
-			check_stmt(c, fs->init, 0);
-		}
-		if (fs->cond) {
-			Operand operand = {Addressing_Invalid};
-			check_expr(c, &operand, fs->cond);
-			if (operand.mode != Addressing_Invalid &&
-			    !is_type_boolean(operand.type)) {
-				error(ast_node_token(fs->cond),
-				      "Non-boolean condition in `for` statement");
-			}
-		}
-		if (fs->post != NULL) {
-			check_stmt(c, fs->post, 0);
-		}
-		check_stmt(c, fs->body, new_flags);
-
-		check_close_scope(c);
-	case_end;
-
-	case_ast_node(ms, MatchStmt, node);
-		Operand x = {0};
-
-		mod_flags |= Stmt_BreakAllowed;
-		check_open_scope(c, node);
-
-		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, str_lit("match expression"));
-		} else {
-			x.mode  = Addressing_Constant;
-			x.type  = t_bool;
-			x.value = make_exact_value_bool(true);
-
-			Token token = {0};
-			token.pos = ast_node_token(ms->body).pos;
-			token.string = str_lit("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_array(i, bs->stmts) {
-			AstNode *stmt = bs->stmts.e[i];
-			AstNode *default_stmt = NULL;
-			if (stmt->kind == AstNode_CaseClause) {
-				ast_node(cc, CaseClause, stmt);
-				if (cc->list.count == 0) {
-					default_stmt = stmt;
-				}
-			} else {
-				error(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(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;
-				}
-			}
-		}
-;
-
-		MapTypeAndToken seen = {0}; // NOTE(bill): Multimap
-		map_type_and_token_init(&seen, heap_allocator());
-
-		for_array(i, bs->stmts) {
-			AstNode *stmt = bs->stmts.e[i];
-			if (stmt->kind != AstNode_CaseClause) {
-				// NOTE(bill): error handled by above multiple default checker
-				continue;
-			}
-			ast_node(cc, CaseClause, stmt);
-
-
-			for_array(j, cc->list) {
-				AstNode *expr = cc->list.e[j];
-				Operand y = {0};
-				Operand z = {0};
-				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, 0);
-				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);
-					TypeAndToken *found = map_type_and_token_get(&seen, key);
-					if (found != NULL) {
-						gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-						isize count = map_type_and_token_multi_count(&seen, key);
-						TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count);
-
-						map_type_and_token_multi_get_all(&seen, key, taps);
-						bool 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(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;
-							}
-						}
-
-						gb_temp_arena_memory_end(tmp);
-
-						if (continue_outer) {
-							continue;
-						}
-					}
-					TypeAndToken tap = {y.type, ast_node_token(y.expr)};
-					map_type_and_token_multi_insert(&seen, key, tap);
-				}
-			}
-
-			check_open_scope(c, stmt);
-			u32 ft_flags = mod_flags;
-			if (i+1 < bs->stmts.count) {
-				ft_flags |= Stmt_FallthroughAllowed;
-			}
-			check_stmt_list(c, cc->stmts, ft_flags);
-			check_close_scope(c);
-		}
-
-		map_type_and_token_destroy(&seen);
-
-		check_close_scope(c);
-	case_end;
-
-	case_ast_node(ms, TypeMatchStmt, node);
-		Operand x = {0};
-
-		mod_flags |= Stmt_BreakAllowed;
-		check_open_scope(c, node);
-
-		bool is_union_ptr = false;
-		bool is_any = false;
-
-		check_expr(c, &x, ms->tag);
-		check_assignment(c, &x, NULL, str_lit("type match expression"));
-		if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) {
-			gbString str = type_to_string(x.type);
-			error(ast_node_token(x.expr),
-			      "Invalid type for this type match expression, got `%s`", str);
-			gb_string_free(str);
-			break;
-		}
-
-
-		// NOTE(bill): Check for multiple defaults
-		AstNode *first_default = NULL;
-		ast_node(bs, BlockStmt, ms->body);
-		for_array(i, bs->stmts) {
-			AstNode *stmt = bs->stmts.e[i];
-			AstNode *default_stmt = NULL;
-			if (stmt->kind == AstNode_CaseClause) {
-				ast_node(cc, CaseClause, stmt);
-				if (cc->list.count == 0) {
-					default_stmt = stmt;
-				}
-			} else {
-				error(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(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;
-				}
-			}
-		}
-
-		if (ms->var->kind != AstNode_Ident) {
-			break;
-		}
-
-
-		MapBool seen = {0};
-		map_bool_init(&seen, heap_allocator());
-
-		for_array(i, bs->stmts) {
-			AstNode *stmt = bs->stmts.e[i];
-			if (stmt->kind != AstNode_CaseClause) {
-				// NOTE(bill): error handled by above multiple default checker
-				continue;
-			}
-			ast_node(cc, CaseClause, stmt);
-
-			// TODO(bill): Make robust
-			Type *bt = base_type(type_deref(x.type));
-
-
-			AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL;
-			Type *case_type = NULL;
-			if (type_expr != NULL) { // Otherwise it's a default expression
-				Operand y = {0};
-				check_expr_or_type(c, &y, type_expr);
-
-				if (is_union_ptr) {
-					GB_ASSERT(is_type_union(bt));
-					bool tag_type_found = false;
-					for (isize i = 0; i < bt->Record.field_count; i++) {
-						Entity *f = bt->Record.fields[i];
-						if (are_types_identical(f->type, y.type)) {
-							tag_type_found = true;
-							break;
-						}
-					}
-					if (!tag_type_found) {
-						gbString type_str = type_to_string(y.type);
-						error(ast_node_token(y.expr),
-						      "Unknown tag type, got `%s`", type_str);
-						gb_string_free(type_str);
-						continue;
-					}
-					case_type = y.type;
-				} else if (is_any) {
-					case_type = y.type;
-				} else {
-					GB_PANIC("Unknown type to type match statement");
-				}
-
-				HashKey key = hash_pointer(y.type);
-				bool *found = map_bool_get(&seen, key);
-				if (found) {
-					TokenPos pos = cc->token.pos;
-					gbString expr_str = expr_to_string(y.expr);
-					error(ast_node_token(y.expr),
-					      "Duplicate type case `%s`\n"
-					      "\tprevious type case at %.*s(%td:%td)",
-					      expr_str,
-					      LIT(pos.file), pos.line, pos.column);
-					gb_string_free(expr_str);
-					break;
-				}
-				map_bool_set(&seen, key, cast(bool)true);
-			}
-
-			check_open_scope(c, stmt);
-			if (case_type != NULL) {
-				add_type_info_type(c, case_type);
-
-				// NOTE(bill): Dummy type
-				Type *tt = case_type;
-				if (is_union_ptr) {
-					tt = make_type_pointer(c->allocator, case_type);
-					add_type_info_type(c, tt);
-				}
-				Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tt);
-				tag_var->flags |= EntityFlag_Used;
-				add_entity(c, c->context.scope, ms->var, tag_var);
-				add_entity_use(c, ms->var, tag_var);
-			}
-			check_stmt_list(c, cc->stmts, mod_flags);
-			check_close_scope(c);
-		}
-		map_bool_destroy(&seen);
-
-		check_close_scope(c);
-	case_end;
-
-
-	case_ast_node(ds, DeferStmt, node);
-		if (is_ast_node_decl(ds->stmt)) {
-			error(ds->token, "You cannot defer a declaration");
-		} else {
-			bool out_in_defer = c->in_defer;
-			c->in_defer = true;
-			check_stmt(c, ds->stmt, 0);
-			c->in_defer = out_in_defer;
-		}
-	case_end;
-
-	case_ast_node(bs, BranchStmt, node);
-		Token token = bs->token;
-		switch (token.kind) {
-		case Token_break:
-			if ((flags & Stmt_BreakAllowed) == 0) {
-				error(token, "`break` only allowed in `for` or `match` statements");
-			}
-			break;
-		case Token_continue:
-			if ((flags & Stmt_ContinueAllowed) == 0) {
-				error(token, "`continue` only allowed in `for` statements");
-			}
-			break;
-		case Token_fallthrough:
-			if ((flags & Stmt_FallthroughAllowed) == 0) {
-				error(token, "`fallthrough` statement in illegal position");
-			}
-			break;
-		default:
-			error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
-			break;
-		}
-	case_end;
-
-	case_ast_node(us, UsingStmt, node);
-		switch (us->node->kind) {
-		case_ast_node(es, ExprStmt, us->node);
-			// TODO(bill): Allow for just a LHS expression list rather than this silly code
-			Entity *e = NULL;
-
-			bool is_selector = false;
-			AstNode *expr = unparen_expr(es->expr);
-			if (expr->kind == AstNode_Ident) {
-				String name = expr->Ident.string;
-				e = scope_lookup_entity(c->context.scope, name);
-			} else if (expr->kind == AstNode_SelectorExpr) {
-				Operand o = {0};
-				e = check_selector(c, &o, expr);
-				is_selector = true;
-			}
-
-			if (e == NULL) {
-				error(us->token, "`using` applied to an unknown entity");
-				return;
-			}
-
-			switch (e->kind) {
-			case Entity_TypeName: {
-				Type *t = base_type(e->type);
-				if (is_type_struct(t) || 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) {
-							gbString expr_str = expr_to_string(expr);
-							error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
-							gb_string_free(expr_str);
-							return;
-						}
-						f->using_parent = e;
-					}
-				} else if (is_type_union(t)) {
-					for (isize i = 0; i < t->Record.field_count; i++) {
-						Entity *f = t->Record.fields[i];
-						Entity *found = scope_insert_entity(c->context.scope, f);
-						if (found != NULL) {
-							gbString expr_str = expr_to_string(expr);
-							error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
-							gb_string_free(expr_str);
-							return;
-						}
-						f->using_parent = e;
-					}
-					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) {
-							gbString expr_str = expr_to_string(expr);
-							error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
-							gb_string_free(expr_str);
-							return;
-						}
-						f->using_parent = e;
-					}
-				}
-			} break;
-
-			case Entity_ImportName: {
-				Scope *scope = e->ImportName.scope;
-				for_array(i, scope->elements.entries) {
-					Entity *decl = scope->elements.entries.e[i].value;
-					Entity *found = scope_insert_entity(c->context.scope, decl);
-					if (found != NULL) {
-						gbString expr_str = expr_to_string(expr);
-						error(us->token,
-						      "Namespace collision while `using` `%s` of: %.*s\n"
-						      "\tat %.*s(%td:%td)\n"
-						      "\tat %.*s(%td:%td)",
-						      expr_str, LIT(found->token.string),
-						      LIT(found->token.pos.file), found->token.pos.line, found->token.pos.column,
-						      LIT(decl->token.pos.file), decl->token.pos.line, decl->token.pos.column
-						      );
-						gb_string_free(expr_str);
-						return;
-					}
-				}
-			} break;
-
-			case Entity_Variable: {
-				Type *t = base_type(type_deref(e->type));
-				if (is_type_struct(t) || is_type_raw_union(t)) {
-					Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
-					GB_ASSERT(found != NULL);
-					for_array(i, (*found)->elements.entries) {
-						Entity *f = (*found)->elements.entries.e[i].value;
-						if (f->kind == Entity_Variable) {
-							Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
-							if (is_selector) {
-								uvar->using_expr = expr;
-							}
-							Entity *prev = scope_insert_entity(c->context.scope, uvar);
-							if (prev != NULL) {
-								gbString expr_str = expr_to_string(expr);
-								error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
-								gb_string_free(expr_str);
-								return;
-							}
-						}
-					}
-				} else {
-					error(us->token, "`using` can only be applied to variables of type struct or raw_union");
-					return;
-				}
-			} break;
-
-			case Entity_Constant:
-				error(us->token, "`using` cannot be applied to a constant");
-				break;
-
-			case Entity_Procedure:
-			case Entity_Builtin:
-				error(us->token, "`using` cannot be applied to a procedure");
-				break;
-
-			case Entity_ImplicitValue:
-				error(us->token, "`using` cannot be applied to an implicit value");
-				break;
-
-			case Entity_Nil:
-				error(us->token, "`using` cannot be applied to `nil`");
-				break;
-
-			case Entity_Invalid:
-				error(us->token, "`using` cannot be applied to an invalid entity");
-				break;
-
-			default:
-				GB_PANIC("TODO(bill): `using` other expressions?");
-			}
-		case_end;
-
-		case_ast_node(vd, VarDecl, us->node);
-			if (vd->names.count > 1 && vd->type != NULL) {
-				error(us->token, "`using` can only be applied to one variable of the same type");
-			}
-			check_var_decl_node(c, us->node);
-
-			for_array(name_index, vd->names) {
-				AstNode *item = vd->names.e[name_index];
-				ast_node(i, Ident, item);
-				String name = i->string;
-				Entity *e = scope_lookup_entity(c->context.scope, name);
-				Type *t = base_type(type_deref(e->type));
-				if (is_type_struct(t) || is_type_raw_union(t)) {
-					Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
-					GB_ASSERT(found != NULL);
-					for_array(i, (*found)->elements.entries) {
-						Entity *f = (*found)->elements.entries.e[i].value;
-						if (f->kind == Entity_Variable) {
-							Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
-							Entity *prev = scope_insert_entity(c->context.scope, uvar);
-							if (prev != NULL) {
-								error(us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
-								return;
-							}
-						}
-					}
-				} else {
-					error(us->token, "`using` can only be applied to variables of type struct or raw_union");
-					return;
-				}
-			}
-		case_end;
-
-
-		default:
-			error(us->token, "Invalid AST: Using Statement");
-			break;
-		}
-	case_end;
-
-
-
-	case_ast_node(pa, PushAllocator, node);
-		Operand op = {0};
-		check_expr(c, &op, pa->expr);
-		check_assignment(c, &op, t_allocator, str_lit("argument to push_allocator"));
-		check_stmt(c, pa->body, mod_flags);
-	case_end;
-
-
-	case_ast_node(pa, PushContext, node);
-		Operand op = {0};
-		check_expr(c, &op, pa->expr);
-		check_assignment(c, &op, t_context, str_lit("argument to push_context"));
-		check_stmt(c, pa->body, mod_flags);
-	case_end;
-
-
-
-
-
-
-	case_ast_node(vd, VarDecl, node);
-		check_var_decl_node(c, node);
-	case_end;
-
-	case_ast_node(cd, ConstDecl, node);
-		// NOTE(bill): Handled elsewhere
-	case_end;
-
-	case_ast_node(td, TypeDecl, node);
-		// NOTE(bill): Handled elsewhere
-	case_end;
-
-	case_ast_node(pd, ProcDecl, node);
-		// NOTE(bill): This must be handled here so it has access to the parent scope stuff
-		// e.g. using
-		Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL);
-		e->identifier = pd->name;
-
-		DeclInfo *d = make_declaration_info(c->allocator, e->scope);
-		d->proc_decl = node;
-
-		add_entity_and_decl_info(c, pd->name, e, d);
-		check_entity_decl(c, e, d, NULL, NULL);
-	case_end;
-	}
-}

+ 0 - 1487
src/checker/types.cpp

@@ -1,1487 +0,0 @@
-typedef struct Scope Scope;
-
-typedef enum BasicKind {
-	Basic_Invalid,
-	Basic_bool,
-	Basic_i8,
-	Basic_u8,
-	Basic_i16,
-	Basic_u16,
-	Basic_i32,
-	Basic_u32,
-	Basic_i64,
-	Basic_u64,
-	Basic_i128,
-	Basic_u128,
-	// Basic_f16,
-	Basic_f32,
-	Basic_f64,
-	// Basic_f128,
-	Basic_int,
-	Basic_uint,
-	Basic_rawptr,
-	Basic_string, // ^u8 + int
-	Basic_any,    // ^Type_Info + rawptr
-
-	Basic_UntypedBool,
-	Basic_UntypedInteger,
-	Basic_UntypedFloat,
-	Basic_UntypedString,
-	Basic_UntypedRune,
-	Basic_UntypedNil,
-
-	Basic_Count,
-
-	Basic_byte = Basic_u8,
-	Basic_rune = Basic_i32,
-} BasicKind;
-
-typedef enum BasicFlag {
-	BasicFlag_Boolean  = GB_BIT(0),
-	BasicFlag_Integer  = GB_BIT(1),
-	BasicFlag_Unsigned = GB_BIT(2),
-	BasicFlag_Float    = GB_BIT(3),
-	BasicFlag_Pointer  = GB_BIT(4),
-	BasicFlag_String   = GB_BIT(5),
-	BasicFlag_Rune     = GB_BIT(6),
-	BasicFlag_Untyped  = GB_BIT(7),
-
-	BasicFlag_Numeric      = BasicFlag_Integer | BasicFlag_Float,
-	BasicFlag_Ordered      = BasicFlag_Numeric | BasicFlag_String  | BasicFlag_Pointer,
-	BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_Pointer | BasicFlag_String | BasicFlag_Rune,
-} BasicFlag;
-
-typedef struct BasicType {
-	BasicKind kind;
-	u32       flags;
-	i64       size; // -1 if arch. dep.
-	String    name;
-} BasicType;
-
-typedef enum TypeRecordKind {
-	TypeRecord_Invalid,
-
-	TypeRecord_Struct,
-	TypeRecord_Enum,
-	TypeRecord_RawUnion,
-	TypeRecord_Union, // Tagged
-
-	TypeRecord_Count,
-} TypeRecordKind;
-
-typedef struct TypeRecord {
-	TypeRecordKind kind;
-
-	// All record types
-	// Theses are arrays
-	Entity **fields;      // Entity_Variable (otherwise Entity_TypeName if union)
-	i32      field_count; // == offset_count is struct
-	AstNode *node;
-
-	union { // NOTE(bill): Reduce size_of Type
-		struct { // enum only
-			Type *   enum_base; // Default is `int`
-			Entity * enum_count;
-			Entity * min_value;
-			Entity * max_value;
-		};
-		struct { // struct only
-			i64 *    struct_offsets;
-			bool      struct_are_offsets_set;
-			bool      struct_is_packed;
-			bool      struct_is_ordered;
-			Entity **fields_in_src_order; // Entity_Variable
-		};
-	};
-
-	// Entity_Constant or Entity_TypeName
-	Entity **other_fields;
-	i32      other_field_count;
-} TypeRecord;
-
-#define TYPE_KINDS \
-	TYPE_KIND(Basic,   BasicType) \
-	TYPE_KIND(Pointer, struct { Type *elem; }) \
-	TYPE_KIND(Array,   struct { Type *elem; i64 count; }) \
-	TYPE_KIND(Vector,  struct { Type *elem; i64 count; }) \
-	TYPE_KIND(Slice,   struct { Type *elem; }) \
-	TYPE_KIND(Maybe,   struct { Type *elem; }) \
-	TYPE_KIND(Record,  TypeRecord) \
-	TYPE_KIND(Named, struct { \
-		String  name; \
-		Type *  base; \
-		Entity *type_name; /* Entity_TypeName */ \
-	}) \
-	TYPE_KIND(Tuple, struct { \
-		Entity **variables; /* Entity_Variable */ \
-		i32      variable_count; \
-		bool     are_offsets_set; \
-		i64 *    offsets; \
-	}) \
-	TYPE_KIND(Proc, struct { \
-		Scope *scope; \
-		Type * params;  /* Type_Tuple */ \
-		Type * results; /* Type_Tuple */ \
-		i32    param_count; \
-		i32    result_count; \
-		bool   variadic; \
-	})
-
-typedef enum TypeKind {
-	Type_Invalid,
-#define TYPE_KIND(k, ...) GB_JOIN2(Type_, k),
-	TYPE_KINDS
-#undef TYPE_KIND
-	Type_Count,
-} TypeKind;
-
-String const type_strings[] = {
-	{cast(u8 *)"Invalid", gb_size_of("Invalid")},
-#define TYPE_KIND(k, ...) {cast(u8 *)#k, gb_size_of(#k)-1},
-	TYPE_KINDS
-#undef TYPE_KIND
-};
-
-#define TYPE_KIND(k, ...) typedef __VA_ARGS__ GB_JOIN2(Type, k);
-	TYPE_KINDS
-#undef TYPE_KIND
-
-typedef struct Type {
-	TypeKind kind;
-	union {
-#define TYPE_KIND(k, ...) GB_JOIN2(Type, k) k;
-	TYPE_KINDS
-#undef TYPE_KIND
-	};
-} Type;
-
-// NOTE(bill): Internal sizes of certain types
-// string: 2*word_size  (ptr+len)
-// slice:  3*word_size  (ptr+len+cap)
-// array:  count*size_of(elem) aligned
-
-// NOTE(bill): Alignment of structures and other types are to be compatible with C
-
-typedef struct BaseTypeSizes {
-	i64 word_size;
-	i64 max_align;
-} BaseTypeSizes;
-
-typedef Array(isize) Array_isize;
-
-typedef struct Selection {
-	Entity *    entity;
-	Array_isize index;
-	bool        indirect; // Set if there was a pointer deref anywhere down the line
-} Selection;
-Selection empty_selection = {0};
-
-Selection make_selection(Entity *entity, Array_isize index, bool indirect) {
-	Selection s = {entity, index, indirect};
-	return s;
-}
-
-void selection_add_index(Selection *s, isize index) {
-	// IMPORTANT NOTE(bill): this requires a stretchy buffer/dynamic array so it requires some form
-	// of heap allocation
-	if (s->index.e == NULL) {
-		array_init(&s->index, heap_allocator());
-	}
-	array_add(&s->index, index);
-}
-
-
-
-#define STR_LIT(x) {cast(u8 *)(x), gb_size_of(x)-1}
-gb_global Type basic_types[] = {
-	{Type_Basic, {Basic_Invalid,        0,                                       0, STR_LIT("invalid type")}},
-	{Type_Basic, {Basic_bool,           BasicFlag_Boolean,                       1, STR_LIT("bool")}},
-	{Type_Basic, {Basic_i8,             BasicFlag_Integer,                       1, STR_LIT("i8")}},
-	{Type_Basic, {Basic_u8,             BasicFlag_Integer | BasicFlag_Unsigned,  1, STR_LIT("u8")}},
-	{Type_Basic, {Basic_i16,            BasicFlag_Integer,                       2, STR_LIT("i16")}},
-	{Type_Basic, {Basic_u16,            BasicFlag_Integer | BasicFlag_Unsigned,  2, STR_LIT("u16")}},
-	{Type_Basic, {Basic_i32,            BasicFlag_Integer,                       4, STR_LIT("i32")}},
-	{Type_Basic, {Basic_u32,            BasicFlag_Integer | BasicFlag_Unsigned,  4, STR_LIT("u32")}},
-	{Type_Basic, {Basic_i64,            BasicFlag_Integer,                       8, STR_LIT("i64")}},
-	{Type_Basic, {Basic_u64,            BasicFlag_Integer | BasicFlag_Unsigned,  8, STR_LIT("u64")}},
-	{Type_Basic, {Basic_i128,           BasicFlag_Integer,                      16, STR_LIT("i128")}},
-	{Type_Basic, {Basic_u128,           BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
-	// {Type_Basic, {Basic_f16,            BasicFlag_Float,                         2, STR_LIT("f16")}},
-	{Type_Basic, {Basic_f32,            BasicFlag_Float,                         4, STR_LIT("f32")}},
-	{Type_Basic, {Basic_f64,            BasicFlag_Float,                         8, STR_LIT("f64")}},
-	// {Type_Basic, {Basic_f128,           BasicFlag_Float,                        16, STR_LIT("f128")}},
-	{Type_Basic, {Basic_int,            BasicFlag_Integer,                      -1, STR_LIT("int")}},
-	{Type_Basic, {Basic_uint,           BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uint")}},
-	{Type_Basic, {Basic_rawptr,         BasicFlag_Pointer,                      -1, STR_LIT("rawptr")}},
-	{Type_Basic, {Basic_string,         BasicFlag_String,                       -1, STR_LIT("string")}},
-	{Type_Basic, {Basic_any,            0,                                      -1, STR_LIT("any")}},
-	{Type_Basic, {Basic_UntypedBool,    BasicFlag_Boolean | BasicFlag_Untyped,   0, STR_LIT("untyped bool")}},
-	{Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped,   0, STR_LIT("untyped integer")}},
-	{Type_Basic, {Basic_UntypedFloat,   BasicFlag_Float   | BasicFlag_Untyped,   0, STR_LIT("untyped float")}},
-	{Type_Basic, {Basic_UntypedString,  BasicFlag_String  | BasicFlag_Untyped,   0, STR_LIT("untyped string")}},
-	{Type_Basic, {Basic_UntypedRune,    BasicFlag_Integer | BasicFlag_Untyped,   0, STR_LIT("untyped rune")}},
-	{Type_Basic, {Basic_UntypedNil,     BasicFlag_Untyped,                       0, STR_LIT("untyped nil")}},
-};
-
-gb_global Type basic_type_aliases[] = {
-	{Type_Basic, {Basic_byte, BasicFlag_Integer | BasicFlag_Unsigned, 1, STR_LIT("byte")}},
-	{Type_Basic, {Basic_rune, BasicFlag_Integer,                      4, STR_LIT("rune")}},
-};
-
-gb_global Type *t_invalid         = &basic_types[Basic_Invalid];
-gb_global Type *t_bool            = &basic_types[Basic_bool];
-gb_global Type *t_i8              = &basic_types[Basic_i8];
-gb_global Type *t_u8              = &basic_types[Basic_u8];
-gb_global Type *t_i16             = &basic_types[Basic_i16];
-gb_global Type *t_u16             = &basic_types[Basic_u16];
-gb_global Type *t_i32             = &basic_types[Basic_i32];
-gb_global Type *t_u32             = &basic_types[Basic_u32];
-gb_global Type *t_i64             = &basic_types[Basic_i64];
-gb_global Type *t_u64             = &basic_types[Basic_u64];
-gb_global Type *t_i128            = &basic_types[Basic_i128];
-gb_global Type *t_u128            = &basic_types[Basic_u128];
-// gb_global Type *t_f16             = &basic_types[Basic_f16];
-gb_global Type *t_f32             = &basic_types[Basic_f32];
-gb_global Type *t_f64             = &basic_types[Basic_f64];
-// gb_global Type *t_f128            = &basic_types[Basic_f128];
-gb_global Type *t_int             = &basic_types[Basic_int];
-gb_global Type *t_uint            = &basic_types[Basic_uint];
-gb_global Type *t_rawptr          = &basic_types[Basic_rawptr];
-gb_global Type *t_string          = &basic_types[Basic_string];
-gb_global Type *t_any             = &basic_types[Basic_any];
-gb_global Type *t_untyped_bool    = &basic_types[Basic_UntypedBool];
-gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger];
-gb_global Type *t_untyped_float   = &basic_types[Basic_UntypedFloat];
-gb_global Type *t_untyped_string  = &basic_types[Basic_UntypedString];
-gb_global Type *t_untyped_rune    = &basic_types[Basic_UntypedRune];
-gb_global Type *t_untyped_nil     = &basic_types[Basic_UntypedNil];
-gb_global Type *t_byte            = &basic_type_aliases[0];
-gb_global Type *t_rune            = &basic_type_aliases[1];
-
-
-gb_global Type *t_u8_ptr  = NULL;
-gb_global Type *t_int_ptr = NULL;
-
-gb_global Type *t_type_info            = NULL;
-gb_global Type *t_type_info_ptr        = NULL;
-gb_global Type *t_type_info_member     = NULL;
-gb_global Type *t_type_info_member_ptr = NULL;
-
-gb_global Type *t_type_info_named      = NULL;
-gb_global Type *t_type_info_integer    = NULL;
-gb_global Type *t_type_info_float      = NULL;
-gb_global Type *t_type_info_any        = NULL;
-gb_global Type *t_type_info_string     = NULL;
-gb_global Type *t_type_info_boolean    = NULL;
-gb_global Type *t_type_info_pointer    = NULL;
-gb_global Type *t_type_info_maybe      = NULL;
-gb_global Type *t_type_info_procedure  = NULL;
-gb_global Type *t_type_info_array      = NULL;
-gb_global Type *t_type_info_slice      = NULL;
-gb_global Type *t_type_info_vector     = NULL;
-gb_global Type *t_type_info_tuple      = NULL;
-gb_global Type *t_type_info_struct     = NULL;
-gb_global Type *t_type_info_union      = NULL;
-gb_global Type *t_type_info_raw_union  = NULL;
-gb_global Type *t_type_info_enum       = NULL;
-
-gb_global Type *t_allocator            = NULL;
-gb_global Type *t_allocator_ptr        = NULL;
-gb_global Type *t_context              = NULL;
-gb_global Type *t_context_ptr          = NULL;
-
-
-
-
-
-
-gbString type_to_string(Type *type);
-
-Type *base_type(Type *t) {
-	for (;;) {
-		if (t == NULL || t->kind != Type_Named) {
-			break;
-		}
-		t = t->Named.base;
-	}
-	return t;
-}
-
-void set_base_type(Type *t, Type *base) {
-	if (t && t->kind == Type_Named) {
-		t->Named.base = base;
-	}
-}
-
-
-Type *alloc_type(gbAllocator a, TypeKind kind) {
-	Type *t = gb_alloc_item(a, Type);
-	t->kind = kind;
-	return t;
-}
-
-
-Type *make_type_basic(gbAllocator a, BasicType basic) {
-	Type *t = alloc_type(a, Type_Basic);
-	t->Basic = basic;
-	return t;
-}
-
-Type *make_type_pointer(gbAllocator a, Type *elem) {
-	Type *t = alloc_type(a, Type_Pointer);
-	t->Pointer.elem = elem;
-	return t;
-}
-
-Type *make_type_maybe(gbAllocator a, Type *elem) {
-	Type *t = alloc_type(a, Type_Maybe);
-	t->Maybe.elem = elem;
-	return t;
-}
-
-Type *make_type_array(gbAllocator a, Type *elem, i64 count) {
-	Type *t = alloc_type(a, Type_Array);
-	t->Array.elem = elem;
-	t->Array.count = count;
-	return t;
-}
-
-Type *make_type_vector(gbAllocator a, Type *elem, i64 count) {
-	Type *t = alloc_type(a, Type_Vector);
-	t->Vector.elem = elem;
-	t->Vector.count = count;
-	return t;
-}
-
-Type *make_type_slice(gbAllocator a, Type *elem) {
-	Type *t = alloc_type(a, Type_Slice);
-	t->Array.elem = elem;
-	return t;
-}
-
-
-Type *make_type_struct(gbAllocator a) {
-	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_Record);
-	t->Record.kind = TypeRecord_Union;
-	return t;
-}
-
-Type *make_type_raw_union(gbAllocator a) {
-	Type *t = alloc_type(a, Type_Record);
-	t->Record.kind = TypeRecord_RawUnion;
-	return t;
-}
-
-Type *make_type_enum(gbAllocator a) {
-	Type *t = alloc_type(a, Type_Record);
-	t->Record.kind = TypeRecord_Enum;
-	return t;
-}
-
-
-
-Type *make_type_named(gbAllocator a, String name, Type *base, Entity *type_name) {
-	Type *t = alloc_type(a, Type_Named);
-	t->Named.name = name;
-	t->Named.base = base;
-	t->Named.type_name = type_name;
-	return t;
-}
-
-Type *make_type_tuple(gbAllocator a) {
-	Type *t = alloc_type(a, Type_Tuple);
-	return t;
-}
-
-Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_count, Type *results, isize result_count, bool variadic) {
-	Type *t = alloc_type(a, Type_Proc);
-
-	if (variadic) {
-		if (param_count == 0) {
-			GB_PANIC("variadic procedure must have at least one parameter");
-		}
-		GB_ASSERT(params != NULL && params->kind == Type_Tuple);
-		Entity *e = params->Tuple.variables[param_count-1];
-		if (base_type(e->type)->kind != Type_Slice) {
-			// NOTE(bill): For custom calling convention
-			GB_PANIC("variadic parameter must be of type slice");
-		}
-	}
-
-	t->Proc.scope        = scope;
-	t->Proc.params       = params;
-	t->Proc.param_count  = param_count;
-	t->Proc.results      = results;
-	t->Proc.result_count = result_count;
-	t->Proc.variadic     = variadic;
-	return t;
-}
-
-
-Type *type_deref(Type *t) {
-	if (t != NULL) {
-		Type *bt = base_type(t);
-		if (bt == NULL)
-			return NULL;
-		if (bt != NULL && bt->kind == Type_Pointer)
-			return bt->Pointer.elem;
-	}
-	return t;
-}
-
-Type *get_enum_base_type(Type *t) {
-	Type *bt = base_type(t);
-	if (bt->kind == Type_Record && bt->Record.kind == TypeRecord_Enum) {
-		GB_ASSERT(bt->Record.enum_base != NULL);
-		return bt->Record.enum_base;
-	}
-	return t;
-}
-
-bool is_type_named(Type *t) {
-	if (t->kind == Type_Basic) {
-		return true;
-	}
-	return t->kind == Type_Named;
-}
-bool is_type_boolean(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Basic) {
-		return (t->Basic.flags & BasicFlag_Boolean) != 0;
-	}
-	return false;
-}
-bool is_type_integer(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Basic) {
-		return (t->Basic.flags & BasicFlag_Integer) != 0;
-	}
-	return false;
-}
-bool is_type_unsigned(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Basic) {
-		return (t->Basic.flags & BasicFlag_Unsigned) != 0;
-	}
-	return false;
-}
-bool is_type_numeric(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Basic) {
-		return (t->Basic.flags & BasicFlag_Numeric) != 0;
-	}
-	if (t->kind == Type_Vector) {
-		return is_type_numeric(t->Vector.elem);
-	}
-	return false;
-}
-bool is_type_string(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Basic) {
-		return (t->Basic.flags & BasicFlag_String) != 0;
-	}
-	return false;
-}
-bool is_type_typed(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Basic) {
-		return (t->Basic.flags & BasicFlag_Untyped) == 0;
-	}
-	return true;
-}
-bool is_type_untyped(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Basic) {
-		return (t->Basic.flags & BasicFlag_Untyped) != 0;
-	}
-	return false;
-}
-bool is_type_ordered(Type *t) {
-	t = base_type(get_enum_base_type(t));
-	if (t->kind == Type_Basic) {
-		return (t->Basic.flags & BasicFlag_Ordered) != 0;
-	}
-	if (t->kind == Type_Pointer) {
-		return true;
-	}
-	return false;
-}
-bool is_type_constant_type(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Basic) {
-		return (t->Basic.flags & BasicFlag_ConstantType) != 0;
-	}
-	if (t->kind == Type_Record) {
-		return t->Record.kind == TypeRecord_Enum;
-	}
-	return false;
-}
-bool is_type_float(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Basic) {
-		return (t->Basic.flags & BasicFlag_Float) != 0;
-	}
-	return false;
-}
-bool is_type_f32(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Basic) {
-		return t->Basic.kind == Basic_f32;
-	}
-	return false;
-}
-bool is_type_f64(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Basic) {
-		return t->Basic.kind == Basic_f64;
-	}
-	return false;
-}
-bool is_type_pointer(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Basic) {
-		return (t->Basic.flags & BasicFlag_Pointer) != 0;
-	}
-	return t->kind == Type_Pointer;
-}
-bool is_type_maybe(Type *t) {
-	t = base_type(t);
-	return t->kind == Type_Maybe;
-}
-bool is_type_tuple(Type *t) {
-	t = base_type(t);
-	return t->kind == Type_Tuple;
-}
-
-
-bool is_type_int_or_uint(Type *t) {
-	if (t->kind == Type_Basic) {
-		return (t->Basic.kind == Basic_int) || (t->Basic.kind == Basic_uint);
-	}
-	return false;
-}
-bool is_type_rawptr(Type *t) {
-	if (t->kind == Type_Basic) {
-		return t->Basic.kind == Basic_rawptr;
-	}
-	return false;
-}
-bool is_type_u8(Type *t) {
-	if (t->kind == Type_Basic) {
-		return t->Basic.kind == Basic_u8;
-	}
-	return false;
-}
-bool is_type_array(Type *t) {
-	t = base_type(t);
-	return t->kind == Type_Array;
-}
-bool is_type_slice(Type *t) {
-	t = base_type(t);
-	return t->kind == Type_Slice;
-}
-bool is_type_u8_slice(Type *t) {
-	t = base_type(t);
-	if (t->kind == Type_Slice) {
-		return is_type_u8(t->Slice.elem);
-	}
-	return false;
-}
-bool is_type_vector(Type *t) {
-	t = base_type(t);
-	return t->kind == Type_Vector;
-}
-bool is_type_proc(Type *t) {
-	t = base_type(t);
-	return t->kind == Type_Proc;
-}
-Type *base_vector_type(Type *t) {
-	if (is_type_vector(t)) {
-		t = base_type(t);
-		return t->Vector.elem;
-	}
-	return t;
-}
-
-
-bool is_type_enum(Type *t) {
-	t = base_type(t);
-	return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum);
-}
-bool is_type_struct(Type *t) {
-	t = base_type(t);
-	return (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct);
-}
-bool is_type_union(Type *t) {
-	t = base_type(t);
-	return (t->kind == Type_Record && t->Record.kind == TypeRecord_Union);
-}
-bool is_type_raw_union(Type *t) {
-	t = base_type(t);
-	return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion);
-}
-
-bool is_type_any(Type *t) {
-	t = base_type(t);
-	return (t->kind == Type_Basic && t->Basic.kind == Basic_any);
-}
-bool is_type_untyped_nil(Type *t) {
-	t = base_type(t);
-	return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedNil);
-}
-
-
-
-bool is_type_indexable(Type *t) {
-	return is_type_array(t) || is_type_slice(t) || is_type_vector(t) || is_type_string(t);
-}
-
-
-bool type_has_nil(Type *t) {
-	t = base_type(t);
-	switch (t->kind) {
-	case Type_Basic:
-		return is_type_rawptr(t);
-
-	case Type_Tuple:
-		return false;
-
-	case Type_Record:
-		switch (t->Record.kind) {
-		case TypeRecord_Enum:
-			return false;
-		}
-		break;
-	}
-	return true;
-}
-
-
-bool is_type_comparable(Type *t) {
-	t = base_type(get_enum_base_type(t));
-	switch (t->kind) {
-	case Type_Basic:
-		return t->kind != Basic_UntypedNil;
-	case Type_Pointer:
-		return true;
-	case Type_Record: {
-		if (false && is_type_struct(t)) {
-			// TODO(bill): Should I even allow this?
-			for (isize i = 0; i < t->Record.field_count; i++) {
-				if (!is_type_comparable(t->Record.fields[i]->type))
-					return false;
-			}
-		} else if (is_type_enum(t)) {
-			return is_type_comparable(t->Record.enum_base);
-		}
-		return false;
-	} break;
-	case Type_Array:
-		return is_type_comparable(t->Array.elem);
-	case Type_Vector:
-		return is_type_comparable(t->Vector.elem);
-	case Type_Proc:
-		return true;
-	}
-	return false;
-}
-
-bool are_types_identical(Type *x, Type *y) {
-	if (x == y)
-		return true;
-
-	if ((x == NULL && y != NULL) ||
-	    (x != NULL && y == NULL)) {
-		return false;
-	}
-
-	switch (x->kind) {
-	case Type_Basic:
-		if (y->kind == Type_Basic) {
-			return x->Basic.kind == y->Basic.kind;
-		}
-		break;
-
-	case Type_Array:
-		if (y->kind == Type_Array) {
-			return (x->Array.count == y->Array.count) && are_types_identical(x->Array.elem, y->Array.elem);
-		}
-		break;
-
-	case Type_Vector:
-		if (y->kind == Type_Vector) {
-			return (x->Vector.count == y->Vector.count) && are_types_identical(x->Vector.elem, y->Vector.elem);
-		}
-		break;
-
-	case Type_Slice:
-		if (y->kind == Type_Slice) {
-			return are_types_identical(x->Slice.elem, y->Slice.elem);
-		}
-		break;
-
-	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 &&
-					    x->Record.struct_is_packed == y->Record.struct_is_packed &&
-					    x->Record.struct_is_ordered == y->Record.struct_is_ordered) {
-						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;
-							}
-							if (str_ne(x->Record.fields[i]->token.string, y->Record.fields[i]->token.string)) {
-								return false;
-							}
-						}
-						return true;
-					}
-					break;
-
-				case TypeRecord_Enum:
-					// NOTE(bill): Each enum is unique
-					return x == y;
-				}
-			}
-		}
-		break;
-
-	case Type_Pointer:
-		if (y->kind == Type_Pointer) {
-			return are_types_identical(x->Pointer.elem, y->Pointer.elem);
-		}
-		break;
-
-	case Type_Maybe:
-		if (y->kind == Type_Maybe) {
-			return are_types_identical(x->Maybe.elem, y->Maybe.elem);
-		}
-		break;
-
-	case Type_Named:
-		if (y->kind == Type_Named) {
-			return x->Named.base == y->Named.base;
-		}
-		break;
-
-	case Type_Tuple:
-		if (y->kind == Type_Tuple) {
-			if (x->Tuple.variable_count == y->Tuple.variable_count) {
-				for (isize i = 0; i < x->Tuple.variable_count; i++) {
-					if (!are_types_identical(x->Tuple.variables[i]->type, y->Tuple.variables[i]->type)) {
-						return false;
-					}
-				}
-				return true;
-			}
-		}
-		break;
-
-	case Type_Proc:
-		if (y->kind == Type_Proc) {
-			return are_types_identical(x->Proc.params, y->Proc.params) &&
-			       are_types_identical(x->Proc.results, y->Proc.results);
-		}
-		break;
-	}
-
-
-	return false;
-}
-
-
-Type *default_type(Type *type) {
-	if (type->kind == Type_Basic) {
-		switch (type->Basic.kind) {
-		case Basic_UntypedBool:    return t_bool;
-		case Basic_UntypedInteger: return t_int;
-		case Basic_UntypedFloat:   return t_f64;
-		case Basic_UntypedString:  return t_string;
-		case Basic_UntypedRune:    return t_rune;
-		}
-	}
-	return type;
-}
-
-
-
-
-gb_global Entity *entity__any_type_info  = NULL;
-gb_global Entity *entity__any_data       = NULL;
-gb_global Entity *entity__string_data    = NULL;
-gb_global Entity *entity__string_count   = NULL;
-gb_global Entity *entity__slice_count    = NULL;
-gb_global Entity *entity__slice_capacity = NULL;
-
-Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel);
-
-Selection lookup_field(gbAllocator a, Type *type_, String field_name, bool is_type) {
-	return lookup_field_with_selection(a, type_, field_name, is_type, empty_selection);
-}
-
-Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel) {
-	GB_ASSERT(type_ != NULL);
-
-	if (str_eq(field_name, str_lit("_"))) {
-		return empty_selection;
-	}
-
-	Type *type = type_deref(type_);
-	bool is_ptr = type != type_;
-	sel.indirect = sel.indirect || is_ptr;
-
-	type = base_type(type);
-
-	if (type->kind == Type_Basic) {
-		switch (type->Basic.kind) {
-		case Basic_any: {
-			String type_info_str = str_lit("type_info");
-			String data_str = str_lit("data");
-			if (entity__any_type_info == NULL) {
-				entity__any_type_info = make_entity_field(a, NULL, make_token_ident(type_info_str), t_type_info_ptr, false, 0);
-			}
-			if (entity__any_data == NULL) {
-				entity__any_data = make_entity_field(a, NULL, make_token_ident(data_str), t_rawptr, false, 1);
-			}
-
-			if (str_eq(field_name, type_info_str)) {
-				selection_add_index(&sel, 0);
-				sel.entity = entity__any_type_info;
-				return sel;
-			} else if (str_eq(field_name, data_str)) {
-				selection_add_index(&sel, 1);
-				sel.entity = entity__any_data;
-				return sel;
-			}
-		} break;
-		case Basic_string: {
-			String data_str = str_lit("data");
-			String count_str = str_lit("count");
-			if (entity__string_data == NULL) {
-				entity__string_data = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, t_u8), false, 0);
-			}
-
-			if (entity__string_count == NULL) {
-				entity__string_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1);
-			}
-
-			if (str_eq(field_name, data_str)) {
-				selection_add_index(&sel, 0);
-				sel.entity = entity__string_data;
-				return sel;
-			} else if (str_eq(field_name, count_str)) {
-				selection_add_index(&sel, 1);
-				sel.entity = entity__string_count;
-				return sel;
-			}
-		} break;
-		}
-
-		return sel;
-	} else if (type->kind == Type_Array) {
-		String count_str = str_lit("count");
-		// NOTE(bill): Underlying memory address cannot be changed
-		if (str_eq(field_name, count_str)) {
-			// HACK(bill): Memory leak
-			sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, make_exact_value_integer(type->Array.count));
-			return sel;
-		}
-	} else if (type->kind == Type_Vector) {
-		String count_str = str_lit("count");
-		// NOTE(bill): Vectors are not addressable
-		if (str_eq(field_name, count_str)) {
-			// HACK(bill): Memory leak
-			sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, make_exact_value_integer(type->Vector.count));
-			return sel;
-		}
-
-		if (type->Vector.count <= 4 && !is_type_boolean(type->Vector.elem)) {
-			// HACK(bill): Memory leak
-			switch (type->Vector.count) {
-			#define _VECTOR_FIELD_CASE(_length, _name) \
-			case (_length): \
-				if (str_eq(field_name, str_lit(_name))) { \
-					selection_add_index(&sel, (_length)-1); \
-					sel.entity = make_entity_vector_elem(a, NULL, make_token_ident(str_lit(_name)), type->Vector.elem, (_length)-1); \
-					return sel; \
-				} \
-				/*fallthrough*/
-
-			_VECTOR_FIELD_CASE(4, "w");
-			_VECTOR_FIELD_CASE(3, "z");
-			_VECTOR_FIELD_CASE(2, "y");
-			_VECTOR_FIELD_CASE(1, "x");
-			default: break;
-
-			#undef _VECTOR_FIELD_CASE
-			}
-		}
-
-	} else if (type->kind == Type_Slice) {
-		String data_str     = str_lit("data");
-		String count_str    = str_lit("count");
-		String capacity_str = str_lit("capacity");
-
-		if (str_eq(field_name, data_str)) {
-			selection_add_index(&sel, 0);
-			// HACK(bill): Memory leak
-			sel.entity = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, type->Slice.elem), false, 0);
-			return sel;
-		} else if (str_eq(field_name, count_str)) {
-			selection_add_index(&sel, 1);
-			if (entity__slice_count == NULL) {
-				entity__slice_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1);
-			}
-
-			sel.entity = entity__slice_count;
-			return sel;
-		} else if (str_eq(field_name, capacity_str)) {
-			selection_add_index(&sel, 2);
-			if (entity__slice_capacity == NULL) {
-				entity__slice_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 2);
-			}
-
-			sel.entity = entity__slice_capacity;
-			return sel;
-		}
-	}
-
-	if (type->kind != Type_Record) {
-		return sel;
-	}
-	if (is_type) {
-		if (is_type_union(type)) {
-			// NOTE(bill): The subtype for a union are stored in the fields
-			// as they are "kind of" like variables but not
-			for (isize i = 0; i < type->Record.field_count; i++) {
-				Entity *f = type->Record.fields[i];
-				GB_ASSERT(f->kind == Entity_TypeName);
-				String str = f->token.string;
-
-				if (str_eq(field_name, str)) {
-					sel.entity = f;
-					selection_add_index(&sel, i);
-					return sel;
-				}
-			}
-		}
-
-		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 (str_eq(field_name, str)) {
-				sel.entity = f;
-				selection_add_index(&sel, i);
-				return sel;
-			}
-		}
-
-		if (is_type_enum(type)) {
-			if (str_eq(field_name, str_lit("count"))) {
-				sel.entity = type->Record.enum_count;
-				return sel;
-			} else if (str_eq(field_name, str_lit("min_value"))) {
-				sel.entity = type->Record.min_value;
-				return sel;
-			} else if (str_eq(field_name, str_lit("max_value"))) {
-				sel.entity = type->Record.max_value;
-				return sel;
-			}
-		}
-
-	} else if (!is_type_enum(type) && !is_type_union(type)) {
-		for (isize i = 0; i < type->Record.field_count; i++) {
-			Entity *f = type->Record.fields[i];
-			GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field);
-			String str = f->token.string;
-			if (str_eq(field_name, str)) {
-				selection_add_index(&sel, i);  // HACK(bill): Leaky memory
-				sel.entity = f;
-				return sel;
-			}
-
-			if (f->flags & EntityFlag_Anonymous) {
-				isize prev_count = sel.index.count;
-				selection_add_index(&sel, i); // HACK(bill): Leaky memory
-
-				sel = lookup_field_with_selection(a, f->type, field_name, is_type, sel);
-
-				if (sel.entity != NULL) {
-					if (is_type_pointer(f->type)) {
-						sel.indirect = true;
-					}
-					return sel;
-				}
-				sel.index.count = prev_count;
-			}
-		}
-	}
-
-	return sel;
-}
-
-
-
-i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t);
-i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t);
-i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, i64 index);
-
-i64 align_formula(i64 size, i64 align) {
-	if (align > 0) {
-		i64 result = size + align-1;
-		return result - result%align;
-	}
-	return size;
-}
-
-i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
-	t = base_type(t);
-
-	switch (t->kind) {
-	case Type_Array:
-		return type_align_of(s, allocator, t->Array.elem);
-	case Type_Vector: {
-		i64 size = type_size_of(s, allocator, t->Vector.elem);
-		i64 count = gb_max(prev_pow2(t->Vector.count), 1);
-		i64 total = size * count;
-		return gb_clamp(total, 1, s.max_align);
-	} break;
-
-	case Type_Tuple: {
-		i64 max = 1;
-		for (isize i = 0; i < t->Tuple.variable_count; i++) {
-			i64 align = type_align_of(s, allocator, t->Tuple.variables[i]->type);
-			if (max < align) {
-				max = align;
-			}
-		}
-		return max;
-	} break;
-
-	case Type_Maybe:
-		return gb_max(type_align_of(s, allocator, t->Maybe.elem), type_align_of(s, allocator, t_bool));
-
-	case Type_Record: {
-		switch (t->Record.kind) {
-		case TypeRecord_Struct:
-			if (t->Record.field_count > 0) {
-				// TODO(bill): What is this supposed to be?
-				if (t->Record.struct_is_packed) {
-					i64 max = s.word_size;
-					for (isize i = 1; i < t->Record.field_count; i++) {
-						// NOTE(bill): field zero is null
-						i64 align = type_align_of(s, allocator, t->Record.fields[i]->type);
-						if (max < align) {
-							max = align;
-						}
-					}
-					return max;
-				}
-				return type_align_of(s, allocator, t->Record.fields[0]->type);
-			}
-			break;
-		case TypeRecord_Union: {
-			i64 max = 1;
-			for (isize i = 1; i < t->Record.field_count; i++) {
-				// NOTE(bill): field zero is null
-				i64 align = type_align_of(s, allocator, t->Record.fields[i]->type);
-				if (max < align) {
-					max = align;
-				}
-			}
-			return max;
-		} break;
-		case TypeRecord_RawUnion: {
-			i64 max = 1;
-			for (isize i = 0; i < t->Record.field_count; i++) {
-				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;
-	}
-
-	// return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.max_align);
-	// NOTE(bill): Things that are bigger than s.word_size, are actually comprised of smaller types
-	// TODO(bill): Is this correct for 128-bit types (integers)?
-	return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.word_size);
-}
-
-i64 *type_set_offsets_of(BaseTypeSizes s, gbAllocator allocator, Entity **fields, isize field_count, bool is_packed) {
-	i64 *offsets = gb_alloc_array(allocator, i64, field_count);
-	i64 curr_offset = 0;
-	if (is_packed) {
-		for (isize i = 0; i < field_count; i++) {
-			offsets[i] = curr_offset;
-			curr_offset += type_size_of(s, allocator, fields[i]->type);
-		}
-
-	} else {
-		for (isize i = 0; i < field_count; i++) {
-			i64 align = type_align_of(s, allocator, fields[i]->type);
-			curr_offset = align_formula(curr_offset, align);
-			offsets[i] = curr_offset;
-			curr_offset += type_size_of(s, allocator, fields[i]->type);
-		}
-	}
-	return offsets;
-}
-
-bool type_set_offsets(BaseTypeSizes s, gbAllocator allocator, Type *t) {
-	t = base_type(t);
-	if (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;
-		}
-	} else if (is_type_tuple(t)) {
-		if (!t->Tuple.are_offsets_set) {
-			t->Tuple.offsets = type_set_offsets_of(s, allocator, t->Tuple.variables, t->Tuple.variable_count, false);
-			t->Tuple.are_offsets_set = true;
-			return true;
-		}
-	} else {
-		GB_PANIC("Invalid type for setting offsets");
-	}
-	return false;
-}
-
-i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
-	t = base_type(t);
-	switch (t->kind) {
-	case Type_Basic: {
-		GB_ASSERT(is_type_typed(t));
-		BasicKind kind = t->Basic.kind;
-		i64 size = t->Basic.size;
-		if (size > 0) {
-			return size;
-		}
-		switch (kind) {
-		case Basic_string: return 2*s.word_size;
-		case Basic_any:    return 2*s.word_size;
-
-		case Basic_int: case Basic_uint: case Basic_rawptr:
-			return s.word_size;
-		}
-	} break;
-
-	case Type_Array: {
-		i64 count = t->Array.count;
-		if (count == 0) {
-			return 0;
-		}
-		i64 align = type_align_of(s, allocator, t->Array.elem);
-		i64 size  = type_size_of(s,  allocator, t->Array.elem);
-		i64 alignment = align_formula(size, align);
-		return alignment*(count-1) + size;
-	} break;
-
-	case Type_Vector: {
-		i64 count = t->Vector.count;
-		if (count == 0) {
-			return 0;
-		}
-		// i64 align = type_align_of(s, allocator, t->Vector.elem);
-		i64 bit_size = 8*type_size_of(s,  allocator, t->Vector.elem);
-		if (is_type_boolean(t->Vector.elem)) {
-			bit_size = 1; // NOTE(bill): LLVM can store booleans as 1 bit because a boolean _is_ an `i1`
-			              // Silly LLVM spec
-		}
-		i64 total_size_in_bits = bit_size * count;
-		i64 total_size = (total_size_in_bits+7)/8;
-		return total_size;
-	} break;
-
-
-	case Type_Slice: // ptr + len + cap
-		return 3 * s.word_size;
-
-	case Type_Maybe: { // value + bool
-		Type *elem = t->Maybe.elem;
-		i64 align = type_align_of(s, allocator, elem);
-		i64 size = align_formula(type_size_of(s, allocator, elem), align);
-		size += type_size_of(s, allocator, t_bool);
-		return align_formula(size, align);
-	}
-
-	case Type_Tuple: {
-		i64 count = t->Tuple.variable_count;
-		if (count == 0) {
-			return 0;
-		}
-		type_set_offsets(s, allocator, t);
-		i64 size = t->Tuple.offsets[count-1] + type_size_of(s, allocator, t->Tuple.variables[count-1]->type);
-		i64 align = type_align_of(s, allocator, t);
-		return align_formula(size, align);
-	} 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);
-			i64 size = t->Record.struct_offsets[count-1] + type_size_of(s, allocator, t->Record.fields[count-1]->type);
-			i64 align = type_align_of(s, allocator, t);
-			return align_formula(size, align);
-		} break;
-
-		case TypeRecord_Union: {
-			i64 count = t->Record.field_count;
-			i64 max = 0;
-			// NOTE(bill): Zeroth field is invalid
-			for (isize i = 1; i < count; i++) {
-				i64 size = type_size_of(s, allocator, t->Record.fields[i]->type);
-				if (max < size) {
-					max = size;
-				}
-			}
-			// NOTE(bill): Align to int
-			i64 align = type_align_of(s, allocator, t);
-			isize size =  align_formula(max, s.word_size);
-			size += type_size_of(s, allocator, t_int);
-			return align_formula(size, align);
-		} 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;
-				}
-			}
-			// TODO(bill): Is this how it should work?
-			i64 align = type_align_of(s, allocator, t);
-			return align_formula(max, align);
-		} break;
-
-		case TypeRecord_Enum: {
-			return type_size_of(s, allocator, t->Record.enum_base);
-		} break;
-		}
-	} break;
-	}
-
-	// Catch all
-	return s.word_size;
-}
-
-i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) {
-	t = base_type(t);
-	if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) {
-		type_set_offsets(s, allocator, t);
-		if (gb_is_between(index, 0, t->Record.field_count-1)) {
-			return t->Record.struct_offsets[index];
-		}
-	} else if (t->kind == Type_Tuple) {
-		type_set_offsets(s, allocator, t);
-		if (gb_is_between(index, 0, t->Tuple.variable_count-1)) {
-			return t->Tuple.offsets[index];
-		}
-	}  else if (t->kind == Type_Basic) {
-		if (t->Basic.kind == Basic_string) {
-			switch (index) {
-			case 0: return 0;
-			case 1: return s.word_size;
-			}
-		} else if (t->Basic.kind == Basic_any) {
-			switch (index) {
-			case 0: return 0;
-			case 1: return s.word_size;
-			}
-		}
-	} else if (t->kind == Type_Slice) {
-		switch (index) {
-		case 0: return 0;
-		case 1: return 1*s.word_size;
-		case 2: return 2*s.word_size;
-		}
-	}
-	return 0;
-}
-
-
-i64 type_offset_of_from_selection(BaseTypeSizes s, gbAllocator allocator, Type *type, Selection sel) {
-	GB_ASSERT(sel.indirect == false);
-
-	Type *t = type;
-	i64 offset = 0;
-	for_array(i, sel.index) {
-		isize index = sel.index.e[i];
-		t = base_type(t);
-		offset += type_offset_of(s, allocator, t, index);
-		if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) {
-			t = t->Record.fields[index]->type;
-		} else {
-			// NOTE(bill): string/any/slices don't have record fields so this case doesn't need to be handled
-		}
-	}
-	return offset;
-}
-
-
-
-gbString write_type_to_string(gbString str, Type *type) {
-	if (type == NULL) {
-		return gb_string_appendc(str, "<no type>");
-	}
-
-	switch (type->kind) {
-	case Type_Basic:
-		str = gb_string_append_length(str, type->Basic.name.text, type->Basic.name.len);
-		break;
-
-	case Type_Pointer:
-		str = gb_string_appendc(str, "^");
-		str = write_type_to_string(str, type->Pointer.elem);
-		break;
-
-	case Type_Maybe:
-		str = gb_string_appendc(str, "?");
-		str = write_type_to_string(str, type->Maybe.elem);
-		break;
-
-	case Type_Array:
-		str = gb_string_appendc(str, gb_bprintf("[%td]", type->Array.count));
-		str = write_type_to_string(str, type->Array.elem);
-		break;
-
-	case Type_Vector:
-		str = gb_string_appendc(str, gb_bprintf("{%td}", type->Vector.count));
-		str = write_type_to_string(str, type->Vector.elem);
-		break;
-
-	case Type_Slice:
-		str = gb_string_appendc(str, "[]");
-		str = write_type_to_string(str, type->Array.elem);
-		break;
-
-	case Type_Record: {
-		switch (type->Record.kind) {
-		case TypeRecord_Struct:
-			str = gb_string_appendc(str, "struct");
-			if (type->Record.struct_is_packed) {
-				str = gb_string_appendc(str, " #packed");
-			}
-			if (type->Record.struct_is_ordered) {
-				str = gb_string_appendc(str, " #ordered");
-			}
-			str = gb_string_appendc(str, " {");
-			for (isize i = 0; i < type->Record.field_count; i++) {
-				Entity *f = type->Record.fields[i];
-				GB_ASSERT(f->kind == Entity_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_Union:
-			str = gb_string_appendc(str, "union{");
-			for (isize i = 1; i < type->Record.field_count; i++) {
-				Entity *f = type->Record.fields[i];
-				GB_ASSERT(f->kind == Entity_TypeName);
-				if (i > 1) {
-					str = gb_string_appendc(str, "; ");
-				}
-				str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
-				str = gb_string_appendc(str, ": ");
-				str = write_type_to_string(str, base_type(f->type));
-			}
-			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 TypeRecord_Enum:
-			str = gb_string_appendc(str, "enum ");
-			str = write_type_to_string(str, type->Record.enum_base);
-			break;
-		}
-	} break;
-
-
-	case Type_Named:
-		if (type->Named.type_name != NULL) {
-			str = gb_string_append_length(str, type->Named.name.text, type->Named.name.len);
-		} else {
-			// NOTE(bill): Just in case
-			str = gb_string_appendc(str, "<named type>");
-		}
-		break;
-
-	case Type_Tuple:
-		if (type->Tuple.variable_count > 0) {
-			for (isize i = 0; i < type->Tuple.variable_count; i++) {
-				Entity *var = type->Tuple.variables[i];
-				if (var != NULL) {
-					GB_ASSERT(var->kind == Entity_Variable);
-					if (i > 0)
-						str = gb_string_appendc(str, ", ");
-					str = write_type_to_string(str, var->type);
-				}
-			}
-		}
-		break;
-
-	case Type_Proc:
-		str = gb_string_appendc(str, "proc(");
-		if (type->Proc.params)
-			str = write_type_to_string(str, type->Proc.params);
-		str = gb_string_appendc(str, ")");
-		if (type->Proc.results) {
-			str = gb_string_appendc(str, " -> ");
-			str = write_type_to_string(str, type->Proc.results);
-		}
-		break;
-	}
-
-	return str;
-}
-
-
-gbString type_to_string(Type *type) {
-	gbString str = gb_string_make(gb_heap_allocator(), "");
-	return write_type_to_string(str, type);
-}
-
-

+ 0 - 250
src/common.cpp

@@ -1,250 +0,0 @@
-#define GB_NO_DEFER
-#define GB_IMPLEMENTATION
-#include "gb/gb.h"
-
-typedef _Bool bool;
-
-gbAllocator heap_allocator(void) {
-	return gb_heap_allocator();
-}
-
-#include "string.cpp"
-#include "array.cpp"
-
-gb_global String global_module_path = {0};
-gb_global bool global_module_path_set = false;
-
-
-String get_module_dir() {
-	if (global_module_path_set) {
-		return global_module_path;
-	}
-
-	Array(wchar_t) path_buf;
-	array_init_count(&path_buf, heap_allocator(), 300);
-
-	isize len = 0;
-	for (;;) {
-		len = GetModuleFileNameW(NULL, &path_buf.e[0], path_buf.count);
-		if (len == 0) {
-			return make_string(NULL, 0);
-		}
-		if (len < path_buf.count) {
-			break;
-		}
-		array_resize(&path_buf, 2*path_buf.count + 300);
-	}
-
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
-
-	wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
-
-	GetModuleFileNameW(NULL, text, len);
-	String path = string16_to_string(heap_allocator(), make_string16(text, len));
-	for (isize i = path.len-1; i >= 0; i--) {
-		u8 c = path.text[i];
-		if (c == '/' || c == '\\') {
-			break;
-		}
-		path.len--;
-	}
-
-	global_module_path = path;
-	global_module_path_set = true;
-
-	gb_temp_arena_memory_end(tmp);
-
-	array_free(&path_buf);
-
-	return path;
-}
-
-String path_to_fullpath(gbAllocator a, String s) {
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
-	String16 string16 = string_to_string16(string_buffer_allocator, s);
-	String result = {0};
-
-	DWORD len = GetFullPathNameW(string16.text, 0, NULL, NULL);
-	if (len != 0) {
-		wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
-		GetFullPathNameW(string16.text, len, text, NULL);
-		text[len] = 0;
-		result = string16_to_string(a, make_string16(text, len));
-	}
-	gb_temp_arena_memory_end(tmp);
-	return result;
-}
-
-// Hasing
-typedef enum HashKeyKind {
-	HashKey_Default,
-	HashKey_String,
-	HashKey_Pointer,
-} HashKeyKind;
-
-typedef struct HashKey {
-	HashKeyKind kind;
-	u64         key;
-	union {
-		String string; // if String, s.len > 0
-		void * ptr;
-	};
-} HashKey;
-
-gb_inline HashKey hashing_proc(void const *data, isize len) {
-	HashKey h = {HashKey_Default};
-	h.kind = HashKey_Default;
-	// h.key = gb_murmur64(data, len);
-	h.key = gb_fnv64a(data, len);
-	return h;
-}
-
-gb_inline HashKey hash_string(String s) {
-	HashKey h = hashing_proc(s.text, s.len);
-	h.kind = HashKey_String;
-	h.string = s;
-	return h;
-}
-
-gb_inline HashKey hash_pointer(void *ptr) {
-	HashKey h = {HashKey_Default};
-	h.key = cast(u64)cast(uintptr)ptr;
-	h.ptr = ptr;
-	h.kind = HashKey_Default;
-	return h;
-}
-
-bool hash_key_equal(HashKey a, HashKey b) {
-	if (a.key == b.key) {
-		// NOTE(bill): If two string's hashes collide, compare the strings themselves
-		if (a.kind == HashKey_String) {
-			if (b.kind == HashKey_String) {
-				return str_eq(a.string, b.string);
-			}
-			return false;
-		}
-		return true;
-	}
-	return false;
-}
-
-i64 next_pow2(i64 n) {
-	if (n <= 0) {
-		return 0;
-	}
-	n--;
-	n |= n >> 1;
-	n |= n >> 2;
-	n |= n >> 4;
-	n |= n >> 8;
-	n |= n >> 16;
-	n |= n >> 32;
-	n++;
-	return n;
-}
-
-i64 prev_pow2(i64 n) {
-	if (n <= 0) {
-		return 0;
-	}
-	n |= n >> 1;
-	n |= n >> 2;
-	n |= n >> 4;
-	n |= n >> 8;
-	n |= n >> 16;
-	n |= n >> 32;
-	return n - (n >> 1);
-}
-
-i16 f32_to_f16(f32 value) {
-	union { u32 i; f32 f; } v;
-	i32 i, s, e, m;
-
-	v.f = value;
-	i = (i32)v.i;
-
-	s =  (i >> 16) & 0x00008000;
-	e = ((i >> 23) & 0x000000ff) - (127 - 15);
-	m =   i        & 0x007fffff;
-
-
-	if (e <= 0) {
-		if (e < -10) return cast(i16)s;
-		m = (m | 0x00800000) >> (1 - e);
-
-		if (m & 0x00001000)
-			m += 0x00002000;
-
-		return cast(i16)(s | (m >> 13));
-	} else if (e == 0xff - (127 - 15)) {
-		if (m == 0) {
-			return cast(i16)(s | 0x7c00); /* NOTE(bill): infinity */
-		} else {
-			/* NOTE(bill): NAN */
-			m >>= 13;
-			return cast(i16)(s | 0x7c00 | m | (m == 0));
-		}
-	} else {
-		if (m & 0x00001000) {
-			m += 0x00002000;
-			if (m & 0x00800000) {
-				m = 0;
-				e += 1;
-			}
-		}
-
-		if (e > 30) {
-			float volatile f = 1e12f;
-			int j;
-			for (j = 0; j < 10; j++)
-				f *= f; /* NOTE(bill): Cause overflow */
-
-			return cast(i16)(s | 0x7c00);
-		}
-
-		return cast(i16)(s | (e << 10) | (m >> 13));
-	}
-}
-
-
-
-#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
-
-
-// Doubly Linked Lists
-
-#define DLIST_SET(curr_element, next_element)  do { \
-	(curr_element)->next = (next_element);             \
-	(curr_element)->next->prev = (curr_element);       \
-	(curr_element) = (curr_element)->next;             \
-} while (0)
-
-#define DLIST_APPEND(root_element, curr_element, next_element) do { \
-	if ((root_element) == NULL) { \
-		(root_element) = (curr_element) = (next_element); \
-	} else { \
-		DLIST_SET(curr_element, next_element); \
-	} \
-} while (0)
-
-////////////////////////////////////////////////////////////////
-//
-// Generic Data Structures
-//
-////////////////////////////////////////////////////////////////
-
-
-#define MAP_TYPE String
-#define MAP_FUNC map_string_
-#define MAP_NAME MapString
-#include "map.c"
-
-#define MAP_TYPE bool
-#define MAP_FUNC map_bool_
-#define MAP_NAME MapBool
-#include "map.c"
-
-#define MAP_TYPE isize
-#define MAP_FUNC map_isize_
-#define MAP_NAME MapIsize
-#include "map.c"

+ 0 - 400
src/exact_value.cpp

@@ -1,400 +0,0 @@
-#include <math.h>
-
-// TODO(bill): Big numbers
-// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!!
-
-typedef struct AstNode AstNode;
-
-typedef enum ExactValueKind {
-	ExactValue_Invalid,
-
-	ExactValue_Bool,
-	ExactValue_String,
-	ExactValue_Integer,
-	ExactValue_Float,
-	ExactValue_Pointer,
-	ExactValue_Compound, // TODO(bill): Is this good enough?
-
-	ExactValue_Count,
-} ExactValueKind;
-
-typedef struct ExactValue {
-	ExactValueKind kind;
-	union {
-		bool      value_bool;
-		String   value_string;
-		i64      value_integer; // NOTE(bill): This must be an integer and not a pointer
-		f64      value_float;
-		i64      value_pointer;
-		AstNode *value_compound;
-	};
-} ExactValue;
-
-HashKey hash_exact_value(ExactValue v) {
-	return hashing_proc(&v, gb_size_of(ExactValue));
-}
-
-
-ExactValue make_exact_value_compound(AstNode *node) {
-	ExactValue result = {ExactValue_Compound};
-	result.value_compound = node;
-	return result;
-}
-
-ExactValue make_exact_value_bool(bool b) {
-	ExactValue result = {ExactValue_Bool};
-	result.value_bool = (b != 0);
-	return result;
-}
-
-ExactValue make_exact_value_string(String string) {
-	// TODO(bill): Allow for numbers with underscores in them
-	ExactValue result = {ExactValue_String};
-	result.value_string = string;
-	return result;
-}
-
-ExactValue make_exact_value_integer_from_string(String string) {
-	// TODO(bill): Allow for numbers with underscores in them
-	ExactValue result = {ExactValue_Integer};
-	i32 base = 10;
-	if (string.text[0] == '0') {
-		switch (string.text[1]) {
-		case 'b': base = 2;  break;
-		case 'o': base = 8;  break;
-		case 'd': base = 10; break;
-		case 'x': base = 16; break;
-		}
-	}
-
-	result.value_integer = gb_str_to_i64(cast(char *)string.text, NULL, base);
-
-	return result;
-}
-
-ExactValue make_exact_value_integer(i64 i) {
-	ExactValue result = {ExactValue_Integer};
-	result.value_integer = i;
-	return result;
-}
-
-ExactValue make_exact_value_float_from_string(String string) {
-	// TODO(bill): Allow for numbers with underscores in them
-	ExactValue result = {ExactValue_Float};
-	result.value_float = gb_str_to_f64(cast(char *)string.text, NULL);
-	return result;
-}
-
-ExactValue make_exact_value_float(f64 f) {
-	ExactValue result = {ExactValue_Float};
-	result.value_float = f;
-	return result;
-}
-
-ExactValue make_exact_value_pointer(i64 ptr) {
-	ExactValue result = {ExactValue_Pointer};
-	result.value_pointer = ptr;
-	return result;
-}
-
-ExactValue make_exact_value_from_basic_literal(Token token) {
-	switch (token.kind) {
-	case Token_String:  return make_exact_value_string(token.string);
-	case Token_Integer: return make_exact_value_integer_from_string(token.string);
-	case Token_Float:   return make_exact_value_float_from_string(token.string);
-	case Token_Rune: {
-		Rune r = GB_RUNE_INVALID;
-		gb_utf8_decode(token.string.text, token.string.len, &r);
-		// gb_printf("%.*s rune: %d\n", LIT(token.string), r);
-		return make_exact_value_integer(r);
-	}
-	default:
-		GB_PANIC("Invalid token for basic literal");
-		break;
-	}
-
-	ExactValue result = {ExactValue_Invalid};
-	return result;
-}
-
-ExactValue exact_value_to_integer(ExactValue v) {
-	switch (v.kind) {
-	case ExactValue_Integer:
-		return v;
-	case ExactValue_Float: {
-		i64 i = cast(i64)v.value_float;
-		f64 f = cast(f64)i;
-		if (f == v.value_float) {
-			return make_exact_value_integer(i);
-		}
-	} break;
-
-	case ExactValue_Pointer:
-		return make_exact_value_integer(cast(i64)cast(intptr)v.value_pointer);
-	}
-	ExactValue r = {ExactValue_Invalid};
-	return r;
-}
-
-ExactValue exact_value_to_float(ExactValue v) {
-	switch (v.kind) {
-	case ExactValue_Integer:
-		return make_exact_value_float(cast(i64)v.value_integer);
-	case ExactValue_Float:
-		return v;
-	}
-	ExactValue r = {ExactValue_Invalid};
-	return r;
-}
-
-
-ExactValue exact_unary_operator_value(Token op, ExactValue v, i32 precision) {
-	switch (op.kind) {
-	case Token_Add:	{
-		switch (v.kind) {
-		case ExactValue_Invalid:
-		case ExactValue_Integer:
-		case ExactValue_Float:
-			return v;
-		}
-	} break;
-
-	case Token_Sub:	{
-		switch (v.kind) {
-		case ExactValue_Invalid:
-			return v;
-		case ExactValue_Integer: {
-			ExactValue i = v;
-			i.value_integer = -i.value_integer;
-			return i;
-		}
-		case ExactValue_Float: {
-			ExactValue i = v;
-			i.value_float = -i.value_float;
-			return i;
-		}
-		}
-	} break;
-
-	case Token_Xor: {
-		i64 i = 0;
-		switch (v.kind) {
-		case ExactValue_Invalid:
-			return v;
-		case ExactValue_Integer:
-			i = v.value_integer;
-			i = ~i;
-			break;
-		default:
-			goto failure;
-		}
-
-		// NOTE(bill): unsigned integers will be negative and will need to be
-		// limited to the types precision
-		if (precision > 0)
-			i &= ~((~0ll)<<precision);
-
-		return make_exact_value_integer(i);
-	} break;
-
-	case Token_Not: {
-		switch (v.kind) {
-		case ExactValue_Invalid: return v;
-		case ExactValue_Bool:
-			return make_exact_value_bool(!v.value_bool);
-		}
-	} break;
-	}
-
-failure:
-	GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op.kind]));
-
-	ExactValue error_value = {0};
-	return error_value;
-}
-
-// NOTE(bill): Make sure things are evaluated in correct order
-i32 exact_value_order(ExactValue v) {
-	switch (v.kind) {
-	case ExactValue_Invalid:
-		return 0;
-	case ExactValue_Bool:
-	case ExactValue_String:
-		return 1;
-	case ExactValue_Integer:
-		return 2;
-	case ExactValue_Float:
-		return 3;
-	case ExactValue_Pointer:
-		return 4;
-
-	default:
-		GB_PANIC("How'd you get here? Invalid Value.kind");
-		return -1;
-	}
-}
-
-void match_exact_values(ExactValue *x, ExactValue *y) {
-	if (exact_value_order(*y) < exact_value_order(*x)) {
-		match_exact_values(y, x);
-		return;
-	}
-
-	switch (x->kind) {
-	case ExactValue_Invalid:
-		*y = *x;
-		return;
-
-	case ExactValue_Bool:
-	case ExactValue_String:
-		return;
-
-	case ExactValue_Integer:
-		switch (y->kind) {
-		case ExactValue_Integer:
-			return;
-		case ExactValue_Float:
-			// TODO(bill): Is this good enough?
-			*x = make_exact_value_float(cast(f64)x->value_integer);
-			return;
-		}
-		break;
-
-	case ExactValue_Float:
-		if (y->kind == ExactValue_Float)
-			return;
-		break;
-	}
-
-	compiler_error("How'd you get here? Invalid ExactValueKind");
-}
-
-// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
-ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
-	match_exact_values(&x, &y);
-
-	switch (x.kind) {
-	case ExactValue_Invalid:
-		return x;
-
-	case ExactValue_Bool:
-		switch (op.kind) {
-		case Token_CmpAnd: return make_exact_value_bool(x.value_bool && y.value_bool);
-		case Token_CmpOr:  return make_exact_value_bool(x.value_bool || y.value_bool);
-		case Token_And:    return make_exact_value_bool(x.value_bool & y.value_bool);
-		case Token_Or:     return make_exact_value_bool(x.value_bool | y.value_bool);
-		default: goto error;
-		}
-		break;
-
-	case ExactValue_Integer: {
-		i64 a = x.value_integer;
-		i64 b = y.value_integer;
-		i64 c = 0;
-		switch (op.kind) {
-		case Token_Add:    c = a + b;  break;
-		case Token_Sub:    c = a - b;  break;
-		case Token_Mul:    c = a * b;  break;
-		case Token_Quo:    return make_exact_value_float(fmod(cast(f64)a, cast(f64)b));
-		case Token_QuoEq:  c = a / b;  break; // NOTE(bill): Integer division
-		case Token_Mod:    c = a % b;  break;
-		case Token_And:    c = a & b;  break;
-		case Token_Or:     c = a | b;  break;
-		case Token_Xor:    c = a ^ b;  break;
-		case Token_AndNot: c = a&(~b); break;
-		case Token_Shl:    c = a << b; break;
-		case Token_Shr:    c = a >> b; break;
-		default: goto error;
-		}
-		return make_exact_value_integer(c);
-	} break;
-
-	case ExactValue_Float: {
-		f64 a = x.value_float;
-		f64 b = y.value_float;
-		switch (op.kind) {
-		case Token_Add: return make_exact_value_float(a + b);
-		case Token_Sub: return make_exact_value_float(a - b);
-		case Token_Mul: return make_exact_value_float(a * b);
-		case Token_Quo: return make_exact_value_float(a / b);
-		default: goto error;
-		}
-	} break;
-	}
-
-error:
-	ExactValue error_value = {0};
-	// gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op.kind));
-	return error_value;
-}
-
-gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { Token op = {Token_Add};        return exact_binary_operator_value(op, x, y); }
-gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { Token op = {Token_Sub};        return exact_binary_operator_value(op, x, y); }
-gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { Token op = {Token_Mul};        return exact_binary_operator_value(op, x, y); }
-gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { Token op = {Token_Quo};        return exact_binary_operator_value(op, x, y); }
-gb_inline ExactValue exact_value_shift(Token op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); }
-
-
-i32 cmp_f64(f64 a, f64 b) {
-	return (a > b) - (a < b);
-}
-
-bool compare_exact_values(Token op, ExactValue x, ExactValue y) {
-	match_exact_values(&x, &y);
-
-	switch (x.kind) {
-	case ExactValue_Invalid:
-		return false;
-
-	case ExactValue_Bool:
-		switch (op.kind) {
-		case Token_CmpEq: return x.value_bool == y.value_bool;
-		case Token_NotEq: return x.value_bool != y.value_bool;
-		}
-		break;
-
-	case ExactValue_Integer: {
-		i64 a = x.value_integer;
-		i64 b = y.value_integer;
-		switch (op.kind) {
-		case Token_CmpEq: return a == b;
-		case Token_NotEq: return a != b;
-		case Token_Lt:    return a <  b;
-		case Token_LtEq:  return a <= b;
-		case Token_Gt:    return a >  b;
-		case Token_GtEq:  return a >= b;
-		}
-	} break;
-
-	case ExactValue_Float: {
-		f64 a = x.value_float;
-		f64 b = y.value_float;
-		switch (op.kind) {
-		case Token_CmpEq: return cmp_f64(a, b) == 0;
-		case Token_NotEq: return cmp_f64(a, b) != 0;
-		case Token_Lt:    return cmp_f64(a, b) <  0;
-		case Token_LtEq:  return cmp_f64(a, b) <= 0;
-		case Token_Gt:    return cmp_f64(a, b) >  0;
-		case Token_GtEq:  return cmp_f64(a, b) >= 0;
-		}
-	} break;
-
-	case ExactValue_String: {
-		String a = x.value_string;
-		String b = y.value_string;
-		isize len = gb_min(a.len, b.len);
-		// TODO(bill): gb_memcompare is used because the strings are UTF-8
-		switch (op.kind) {
-		case Token_CmpEq: return gb_memcompare(a.text, b.text, len) == 0;
-		case Token_NotEq: return gb_memcompare(a.text, b.text, len) != 0;
-		case Token_Lt:    return gb_memcompare(a.text, b.text, len) <  0;
-		case Token_LtEq:  return gb_memcompare(a.text, b.text, len) <= 0;
-		case Token_Gt:    return gb_memcompare(a.text, b.text, len) >  0;
-		case Token_GtEq:  return gb_memcompare(a.text, b.text, len) >= 0;
-		}
-	} break;
-	}
-
-	GB_PANIC("Invalid comparison");
-	return false;
-}

+ 0 - 265
src/main.cpp

@@ -1,265 +0,0 @@
-#define VERSION_STRING "v0.0.3"
-
-#include "common.cpp"
-#include "timings.cpp"
-#include "unicode.cpp"
-#include "tokenizer.cpp"
-#include "parser.cpp"
-// #include "printer.cpp"
-#include "checker/checker.cpp"
-#include "ssa.cpp"
-#include "ssa_opt.cpp"
-#include "ssa_print.cpp"
-// #include "vm.cpp"
-
-// NOTE(bill): `name` is used in debugging and profiling modes
-i32 win32_exec_command_line_app(char *name, char *fmt, ...) {
-	STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
-	PROCESS_INFORMATION pi = {0};
-	char cmd_line[4096] = {0};
-	isize cmd_len;
-	va_list va;
-	gbTempArenaMemory tmp;
-	String16 cmd;
-	i32 exit_code = 0;
-
-	start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
-	start_info.wShowWindow = SW_SHOW;
-	start_info.hStdInput   = GetStdHandle(STD_INPUT_HANDLE);
-	start_info.hStdOutput  = GetStdHandle(STD_OUTPUT_HANDLE);
-	start_info.hStdError   = GetStdHandle(STD_ERROR_HANDLE);
-
-	va_start(va, fmt);
-	cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
-	va_end(va);
-	// gb_printf("%.*s\n", cast(int)cmd_len, cmd_line);
-
-	tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
-
-	cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1));
-
-	if (CreateProcessW(NULL, cmd.text,
-	                   NULL, NULL, true, 0, NULL, NULL,
-	                   &start_info, &pi)) {
-		WaitForSingleObject(pi.hProcess, INFINITE);
-		GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code);
-
-		CloseHandle(pi.hProcess);
-		CloseHandle(pi.hThread);
-	} else {
-		// NOTE(bill): failed to create process
-		gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
-		exit_code = -1;
-	}
-
-	gb_temp_arena_memory_end(tmp);
-	return exit_code;
-}
-
-typedef enum ArchKind {
-	ArchKind_x64,
-	ArchKind_x86,
-} ArchKind;
-
-typedef struct ArchData {
-	BaseTypeSizes sizes;
-	String llc_flags;
-	String link_flags;
-} ArchData;
-
-ArchData make_arch_data(ArchKind kind) {
-	ArchData data = {0};
-
-	switch (kind) {
-	case ArchKind_x64:
-	default:
-		data.sizes.word_size = 8;
-		data.sizes.max_align = 16;
-		data.llc_flags = str_lit("-march=x86-64 ");
-		data.link_flags = str_lit("/machine:x64 ");
-		break;
-
-	case ArchKind_x86:
-		data.sizes.word_size = 4;
-		data.sizes.max_align = 8;
-		data.llc_flags = str_lit("-march=x86 ");
-		data.link_flags = str_lit("/machine:x86 ");
-		break;
-	}
-
-	return data;
-}
-
-void usage(char *argv0) {
-	gb_printf_err("%s is a tool for managing Odin source code\n", argv0);
-	gb_printf_err("Usage:");
-	gb_printf_err("\n\t%s command [arguments]\n", argv0);
-	gb_printf_err("Commands:");
-	gb_printf_err("\n\tbuild   compile .odin file");
-	gb_printf_err("\n\trun     compile and run .odin file");
-	gb_printf_err("\n\tversion print Odin version");
-	gb_printf_err("\n\n");
-}
-
-int main(int argc, char **argv) {
-	if (argc < 2) {
-		usage(argv[0]);
-		return 1;
-	}
-
-	Timings timings = {0};
-	timings_init(&timings, str_lit("Total Time"), 128);
-	// defer (timings_destroy(&timings));
-
-#if 1
-	init_string_buffer_memory();
-	init_global_error_collector();
-
-	String module_dir = get_module_dir();
-
-	init_universal_scope();
-
-	char *init_filename = NULL;
-	bool run_output = false;
-	String arg1 = make_string_c(argv[1]);
-	if (str_eq(arg1, str_lit("run"))) {
-		run_output = true;
-		init_filename = argv[2];
-	} else if (str_eq(arg1, str_lit("build"))) {
-		init_filename = argv[2];
-	} else if (str_eq(arg1, str_lit("version"))) {
-		gb_printf("%s version %s", argv[0], VERSION_STRING);
-		return 0;
-	} else {
-		usage(argv[0]);
-		return 1;
-	}
-
-	// TODO(bill): prevent compiling without a linker
-
-	timings_start_section(&timings, str_lit("parse files"));
-
-	Parser parser = {0};
-	if (!init_parser(&parser)) {
-		return 1;
-	}
-	// defer (destroy_parser(&parser));
-
-	if (parse_files(&parser, init_filename) != ParseFile_None) {
-		return 1;
-	}
-
-
-#if 1
-	timings_start_section(&timings, str_lit("type check"));
-
-	Checker checker = {0};
-	ArchData arch_data = make_arch_data(ArchKind_x64);
-
-	init_checker(&checker, &parser, arch_data.sizes);
-	// defer (destroy_checker(&checker));
-
-	check_parsed_files(&checker);
-
-
-#endif
-#if 1
-
-	ssaGen ssa = {0};
-	if (!ssa_gen_init(&ssa, &checker)) {
-		return 1;
-	}
-	// defer (ssa_gen_destroy(&ssa));
-
-	timings_start_section(&timings, str_lit("ssa gen"));
-	ssa_gen_tree(&ssa);
-
-	timings_start_section(&timings, str_lit("ssa opt"));
-	ssa_opt_tree(&ssa);
-
-	timings_start_section(&timings, str_lit("ssa print"));
-	ssa_print_llvm_ir(&ssa);
-
-	// prof_print_all();
-
-#if 1
-	timings_start_section(&timings, str_lit("llvm-opt"));
-
-	char const *output_name = ssa.output_file.filename;
-	isize base_name_len = gb_path_extension(output_name)-1 - output_name;
-	String output = make_string(cast(u8 *)output_name, base_name_len);
-
-	i32 optimization_level = 0;
-	optimization_level = gb_clamp(optimization_level, 0, 3);
-
-	i32 exit_code = 0;
-	// For more passes arguments: http://llvm.org/docs/Passes.html
-	exit_code = win32_exec_command_line_app("llvm-opt",
-		"%.*sbin/opt %s -o %.*s.bc "
-		"-mem2reg "
-		"-memcpyopt "
-		"-die "
-		// "-dse "
-		// "-dce "
-		// "-S "
-		"",
-		LIT(module_dir),
-		output_name, LIT(output));
-	if (exit_code != 0) {
-		return exit_code;
-	}
-
-	#if 1
-	timings_start_section(&timings, str_lit("llvm-llc"));
-	// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
-	exit_code = win32_exec_command_line_app("llvm-llc",
-		"%.*sbin/llc %.*s.bc -filetype=obj -O%d "
-		"%.*s "
-		// "-debug-pass=Arguments "
-		"",
-		LIT(module_dir),
-		LIT(output),
-		optimization_level,
-		LIT(arch_data.llc_flags));
-	if (exit_code != 0) {
-		return exit_code;
-	}
-
-	timings_start_section(&timings, str_lit("msvc-link"));
-
-	gbString lib_str = gb_string_make(heap_allocator(), "Kernel32.lib");
-	// defer (gb_string_free(lib_str));
-	char lib_str_buf[1024] = {0};
-	for_array(i, parser.foreign_libraries) {
-		String lib = parser.foreign_libraries.e[i];
-		isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
-		                        " %.*s.lib", LIT(lib));
-		lib_str = gb_string_appendc(lib_str, lib_str_buf);
-	}
-
-	exit_code = win32_exec_command_line_app("msvc-link",
-		"link %.*s.obj -OUT:%.*s.exe %s "
-		"/defaultlib:libcmt "
-		"/nologo /incremental:no /opt:ref /subsystem:console "
-		" %.*s "
-		"",
-		LIT(output), LIT(output),
-		lib_str, LIT(arch_data.link_flags));
-	if (exit_code != 0) {
-		return exit_code;
-	}
-
-	// timings_print_all(&timings);
-
-	if (run_output) {
-		win32_exec_command_line_app("odin run",
-			"%.*s.exe", cast(int)base_name_len, output_name);
-	}
-	#endif
-#endif
-#endif
-#endif
-
-
-	return 0;
-}

+ 0 - 1305
src/old_vm.cpp

@@ -1,1305 +0,0 @@
-// TODO(bill): COMPLETELY REWORK THIS ENTIRE INTERPRETER
-#include "dyncall/include/dyncall.h"
-
-struct VirtualMachine;
-
-struct vmValueProc {
-	ssaProcedure *proc; // If `NULL`, use `ptr` instead and call external procedure
-	void *        ptr;
-};
-
-
-struct vmValue {
-	// NOTE(bill): Shouldn't need to store type here as the type checking
-	// has already been handled in the SSA
-	union {
-		f32            val_f32;
-		f64            val_f64;
-		void *         val_ptr;
-		i64            val_int;
-		vmValueProc    val_proc;
-	};
-	Array<vmValue> val_comp; // NOTE(bill): Will be freed through "stack"
-	Type *type;
-};
-
-vmValue vm_make_value_ptr(Type *type, void *ptr) {
-	GB_ASSERT(is_type_pointer(type));
-	vmValue v = {0};
-	v.type = default_type(type);
-	v.val_ptr = ptr;
-	return v;
-}
-vmValue vm_make_value_int(Type *type, i64 i) {
-	GB_ASSERT(is_type_integer(type) ||
-	          is_type_boolean(type) ||
-	          is_type_enum(type));
-	vmValue v = {0};
-	v.type = default_type(type);
-	v.val_int = i;
-	return v;
-}
-vmValue vm_make_value_f32(Type *type, f32 f) {
-	GB_ASSERT(is_type_f32(type));
-	vmValue v = {0};
-	v.type = default_type(type);
-	v.val_f32 = f;
-	return v;
-}
-vmValue vm_make_value_f64(Type *type, f64 f) {
-	GB_ASSERT(is_type_f64(type));
-	vmValue v = {0};
-	v.type = default_type(type);
-	v.val_f64 = f;
-	return v;
-}
-vmValue vm_make_value_comp(Type *type, gbAllocator allocator, isize count) {
-	GB_ASSERT(is_type_string(type)    ||
-	          is_type_any   (type)    ||
-	          is_type_array (type)    ||
-	          is_type_vector(type)    ||
-	          is_type_slice (type)    ||
-	          is_type_maybe (type)    ||
-	          is_type_struct(type)    ||
-	          is_type_union(type)     ||
-	          is_type_raw_union(type) ||
-	          is_type_tuple (type)    ||
-	          is_type_proc  (type));
-	vmValue v = {0};
-	v.type = default_type(type);
-	array_init_count(&v.val_comp, allocator, count);
-	return v;
-}
-
-
-
-
-
-
-struct vmFrame {
-	VirtualMachine *  vm;
-	vmFrame *         caller;
-	ssaProcedure *    curr_proc;
-	ssaBlock *        prev_block;
-	ssaBlock *        curr_block;
-	i32               instr_index; // For the current block
-
-	Map<vmValue>      values; // Key: ssaValue *
-	gbTempArenaMemory temp_arena_memory;
-	gbAllocator       stack_allocator;
-	Array<void *>     locals; // Memory to locals
-	vmValue           result;
-};
-
-struct VirtualMachine {
-	ssaModule *         module;
-	gbArena             stack_arena;
-	gbAllocator         stack_allocator;
-	gbAllocator         heap_allocator;
-	Array<vmFrame>      frame_stack;
-	Map<vmValue>        globals;             // Key: ssaValue *
-	Map<vmValue>        const_compound_lits; // Key: ssaValue *
-	vmValue             exit_value;
-};
-
-void    vm_exec_instr   (VirtualMachine *vm, ssaValue *value);
-vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value);
-void    vm_store        (VirtualMachine *vm, void *dst, vmValue val, Type *type);
-vmValue vm_load         (VirtualMachine *vm, void *ptr, Type *type);
-void    vm_print_value  (vmValue value, Type *type);
-
-void vm_jump_block(vmFrame *f, ssaBlock *target) {
-	f->prev_block = f->curr_block;
-	f->curr_block = target;
-	f->instr_index = 0;
-}
-
-
-vmFrame *vm_back_frame(VirtualMachine *vm) {
-	if (vm->frame_stack.count > 0) {
-		return &vm->frame_stack[vm->frame_stack.count-1];
-	}
-	return NULL;
-}
-
-i64 vm_type_size_of(VirtualMachine *vm, Type *type) {
-	return type_size_of(vm->module->sizes, vm->heap_allocator, type);
-}
-i64 vm_type_align_of(VirtualMachine *vm, Type *type) {
-	return type_align_of(vm->module->sizes, vm->heap_allocator, type);
-}
-i64 vm_type_offset_of(VirtualMachine *vm, Type *type, i64 index) {
-	return type_offset_of(vm->module->sizes, vm->heap_allocator, type, index);
-}
-
-
-void vm_init(VirtualMachine *vm, ssaModule *module) {
-	gb_arena_init_from_allocator(&vm->stack_arena, heap_allocator(), gb_megabytes(64));
-
-	vm->module = module;
-	vm->stack_allocator = gb_arena_allocator(&vm->stack_arena);
-	vm->heap_allocator = heap_allocator();
-	array_init(&vm->frame_stack, vm->heap_allocator);
-	map_init(&vm->globals, vm->heap_allocator);
-	map_init(&vm->const_compound_lits, vm->heap_allocator);
-
-	for_array(i, vm->module->values.entries) {
-		ssaValue *v = vm->module->values.entries[i].value;
-		switch (v->kind) {
-		case ssaValue_Global: {
-			Type *t = ssa_type(v);
-			GB_ASSERT(is_type_pointer(t));
-			i64 size  = vm_type_size_of(vm, t);
-			i64 align = vm_type_align_of(vm, t);
-			void *mem = gb_alloc_align(vm->heap_allocator, size, align);
-			if (v->Global.value != NULL && v->Global.value->kind == ssaValue_Constant) {
-				vm_store(vm, mem, vm_operand_value(vm, v->Global.value), type_deref(t));
-			}
-			map_set(&vm->globals, hash_pointer(v), vm_make_value_ptr(t, mem));
-		} break;
-		}
-	}
-
-}
-void vm_destroy(VirtualMachine *vm) {
-	array_free(&vm->frame_stack);
-	map_destroy(&vm->globals);
-	map_destroy(&vm->const_compound_lits);
-	gb_arena_free(&vm->stack_arena);
-}
-
-
-
-
-
-
-void vm_set_value(vmFrame *f, ssaValue *v, vmValue val) {
-	if (v != NULL) {
-		GB_ASSERT(ssa_type(v) != NULL);
-		map_set(&f->values, hash_pointer(v), val);
-	}
-}
-
-
-
-vmFrame *vm_push_frame(VirtualMachine *vm, ssaProcedure *proc) {
-	vmFrame frame = {0};
-
-	frame.vm          = vm;
-	frame.curr_proc   = proc;
-	frame.prev_block  = proc->blocks[0];
-	frame.curr_block  = proc->blocks[0];
-	frame.instr_index = 0;
-	frame.caller      = vm_back_frame(vm);
-	frame.stack_allocator   = vm->stack_allocator;
-	frame.temp_arena_memory = gb_temp_arena_memory_begin(&vm->stack_arena);
-
-	map_init(&frame.values, vm->heap_allocator);
-	array_init(&frame.locals, vm->heap_allocator, proc->local_count);
-	array_add(&vm->frame_stack, frame);
-	return vm_back_frame(vm);
-}
-
-void vm_pop_frame(VirtualMachine *vm) {
-	vmFrame *f = vm_back_frame(vm);
-
-	gb_temp_arena_memory_end(f->temp_arena_memory);
-	array_free(&f->locals);
-	map_destroy(&f->values);
-
-	array_pop(&vm->frame_stack);
-}
-
-
-vmValue vm_call_proc(VirtualMachine *vm, ssaProcedure *proc, Array<vmValue> values) {
-	Type *type = base_type(proc->type);
-	GB_ASSERT_MSG(type->Proc.param_count == values.count,
-	              "Incorrect number of arguments passed into procedure call!\n"
-	              "%.*s -> %td vs %td",
-	              LIT(proc->name),
-	              type->Proc.param_count, values.count);
-	Type *result_type = type->Proc.results;
-	if (result_type != NULL &&
-	    result_type->Tuple.variable_count == 1) {
-		result_type = result_type->Tuple.variables[0]->type;
-	}
-
-	if (proc->body == NULL) {
-		// GB_PANIC("TODO(bill): external procedure");
-		gb_printf_err("TODO(bill): external procedure: %.*s\n", LIT(proc->name));
-		vmValue result = {0};
-		result.type = result_type;
-		return result;
-	}
-
-	void *result_mem = NULL;
-	if (result_type != NULL) {
-		result_mem = gb_alloc_align(vm->stack_allocator,
-		                            vm_type_size_of(vm, result_type),
-		                            vm_type_align_of(vm, result_type));
-	}
-
-	gb_printf("call: %.*s\n", LIT(proc->name));
-
-	vmFrame *f = vm_push_frame(vm, proc);
-	for_array(i, proc->params) {
-		vm_set_value(f, proc->params[i], values[i]);
-	}
-
-	while (f->curr_block != NULL) {
-		ssaValue *curr_instr = f->curr_block->instrs[f->instr_index++];
-		vm_exec_instr(vm, curr_instr);
-	}
-
-
-
-
-	if (type->Proc.result_count > 0) {
-		vmValue r = f->result;
-
-		gb_printf("%.*s -> ", LIT(proc->name));
-		vm_print_value(r, result_type);
-		gb_printf("\n");
-
-		vm_store(vm, result_mem, r, result_type);
-	}
-
-	vm_pop_frame(vm);
-	if (result_mem != NULL) {
-		return vm_load(vm, result_mem, result_type);
-	}
-
-	vmValue void_result = {0};
-	return void_result;
-}
-
-
-ssaProcedure *vm_lookup_procedure(VirtualMachine *vm, String name) {
-	ssaValue *v = ssa_lookup_member(vm->module, name);
-	GB_ASSERT(v->kind == ssaValue_Proc);
-	return &v->Proc;
-}
-
-vmValue vm_call_proc_by_name(VirtualMachine *vm, String name, Array<vmValue> args) {
-	return vm_call_proc(vm, vm_lookup_procedure(vm, name), args);
-}
-
-vmValue vm_exact_value(VirtualMachine *vm, ssaValue *ptr, ExactValue value, Type *t) {
-	Type *original_type = t;
-	t = base_type(get_enum_base_type(t));
-	// i64 size = vm_type_size_of(vm, t);
-	if (is_type_boolean(t)) {
-		return vm_make_value_int(original_type, value.value_bool);
-	} else if (is_type_integer(t)) {
-		return vm_make_value_int(original_type, value.value_integer);
-	} else if (is_type_float(t)) {
-		if (t->Basic.kind == Basic_f32) {
-			return vm_make_value_f32(original_type, cast(f32)value.value_float);
-		} else if (t->Basic.kind == Basic_f64) {
-			return vm_make_value_f64(original_type, cast(f64)value.value_float);
-		}
-	} else if (is_type_pointer(t)) {
-		return vm_make_value_ptr(original_type, cast(void *)cast(intptr)value.value_pointer);
-	} else if (is_type_string(t)) {
-		vmValue result = vm_make_value_comp(original_type, vm->stack_allocator, 2);
-
-		String str = value.value_string;
-		i64 len = str.len;
-		u8 *text = gb_alloc_array(vm->heap_allocator, u8, len);
-		gb_memcopy(text, str.text, len);
-
-		result.val_comp[0] = vm_make_value_ptr(t_u8_ptr, text);
-		result.val_comp[1] = vm_make_value_int(t_int, len);
-
-		return result;
-	} else if (value.kind == ExactValue_Compound) {
-		if (ptr != NULL) {
-			vmValue *found = map_get(&vm->const_compound_lits, hash_pointer(ptr));
-			if (found != NULL)  {
-				return *found;
-			}
-		}
-
-		ast_node(cl, CompoundLit, value.value_compound);
-
-		if (is_type_array(t)) {
-			vmValue result = {0};
-
-			isize elem_count = cl->elems.count;
-			if (elem_count == 0) {
-				if (ptr != NULL) {
-					map_set(&vm->const_compound_lits, hash_pointer(ptr), result);
-				}
-				return result;
-			}
-
-			Type *type = base_type(t);
-			result = vm_make_value_comp(t, vm->heap_allocator, type->Array.count);
-			for (isize i = 0; i < elem_count; i++) {
-				TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]);
-				vmValue elem = vm_exact_value(vm, NULL, tav->value, tav->type);
-				result.val_comp[i] = elem;
-			}
-
-			if (ptr != NULL) {
-				map_set(&vm->const_compound_lits, hash_pointer(ptr), result);
-			}
-
-			return result;
-		} else if (is_type_vector(t)) {
-			vmValue result = {0};
-
-			isize elem_count = cl->elems.count;
-			if (elem_count == 0) {
-				if (ptr != NULL) {
-					map_set(&vm->const_compound_lits, hash_pointer(ptr), result);
-				}
-				return result;
-			}
-
-			Type *type = base_type(t);
-			result = vm_make_value_comp(t, vm->heap_allocator, type->Array.count);
-			for (isize i = 0; i < elem_count; i++) {
-				TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]);
-				vmValue elem = vm_exact_value(vm, NULL, tav->value, tav->type);
-				result.val_comp[i] = elem;
-			}
-
-			if (ptr != NULL) {
-				map_set(&vm->const_compound_lits, hash_pointer(ptr), result);
-			}
-
-			return result;
-		} else if (is_type_struct(t)) {
-			ast_node(cl, CompoundLit, value.value_compound);
-
-			isize value_count = t->Record.field_count;
-			vmValue result = vm_make_value_comp(t, vm->heap_allocator, value_count);
-
-			if (cl->elems.count == 0) {
-				return result;
-			}
-
-			if (cl->elems[0]->kind == AstNode_FieldValue) {
-				isize elem_count = cl->elems.count;
-				for (isize i = 0; i < elem_count; i++) {
-					ast_node(fv, FieldValue, cl->elems[i]);
-					String name = fv->field->Ident.string;
-
-					TypeAndValue *tav = type_and_value_of_expression(vm->module->info, fv->value);
-					GB_ASSERT(tav != NULL);
-
-					Selection sel = lookup_field(vm->heap_allocator, t, name, false);
-					Entity *f = t->Record.fields[sel.index[0]];
-
-					result.val_comp[f->Variable.field_index] = vm_exact_value(vm, NULL, tav->value, f->type);
-				}
-			} else {
-				for (isize i = 0; i < value_count; i++) {
-					TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]);
-					GB_ASSERT(tav != NULL);
-					Entity *f = t->Record.fields_in_src_order[i];
-					result.val_comp[f->Variable.field_index] = vm_exact_value(vm, NULL, tav->value, f->type);
-				}
-			}
-
-			return result;
-		} else {
-			GB_PANIC("TODO(bill): Other compound types\n");
-		}
-
-	} else if (value.kind == ExactValue_Invalid) {
-		vmValue zero_result = {0};
-		zero_result.type = t;
-		return zero_result;
-	} else {
-		gb_printf_err("TODO(bill): Other constant types: %s\n", type_to_string(original_type));
-	}
-
-	GB_ASSERT_MSG(t == NULL, "%s - %d", type_to_string(t), value.kind);
-	vmValue void_result = {0};
-	return void_result;
-}
-
-
-vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value) {
-	vmFrame *f = vm_back_frame(vm);
-	vmValue v = {0};
-	switch (value->kind) {
-	case ssaValue_Constant: {
-		v = vm_exact_value(vm, value, value->Constant.value, value->Constant.type);
-	} break;
-	case ssaValue_ConstantSlice: {
-		ssaValueConstant *cs = &value->ConstantSlice;
-		v = vm_make_value_comp(ssa_type(value), vm->stack_allocator, 3);
-		v.val_comp[0] = vm_operand_value(vm, cs->backing_array);
-		v.val_comp[1] = vm_make_value_int(t_int, cs->count);
-		v.val_comp[2] = vm_make_value_int(t_int, cs->count);
-	} break;
-	case ssaValue_Nil:
-		GB_PANIC("TODO(bill): ssaValue_Nil");
-		break;
-	case ssaValue_TypeName:
-		GB_PANIC("ssaValue_TypeName has no operand value");
-		break;
-	case ssaValue_Global:
-		v = *map_get(&vm->globals, hash_pointer(value));
-		break;
-	case ssaValue_Param:
-		v = *map_get(&f->values, hash_pointer(value));
-		break;
-	case ssaValue_Proc: {
-		v.type = ssa_type(value);
-		v.val_proc.proc = &value->Proc;
-		// GB_PANIC("TODO(bill): ssaValue_Proc");
-	} break;
-	case ssaValue_Block:
-		GB_PANIC("ssaValue_Block has no operand value");
-		break;
-	case ssaValue_Instr: {
-		vmValue *found = map_get(&f->values, hash_pointer(value));
-		if (found) {
-			v = *found;
-		} else {
-			GB_PANIC("Invalid instruction");
-		}
-	} break;
-	}
-
-	return v;
-}
-
-void vm_store_integer(VirtualMachine *vm, void *dst, vmValue val) {
-	// TODO(bill): I assume little endian here
-	GB_ASSERT(dst != NULL);
-	Type *type = val.type;
-	GB_ASSERT_MSG(is_type_integer(type) || is_type_boolean(type),
-	              "\nExpected integer/boolean, got %s (%s)",
-	              type_to_string(type),
-	              type_to_string(base_type(type)));
-	i64 size = vm_type_size_of(vm, type);
-	gb_memcopy(dst, &val.val_int, size);
-}
-
-void vm_store_pointer(VirtualMachine *vm, void *dst, vmValue val) {
-	// TODO(bill): I assume little endian here
-	GB_ASSERT(dst != NULL);
-	GB_ASSERT(is_type_pointer(val.type));
-	gb_memcopy(dst, &val.val_ptr, vm_type_size_of(vm, t_rawptr));
-}
-
-
-void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) {
-	i64 size = vm_type_size_of(vm, type);
-	Type *original_type = type;
-	// NOTE(bill): enums are pretty much integers
-	type = base_type(get_enum_base_type(type));
-
-	switch (type->kind) {
-	case Type_Basic:
-		switch (type->Basic.kind) {
-		case Basic_bool:
-		case Basic_i8:
-		case Basic_u8:
-		case Basic_i16:
-		case Basic_u16:
-		case Basic_i32:
-		case Basic_u32:
-		case Basic_i64:
-		case Basic_u64:
-		case Basic_int:
-		case Basic_uint:
-			vm_store_integer(vm, dst, val);
-			break;
-		case Basic_f32:
-			*cast(f32 *)dst = val.val_f32;
-			break;
-		case Basic_f64:
-			*cast(f64 *)dst = val.val_f64;
-			break;
-		case Basic_rawptr:
-			vm_store_pointer(vm, dst, val); // NOTE(bill): A pointer can be treated as an integer
-			break;
-		case Basic_string: {
-			i64 word_size = vm_type_size_of(vm, t_int);
-
-			u8 *mem = cast(u8 *)dst;
-			vm_store_pointer(vm, mem+0*word_size, val.val_comp[0]);
-			vm_store_integer(vm, mem+1*word_size, val.val_comp[1]);
-		} break;
-		case Basic_any: {
-			i64 word_size = vm_type_size_of(vm, t_int);
-
-			u8 *mem = cast(u8 *)dst;
-			vm_store_pointer(vm, mem+0*word_size, val.val_comp[0]);
-			vm_store_pointer(vm, mem+1*word_size, val.val_comp[1]);
-		} break;
-		default:
-			gb_printf_err("TODO(bill): other basic types for `vm_store` %s\n", type_to_string(type));
-			break;
-		}
-		break;
-
-	case Type_Pointer:
-		vm_store_pointer(vm, dst, val);
-		break;
-
-	case Type_Record: {
-		u8 *mem = cast(u8 *)dst;
-		gb_zero_size(mem, size);
-
-		if (is_type_struct(type)) {
-			GB_ASSERT_MSG(type->Record.field_count >= val.val_comp.count,
-			              "%td vs %td",
-			              type->Record.field_count, val.val_comp.count);
-
-			isize field_count = gb_min(val.val_comp.count, type->Record.field_count);
-
-			for (isize i = 0; i < field_count; i++) {
-				Entity *f = type->Record.fields[i];
-				i64 offset = vm_type_offset_of(vm, type, i);
-				vm_store(vm, mem+offset, val.val_comp[i], f->type);
-			}
-		} else if (is_type_union(type)) {
-			GB_ASSERT(val.val_comp.count == 2);
-			i64 word_size = vm_type_size_of(vm, t_int);
-			i64 size_of_union = vm_type_size_of(vm, type) - word_size;
-			for (isize i = 0; i < size_of_union; i++) {
-				mem[i] = cast(u8)val.val_comp[0].val_comp[i].val_int;
-			}
-			vm_store_integer(vm, mem + size_of_union, val.val_comp[1]);
-
-		} else if (is_type_raw_union(type)) {
-			GB_ASSERT(val.val_comp.count == 1);
-			i64 word_size = vm_type_size_of(vm, t_int);
-			i64 size_of_union = vm_type_size_of(vm, type) - word_size;
-			for (isize i = 0; i < size_of_union; i++) {
-				mem[i] = cast(u8)val.val_comp[0].val_comp[i].val_int;
-			}
-		} else {
-			GB_PANIC("Unknown record type: %s", type_to_string(type));
-		}
-	} break;
-
-	case Type_Tuple: {
-		u8 *mem = cast(u8 *)dst;
-
-		GB_ASSERT_MSG(type->Tuple.variable_count >= val.val_comp.count,
-		              "%td vs %td",
-		              type->Tuple.variable_count, val.val_comp.count);
-
-		isize variable_count = gb_min(val.val_comp.count, type->Tuple.variable_count);
-
-		for (isize i = 0; i < variable_count; i++) {
-			Entity *f = type->Tuple.variables[i];
-			void *ptr = mem + vm_type_offset_of(vm, type, i);
-			vm_store(vm, ptr, val.val_comp[i], f->type);
-		}
-	} break;
-
-	case Type_Array: {
-		Type *elem_type = type->Array.elem;
-		u8 *mem = cast(u8 *)dst;
-		i64 elem_size = vm_type_size_of(vm, elem_type);
-		i64 elem_count = gb_min(val.val_comp.count, type->Array.count);
-
-		for (i64 i = 0; i < elem_count; i++) {
-			vm_store(vm, mem + i*elem_size, val.val_comp[i], elem_type);
-		}
-	} break;
-
-	case Type_Vector: {
-		Type *elem_type = type->Array.elem;
-		GB_ASSERT_MSG(!is_type_boolean(elem_type), "TODO(bill): Booleans of vectors");
-		u8 *mem = cast(u8 *)dst;
-		i64 elem_size = vm_type_size_of(vm, elem_type);
-		i64 elem_count = gb_min(val.val_comp.count, type->Array.count);
-
-		for (i64 i = 0; i < elem_count; i++) {
-			vm_store(vm, mem + i*elem_size, val.val_comp[i], elem_type);
-		}
-	} break;
-
-	case Type_Slice: {
-		i64 word_size = vm_type_size_of(vm, t_int);
-
-		u8 *mem = cast(u8 *)dst;
-		vm_store_pointer(vm, mem+0*word_size, val.val_comp[0]);
-		vm_store_integer(vm, mem+1*word_size, val.val_comp[1]);
-		vm_store_integer(vm, mem+2*word_size, val.val_comp[2]);
-	} break;
-
-	default:
-		gb_printf_err("TODO(bill): other types for `vm_store` %s\n", type_to_string(type));
-		break;
-	}
-}
-
-vmValue vm_load_integer(VirtualMachine *vm, void *ptr, Type *type) {
-	// TODO(bill): I assume little endian here
-	vmValue v = {0};
-	v.type = type;
-	GB_ASSERT(is_type_integer(type) || is_type_boolean(type));
-	// NOTE(bill): Only load the needed amount
-	gb_memcopy(&v.val_int, ptr, vm_type_size_of(vm, type));
-	return v;
-}
-
-vmValue vm_load_pointer(VirtualMachine *vm, void *ptr, Type *type) {
-	// TODO(bill): I assume little endian here
-	vmValue v = {0};
-	v.type = type;
-	GB_ASSERT(is_type_pointer(type));
-	// NOTE(bill): Only load the needed amount
-	gb_memcopy(&v.val_int, ptr, vm_type_size_of(vm, type));
-	return v;
-}
-
-
-vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) {
-	i64 size = vm_type_size_of(vm, type);
-	Type *original_type = type;
-	type = base_type(get_enum_base_type(type));
-
-	switch (type->kind) {
-	case Type_Basic:
-		switch (type->Basic.kind) {
-		case Basic_bool:
-		case Basic_i8:
-		case Basic_u8:
-		case Basic_i16:
-		case Basic_u16:
-		case Basic_i32:
-		case Basic_u32:
-		case Basic_i64:
-		case Basic_u64:
-		case Basic_int:
-		case Basic_uint:
-			return vm_load_integer(vm, ptr, original_type);
-		case Basic_f32:
-			return vm_make_value_f32(original_type, *cast(f32 *)ptr);
-		case Basic_f64:
-			return vm_make_value_f64(original_type, *cast(f64 *)ptr);
-		case Basic_rawptr:
-			return vm_load_pointer(vm, ptr, original_type);
-
-
-		case Basic_string: {
-			u8 *mem = cast(u8 *)ptr;
-			i64 word_size = vm_type_size_of(vm, t_int);
-			vmValue result = vm_make_value_comp(type, vm->stack_allocator, 2);
-			result.val_comp[0] = vm_load_pointer(vm, mem+0*word_size, t_u8_ptr);
-			result.val_comp[1] = vm_load_integer(vm, mem+1*word_size, t_int);
-			return result;
-		} break;
-
-		default:
-			GB_PANIC("TODO(bill): other basic types for `vm_load` %s", type_to_string(type));
-			break;
-		}
-		break;
-
-	case Type_Pointer:
-		return vm_load_pointer(vm, ptr, original_type);
-
-	case Type_Array: {
-		i64 count = type->Array.count;
-		Type *elem_type = type->Array.elem;
-		i64 elem_size = vm_type_size_of(vm, elem_type);
-
-		vmValue result = vm_make_value_comp(type, vm->stack_allocator, count);
-
-		u8 *mem = cast(u8 *)ptr;
-		for (isize i = 0; i < count; i++) {
-			i64 offset = elem_size*i;
-			vmValue val = vm_load(vm, mem+offset, elem_type);
-			result.val_comp[i] = val;
-		}
-
-		return result;
-	} break;
-
-	case Type_Slice: {
-		Type *elem_type = type->Slice.elem;
-		i64 elem_size = vm_type_size_of(vm, elem_type);
-		i64 word_size = vm_type_size_of(vm, t_int);
-
-		vmValue result = vm_make_value_comp(type, vm->stack_allocator, 3);
-
-		u8 *mem = cast(u8 *)ptr;
-		result.val_comp[0] = vm_load(vm, mem+0*word_size, t_rawptr); // data
-		result.val_comp[1] = vm_load(vm, mem+1*word_size, t_int);    // count
-		result.val_comp[2] = vm_load(vm, mem+2*word_size, t_int);    // capacity
-		return result;
-	} break;
-
-	case Type_Record: {
-		if (is_type_struct(type)) {
-			isize field_count = type->Record.field_count;
-
-			vmValue result = vm_make_value_comp(type, vm->stack_allocator, field_count);
-
-			u8 *mem = cast(u8 *)ptr;
-			for (isize i = 0; i < field_count; i++) {
-				Entity *f = type->Record.fields[i];
-				i64 offset = vm_type_offset_of(vm, type, i);
-				result.val_comp[i] = vm_load(vm, mem+offset, f->type);
-			}
-
-			return result;
-		} else if (is_type_union(type)) {
-			i64 word_size = vm_type_size_of(vm, t_int);
-			i64 size_of_union = vm_type_size_of(vm, type) - word_size;
-			u8 *mem = cast(u8 *)ptr;
-
-			vmValue result = vm_make_value_comp(type, vm->stack_allocator, 2);
-			result.val_comp[0] = vm_load(vm, mem, make_type_array(vm->stack_allocator, t_u8, size_of_union));
-			result.val_comp[1] = vm_load(vm, mem+size_of_union, t_int);
-			return result;
-		} else if (is_type_raw_union(type)) {
-			gb_printf_err("TODO(bill): load raw_union\n");
-		} else {
-			gb_printf_err("TODO(bill): load other records\n");
-		}
-	} break;
-
-	case Type_Tuple: {
-		isize count = type->Tuple.variable_count;
-
-		vmValue result = vm_make_value_comp(type, vm->stack_allocator, count);
-
-		u8 *mem = cast(u8 *)ptr;
-		for (isize i = 0; i < count; i++) {
-			Entity *f = type->Tuple.variables[i];
-			i64 offset = vm_type_offset_of(vm, type, i);
-			result.val_comp[i] = vm_load(vm, mem+offset, f->type);
-		}
-		return result;
-	} break;
-
-	default:
-		GB_PANIC("TODO(bill): other types for `vm_load` %s", type_to_string(type));
-		break;
-	}
-
-	GB_ASSERT(type == NULL);
-	vmValue void_result = {0};
-	return void_result;
-}
-
-vmValue vm_exec_binary_op(VirtualMachine *vm, Type *type, vmValue lhs, vmValue rhs, TokenKind op) {
-	vmValue result = {0};
-
-	type = base_type(type);
-	if (is_type_vector(type)) {
-		Type *elem = type->Vector.elem;
-		i64 count = type->Vector.count;
-
-		result = vm_make_value_comp(type, vm->stack_allocator, count);
-
-		for (i64 i = 0; i < count; i++) {
-			result.val_comp[i] = vm_exec_binary_op(vm, elem, lhs.val_comp[i], rhs.val_comp[i], op);
-		}
-
-		return result;
-	}
-
-	if (gb_is_between(op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) {
-		if (is_type_integer(type) || is_type_boolean(type)) {
-			// TODO(bill): Do I need to take into account the size of the integer?
-			switch (op) {
-			case Token_CmpEq: result.val_int = lhs.val_int == rhs.val_int; break;
-			case Token_NotEq: result.val_int = lhs.val_int != rhs.val_int; break;
-			case Token_Lt:    result.val_int = lhs.val_int <  rhs.val_int; break;
-			case Token_Gt:    result.val_int = lhs.val_int >  rhs.val_int; break;
-			case Token_LtEq:  result.val_int = lhs.val_int <= rhs.val_int; break;
-			case Token_GtEq:  result.val_int = lhs.val_int >= rhs.val_int; break;
-			}
-		} else if (type == t_f32) {
-			switch (op) {
-			case Token_CmpEq: result.val_f32 = lhs.val_f32 == rhs.val_f32; break;
-			case Token_NotEq: result.val_f32 = lhs.val_f32 != rhs.val_f32; break;
-			case Token_Lt:    result.val_f32 = lhs.val_f32 <  rhs.val_f32; break;
-			case Token_Gt:    result.val_f32 = lhs.val_f32 >  rhs.val_f32; break;
-			case Token_LtEq:  result.val_f32 = lhs.val_f32 <= rhs.val_f32; break;
-			case Token_GtEq:  result.val_f32 = lhs.val_f32 >= rhs.val_f32; break;
-			}
-		} else if (type == t_f64) {
-			switch (op) {
-			case Token_CmpEq: result.val_f64 = lhs.val_f64 == rhs.val_f64; break;
-			case Token_NotEq: result.val_f64 = lhs.val_f64 != rhs.val_f64; break;
-			case Token_Lt:    result.val_f64 = lhs.val_f64 <  rhs.val_f64; break;
-			case Token_Gt:    result.val_f64 = lhs.val_f64 >  rhs.val_f64; break;
-			case Token_LtEq:  result.val_f64 = lhs.val_f64 <= rhs.val_f64; break;
-			case Token_GtEq:  result.val_f64 = lhs.val_f64 >= rhs.val_f64; break;
-			}
-		} else if (is_type_string(type)) {
-			Array<vmValue> args = {0};
-			array_init_count(&args, vm->stack_allocator, 2);
-			args[0] = lhs;
-			args[1] = rhs;
-			switch (op) {
-			case Token_CmpEq: result = vm_call_proc_by_name(vm, make_string("__string_eq"), args); break;
-			case Token_NotEq: result = vm_call_proc_by_name(vm, make_string("__string_ne"), args); break;
-			case Token_Lt:    result = vm_call_proc_by_name(vm, make_string("__string_lt"), args); break;
-			case Token_Gt:    result = vm_call_proc_by_name(vm, make_string("__string_gt"), args); break;
-			case Token_LtEq:  result = vm_call_proc_by_name(vm, make_string("__string_le"), args); break;
-			case Token_GtEq:  result = vm_call_proc_by_name(vm, make_string("__string_ge"), args); break;
-			}
-		} else {
-			GB_PANIC("TODO(bill): Vector BinaryOp");
-		}
-	} else {
-		if (is_type_integer(type) || is_type_boolean(type)) {
-			switch (op) {
-			case Token_Add: result.val_int = lhs.val_int + rhs.val_int;  break;
-			case Token_Sub: result.val_int = lhs.val_int - rhs.val_int;  break;
-			case Token_And: result.val_int = lhs.val_int & rhs.val_int;  break;
-			case Token_Or:  result.val_int = lhs.val_int | rhs.val_int;  break;
-			case Token_Xor: result.val_int = lhs.val_int ^ rhs.val_int;  break;
-			case Token_Shl: result.val_int = lhs.val_int << rhs.val_int; break;
-			case Token_Shr: result.val_int = lhs.val_int >> rhs.val_int; break;
-			case Token_Mul: result.val_int = lhs.val_int * rhs.val_int;  break;
-			case Token_Not: result.val_int = lhs.val_int ^ rhs.val_int;  break;
-
-			case Token_AndNot: result.val_int = lhs.val_int & (~rhs.val_int); break;
-
-			// TODO(bill): Take into account size of integer and signedness
-			case Token_Quo: GB_PANIC("TODO(bill): BinaryOp Integer Token_Quo"); break;
-			case Token_Mod: GB_PANIC("TODO(bill): BinaryOp Integer Token_Mod"); break;
-
-			}
-		} else if (is_type_float(type)) {
-			if (type == t_f32) {
-				switch (op) {
-				case Token_Add: result.val_f32 = lhs.val_f32 + rhs.val_f32;  break;
-				case Token_Sub: result.val_f32 = lhs.val_f32 - rhs.val_f32;  break;
-				case Token_Mul: result.val_f32 = lhs.val_f32 * rhs.val_f32;  break;
-				case Token_Quo: result.val_f32 = lhs.val_f32 / rhs.val_f32;  break;
-
-				case Token_Mod: GB_PANIC("TODO(bill): BinaryOp f32 Token_Mod"); break;
-				}
-			} else if (type == t_f64) {
-				switch (op) {
-				case Token_Add: result.val_f64 = lhs.val_f64 + rhs.val_f64;  break;
-				case Token_Sub: result.val_f64 = lhs.val_f64 - rhs.val_f64;  break;
-				case Token_Mul: result.val_f64 = lhs.val_f64 * rhs.val_f64;  break;
-				case Token_Quo: result.val_f64 = lhs.val_f64 / rhs.val_f64;  break;
-
-				case Token_Mod: GB_PANIC("TODO(bill): BinaryOp f64 Token_Mod"); break;
-				}
-			}
-		} else {
-			GB_PANIC("Invalid binary op type");
-		}
-	}
-
-	return result;
-}
-
-void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
-	GB_ASSERT(value != NULL);
-	GB_ASSERT(value->kind == ssaValue_Instr);
-	ssaInstr *instr = &value->Instr;
-	vmFrame *f = vm_back_frame(vm);
-
-#if 0
-	if (instr->kind != ssaInstr_Comment) {
-		gb_printf("exec_instr: %.*s\n", LIT(ssa_instr_strings[instr->kind]));
-	}
-#endif
-
-	switch (instr->kind) {
-	case ssaInstr_StartupRuntime: {
-#if 1
-		Array<vmValue> args = {0}; // Empty
-		vm_call_proc_by_name(vm, make_string(SSA_STARTUP_RUNTIME_PROC_NAME), args); // NOTE(bill): No return value
-#endif
-	} break;
-
-	case ssaInstr_Comment:
-		break;
-
-	case ssaInstr_Local: {
-		Type *type = ssa_type(value);
-		GB_ASSERT(is_type_pointer(type));
-		isize size  = gb_max(1, vm_type_size_of(vm, type));
-		isize align = gb_max(1, vm_type_align_of(vm, type));
-		void *memory = gb_alloc_align(vm->stack_allocator, size, align);
-		GB_ASSERT(memory != NULL);
-		vm_set_value(f, value, vm_make_value_ptr(type, memory));
-		array_add(&f->locals, memory);
-	} break;
-
-	case ssaInstr_ZeroInit: {
-		Type *t = type_deref(ssa_type(instr->ZeroInit.address));
-		vmValue addr = vm_operand_value(vm, instr->ZeroInit.address);
-		void *data = addr.val_ptr;
-		i64 size = vm_type_size_of(vm, t);
-		gb_zero_size(data, size);
-	} break;
-
-	case ssaInstr_Store: {
-		vmValue addr = vm_operand_value(vm, instr->Store.address);
-		vmValue val = vm_operand_value(vm, instr->Store.value);
-		GB_ASSERT(val.type != NULL);
-		Type *t = type_deref(ssa_type(instr->Store.address));
-		vm_store(vm, addr.val_ptr, val, t);
-	} break;
-
-	case ssaInstr_Load: {
-		vmValue addr = vm_operand_value(vm, instr->Load.address);
-		Type *t = ssa_type(value);
-		vmValue v = vm_load(vm, addr.val_ptr, t);
-		vm_set_value(f, value, v);
-	} break;
-
-	case ssaInstr_ArrayElementPtr: {
-		vmValue address    = vm_operand_value(vm, instr->ArrayElementPtr.address);
-		vmValue elem_index = vm_operand_value(vm, instr->ArrayElementPtr.elem_index);
-
-		Type *t = ssa_type(instr->ArrayElementPtr.address);
-		GB_ASSERT(is_type_pointer(t));
-		i64 elem_size = vm_type_size_of(vm, type_deref(t));
-		void *ptr = cast(u8 *)address.val_ptr + elem_index.val_int*elem_size;
-		vm_set_value(f, value, vm_make_value_ptr(t, ptr));
-	} break;
-
-	case ssaInstr_StructElementPtr: {
-		vmValue address = vm_operand_value(vm, instr->StructElementPtr.address);
-		i32 elem_index  = instr->StructElementPtr.elem_index;
-
-		Type *t = ssa_type(instr->StructElementPtr.address);
-		GB_ASSERT(is_type_pointer(t));
-		i64 offset = vm_type_offset_of(vm, type_deref(t), elem_index);
-		void *ptr = cast(u8 *)address.val_ptr + offset;
-		vm_set_value(f, value, vm_make_value_ptr(t, ptr));
-	} break;
-
-	case ssaInstr_PtrOffset: {
-		Type *t = ssa_type(instr->PtrOffset.address);
-		GB_ASSERT(is_type_pointer(t));
-		i64 elem_size = vm_type_size_of(vm, type_deref(t));
-		vmValue address = vm_operand_value(vm, instr->PtrOffset.address);
-		vmValue offset  = vm_operand_value(vm, instr->PtrOffset.offset);
-
-		void *ptr = cast(u8 *)address.val_ptr + offset.val_int*elem_size;
-		vm_set_value(f, value, vm_make_value_ptr(t, ptr));
-	} break;
-
-	case ssaInstr_Phi: {
-		for_array(i, f->curr_block->preds) {
-			ssaBlock *pred = f->curr_block->preds[i];
-			if (f->prev_block == pred) {
-				vmValue edge = vm_operand_value(vm, instr->Phi.edges[i]);
-				vm_set_value(f, value, edge);
-				break;
-			}
-		}
-	} break;
-
-	case ssaInstr_ArrayExtractValue: {
-		vmValue s = vm_operand_value(vm, instr->ArrayExtractValue.address);
-		vmValue v = s.val_comp[instr->ArrayExtractValue.index];
-		vm_set_value(f, value, v);
-	} break;
-
-	case ssaInstr_StructExtractValue: {
-		vmValue s = vm_operand_value(vm, instr->StructExtractValue.address);
-		vmValue v = s.val_comp[instr->StructExtractValue.index];
-		vm_set_value(f, value, v);
-	} break;
-
-	case ssaInstr_Jump: {
-		vm_jump_block(f, instr->Jump.block);
-	} break;
-
-	case ssaInstr_If: {
-		vmValue cond = vm_operand_value(vm, instr->If.cond);
-		if (cond.val_int != 0) {
-			vm_jump_block(f, instr->If.true_block);
-		} else {
-			vm_jump_block(f, instr->If.false_block);
-		}
-	} break;
-
-	case ssaInstr_Return: {
-		Type *return_type = NULL;
-		vmValue result = {0};
-
-		if (instr->Return.value != NULL) {
-			return_type = ssa_type(instr->Return.value);
-			result = vm_operand_value(vm, instr->Return.value);
-		}
-
-		f->result = result;
-		f->curr_block = NULL;
-		f->instr_index = 0;
-		return;
-	} break;
-
-	case ssaInstr_Conv: {
-		// TODO(bill): Assuming little endian
-		vmValue dst = {0};
-		vmValue src = vm_operand_value(vm, instr->Conv.value);
-		i64 from_size = vm_type_size_of(vm, instr->Conv.from);
-		i64 to_size   = vm_type_size_of(vm, instr->Conv.to);
-		switch (instr->Conv.kind) {
-		case ssaConv_trunc:
-			gb_memcopy(&dst, &src, to_size);
-			break;
-		case ssaConv_zext:
-			gb_memcopy(&dst, &src, from_size);
-			break;
-		case ssaConv_fptrunc: {
-			GB_ASSERT(from_size > to_size);
-			GB_ASSERT(base_type(instr->Conv.from) == t_f64);
-			GB_ASSERT(base_type(instr->Conv.to) == t_f32);
-			dst.val_f32 = cast(f32)src.val_f64;
-		} break;
-		case ssaConv_fpext: {
-			GB_ASSERT(from_size < to_size);
-			GB_ASSERT(base_type(instr->Conv.from) == t_f32);
-			GB_ASSERT(base_type(instr->Conv.to) == t_f64);
-			dst.val_f64 = cast(f64)src.val_f32;
-		} break;
-		case ssaConv_fptoui: {
-			Type *from = base_type(instr->Conv.from);
-			if (from == t_f64) {
-				u64 u = cast(u64)src.val_f64;
-				vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, u));
-			} else {
-				u64 u = cast(u64)src.val_f32;
-				vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, u));
-			}
-		} break;
-		case ssaConv_fptosi: {
-			Type *from = base_type(instr->Conv.from);
-			if (from == t_f64) {
-				i64 i = cast(i64)src.val_f64;
-				vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, i));
-			} else {
-				i64 i = cast(i64)src.val_f32;
-				vm_store_integer(vm, &dst, vm_make_value_int(instr->Conv.to, i));
-			}
-		} break;
-		case ssaConv_uitofp: {
-			Type *to = base_type(instr->Conv.to);
-			if (to == t_f64) {
-				dst = vm_make_value_f64(instr->Conv.to, cast(f64)cast(u64)src.val_int);
-			} else {
-				dst = vm_make_value_f32(instr->Conv.to, cast(f32)cast(u64)src.val_int);
-			}
-		} break;
-		case ssaConv_sitofp: {
-			Type *to = base_type(instr->Conv.to);
-			if (to == t_f64) {
-				dst = vm_make_value_f64(instr->Conv.to, cast(f64)cast(i64)src.val_int);
-			} else {
-				dst = vm_make_value_f32(instr->Conv.to, cast(f32)cast(i64)src.val_int);
-			}
-		} break;
-
-		case ssaConv_ptrtoint:
-			dst = vm_make_value_int(instr->Conv.to, cast(i64)src.val_ptr);
-			break;
-		case ssaConv_inttoptr:
-			dst = vm_make_value_ptr(instr->Conv.to, cast(void *)src.val_int);
-			break;
-		case ssaConv_bitcast:
-			dst = src;
-			dst.type = instr->Conv.to;
-			break;
-		}
-
-		vm_set_value(f, value, dst);
-	} break;
-
-	case ssaInstr_Unreachable: {
-		GB_PANIC("Unreachable");
-	} break;
-
-	case ssaInstr_BinaryOp: {
-		ssaInstrBinaryOp *bo = &instr->BinaryOp;
-		Type *type = ssa_type(bo->left);
-		vmValue lhs = vm_operand_value(vm, bo->left);
-		vmValue rhs = vm_operand_value(vm, bo->right);
-		vmValue v = vm_exec_binary_op(vm, type, lhs, rhs, bo->op);
-		vm_set_value(f, value, v);
-	} break;
-
-	case ssaInstr_Call: {
-		Array<vmValue> args = {0};
-		array_init(&args, f->stack_allocator, instr->Call.arg_count);
-		for (isize i = 0; i < instr->Call.arg_count; i++) {
-			array_add(&args, vm_operand_value(vm, instr->Call.args[i]));
-		}
-		vmValue proc = vm_operand_value(vm, instr->Call.value);
-		if (proc.val_proc.proc != NULL) {
-			vmValue result = vm_call_proc(vm, proc.val_proc.proc, args);
-			vm_set_value(f, value, result);
-		} else {
-			GB_PANIC("TODO(bill): external procedure calls");
-		}
-
-	} break;
-
-	case ssaInstr_Select: {
-		vmValue v = {0};
-		vmValue cond = vm_operand_value(vm, instr->Select.cond);
-		if (cond.val_int != 0) {
-			v = vm_operand_value(vm, instr->Select.true_value);
-		} else {
-			v = vm_operand_value(vm, instr->Select.false_value);
-		}
-
-		vm_set_value(f, value, v);
-	} break;
-
-	case ssaInstr_VectorExtractElement: {
-		vmValue vector = vm_operand_value(vm, instr->VectorExtractElement.vector);
-		vmValue index  = vm_operand_value(vm, instr->VectorExtractElement.index);
-		vmValue v = vector.val_comp[index.val_int];
-		vm_set_value(f, value, v);
-	} break;
-
-	case ssaInstr_VectorInsertElement: {
-		vmValue vector = vm_operand_value(vm, instr->VectorInsertElement.vector);
-		vmValue elem   = vm_operand_value(vm, instr->VectorInsertElement.elem);
-		vmValue index  = vm_operand_value(vm, instr->VectorInsertElement.index);
-		vector.val_comp[index.val_int] = elem;
-	} break;
-
-	case ssaInstr_VectorShuffle: {
-		ssaValueVectorShuffle *vs = &instr->VectorShuffle;
-		vmValue old_vector = vm_operand_value(vm, instr->VectorShuffle.vector);
-		vmValue new_vector = vm_make_value_comp(ssa_type(value), vm->stack_allocator, vs->index_count);
-
-		for (i32 i = 0; i < vs->index_count; i++) {
-			new_vector.val_comp[i] = old_vector.val_comp[vs->indices[i]];
-		}
-
-		vm_set_value(f, value, new_vector);
-	} break;
-
-	case ssaInstr_BoundsCheck: {
-		ssaInstrBoundsCheck *bc = &instr->BoundsCheck;
-		Array<vmValue> args = {0};
-		array_init(&args, vm->stack_allocator, 5);
-		array_add(&args, vm_exact_value(vm, NULL, make_exact_value_string(bc->pos.file), t_string));
-		array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.line), t_int));
-		array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.column), t_int));
-		array_add(&args, vm_operand_value(vm, bc->index));
-		array_add(&args, vm_operand_value(vm, bc->len));
-
-		vm_call_proc_by_name(vm, make_string("__bounds_check_error"), args);
-	} break;
-
-	case ssaInstr_SliceBoundsCheck: {
-		ssaInstrSliceBoundsCheck *bc = &instr->SliceBoundsCheck;
-		Array<vmValue> args = {0};
-
-		array_init(&args, vm->stack_allocator, 7);
-		array_add(&args, vm_exact_value(vm, NULL, make_exact_value_string(bc->pos.file), t_string));
-		array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.line), t_int));
-		array_add(&args, vm_exact_value(vm, NULL, make_exact_value_integer(bc->pos.column), t_int));
-		array_add(&args, vm_operand_value(vm, bc->low));
-		array_add(&args, vm_operand_value(vm, bc->high));
-		if (!bc->is_substring) {
-			array_add(&args, vm_operand_value(vm, bc->max));
-			vm_call_proc_by_name(vm, make_string("__slice_expr_error"), args);
-		} else {
-			vm_call_proc_by_name(vm, make_string("__substring_expr_error"), args);
-		}
-	} break;
-
-	default: {
-		GB_PANIC("<unknown instr> %d\n", instr->kind);
-	} break;
-	}
-}
-
-
-
-void vm_print_value(vmValue value, Type *type) {
-	type = base_type(type);
-	if (is_type_string(type)) {
-		vmValue data  = value.val_comp[0];
-		vmValue count = value.val_comp[1];
-		gb_printf("`%.*s`", cast(int)count.val_int, cast(u8 *)data.val_ptr);
-	} else if (is_type_boolean(type)) {
-		if (value.val_int != 0) {
-			gb_printf("true");
-		} else {
-			gb_printf("false");
-		}
-	} else if (is_type_integer(type)) {
-		gb_printf("%lld", cast(i64)value.val_int);
-	} else if (type == t_f32) {
-		gb_printf("%f", value.val_f32);
-	} else if (type == t_f64) {
-		gb_printf("%f", value.val_f64);
-	} else if (is_type_pointer(type)) {
-		gb_printf("0x%08x", value.val_ptr);
-	} else if (is_type_array(type)) {
-		gb_printf("[");
-		for_array(i, value.val_comp) {
-			if (i > 0) {
-				gb_printf(", ");
-			}
-			vm_print_value(value.val_comp[i], type->Array.elem);
-		}
-		gb_printf("]");
-	} else if (is_type_vector(type)) {
-		gb_printf("<");
-		for_array(i, value.val_comp) {
-			if (i > 0) {
-				gb_printf(", ");
-			}
-			vm_print_value(value.val_comp[i], type->Vector.elem);
-		}
-		gb_printf(">");
-	} else if (is_type_slice(type)) {
-		gb_printf("[");
-		for_array(i, value.val_comp) {
-			if (i > 0) {
-				gb_printf(", ");
-			}
-			vm_print_value(value.val_comp[i], type->Slice.elem);
-		}
-		gb_printf("]");
-	} else if (is_type_maybe(type)) {
-		if (value.val_comp[1].val_int != 0) {
-			gb_printf("?");
-			vm_print_value(value.val_comp[0], type->Maybe.elem);
-		} else {
-			gb_printf("nil");
-		}
-	} else if (is_type_struct(type)) {
-		if (value.val_comp.count == 0) {
-			gb_printf("nil");
-		} else {
-			gb_printf("{");
-			for_array(i, value.val_comp) {
-				if (i > 0) {
-					gb_printf(", ");
-				}
-				vm_print_value(value.val_comp[i], type->Record.fields[i]->type);
-			}
-			gb_printf("}");
-		}
-	} else if (is_type_tuple(type)) {
-		if (value.val_comp.count != 1) {
-			gb_printf("(");
-		}
-		for_array(i, value.val_comp) {
-			if (i > 0) {
-				gb_printf(", ");
-			}
-			vm_print_value(value.val_comp[i], type->Tuple.variables[i]->type);
-		}
-		if (value.val_comp.count != 1) {
-			gb_printf(")");
-		}
-	}
-}

+ 0 - 3243
src/parser.cpp

@@ -1,3243 +0,0 @@
-typedef struct AstNode AstNode;
-typedef struct Scope Scope;
-typedef struct DeclInfo DeclInfo;
-
-typedef enum ParseFileError {
-	ParseFile_None,
-
-	ParseFile_WrongExtension,
-	ParseFile_InvalidFile,
-	ParseFile_EmptyFile,
-	ParseFile_Permission,
-	ParseFile_NotFound,
-	ParseFile_InvalidToken,
-
-	ParseFile_Count,
-} ParseFileError;
-
-typedef Array(AstNode *) AstNodeArray;
-
-typedef struct AstFile {
-	i32            id;
-	gbArena        arena;
-	Tokenizer      tokenizer;
-	Array(Token)   tokens;
-	isize          curr_token_index;
-	Token          curr_token;
-	Token          prev_token; // previous non-comment
-
-	// >= 0: In Expression
-	// <  0: In Control Clause
-	// NOTE(bill): Used to prevent type literals in control clauses
-	isize          expr_level;
-
-	AstNodeArray   decls;
-	bool            is_global_scope;
-
-	AstNode *      curr_proc;
-	isize          scope_level;
-	Scope *        scope;       // NOTE(bill): Created in checker
-	DeclInfo *     decl_info;   // NOTE(bill): Created in checker
-
-	// TODO(bill): Error recovery
-#define PARSER_MAX_FIX_COUNT 6
-	isize    fix_count;
-	TokenPos fix_prev_pos;
-} AstFile;
-
-typedef struct ImportedFile {
-	String   path;
-	String   rel_path;
-	TokenPos pos; // #import
-} ImportedFile;
-
-typedef struct Parser {
-	String              init_fullpath;
-	Array(AstFile)      files;
-	Array(ImportedFile) imports;
-	gbAtomic32          import_index;
-	Array(String)       foreign_libraries;
-	isize               total_token_count;
-	gbMutex             mutex;
-} Parser;
-
-typedef enum ProcTag {
-	ProcTag_bounds_check    = GB_BIT(0),
-	ProcTag_no_bounds_check = GB_BIT(1),
-
-	ProcTag_foreign         = GB_BIT(10),
-	ProcTag_link_name       = GB_BIT(11),
-	ProcTag_inline          = GB_BIT(12),
-	ProcTag_no_inline       = GB_BIT(13),
-	ProcTag_dll_import      = GB_BIT(14),
-	ProcTag_dll_export      = GB_BIT(15),
-
-	ProcTag_stdcall         = GB_BIT(16),
-	ProcTag_fastcall        = GB_BIT(17),
-	// ProcTag_cdecl           = GB_BIT(18),
-} ProcTag;
-
-typedef enum VarDeclTag {
-	VarDeclTag_thread_local = GB_BIT(0),
-} VarDeclTag;
-
-typedef enum StmtStateFlag {
-	StmtStateFlag_bounds_check    = GB_BIT(0),
-	StmtStateFlag_no_bounds_check = GB_BIT(1),
-} StmtStateFlag;
-
-
-typedef enum CallExprKind {
-	CallExpr_Prefix,  // call(...)
-	CallExpr_Postfix, // a'call
-	CallExpr_Infix,   // a ''call b
-} CallExprKind;
-
-AstNodeArray make_ast_node_array(AstFile *f) {
-	AstNodeArray a;
-	array_init(&a, gb_arena_allocator(&f->arena));
-	return a;
-}
-
-
-#define AST_NODE_KINDS \
-	AST_NODE_KIND(BasicLit, "basic literal", Token) \
-	AST_NODE_KIND(Ident,    "identifier",    Token) \
-	AST_NODE_KIND(Ellipsis, "ellipsis", struct { \
-		Token token; \
-		AstNode *expr; \
-	}) \
-	AST_NODE_KIND(ProcLit, "procedure literal", struct { \
-		AstNode *type; \
-		AstNode *body; \
-		u64 tags;      \
-	}) \
-	AST_NODE_KIND(CompoundLit, "compound literal", struct { \
-		AstNode *type; \
-		AstNodeArray elems; \
-		Token open, close; \
-	}) \
-AST_NODE_KIND(_ExprBegin,  "",  i32) \
-	AST_NODE_KIND(BadExpr,      "bad expression",         struct { Token begin, end; }) \
-	AST_NODE_KIND(TagExpr,      "tag expression",         struct { Token token, name; AstNode *expr; }) \
-	AST_NODE_KIND(RunExpr,      "run expression",         struct { Token token, name; AstNode *expr; }) \
-	AST_NODE_KIND(UnaryExpr,    "unary expression",       struct { Token op; AstNode *expr; }) \
-	AST_NODE_KIND(BinaryExpr,   "binary expression",      struct { Token op; AstNode *left, *right; } ) \
-	AST_NODE_KIND(ParenExpr,    "parentheses expression", struct { AstNode *expr; Token open, close; }) \
-	AST_NODE_KIND(SelectorExpr, "selector expression",    struct { Token token; AstNode *expr, *selector; }) \
-	AST_NODE_KIND(IndexExpr,    "index expression",       struct { AstNode *expr, *index; Token open, close; }) \
-	AST_NODE_KIND(DerefExpr,    "dereference expression", struct { Token op; AstNode *expr; }) \
-	AST_NODE_KIND(DemaybeExpr,  "demaybe expression",     struct { Token op; AstNode *expr; }) \
-	AST_NODE_KIND(CallExpr,     "call expression", struct { \
-		AstNode *proc; \
-		AstNodeArray args; \
-		Token open, close; \
-		Token ellipsis; \
-		CallExprKind kind; \
-	}) \
-	AST_NODE_KIND(SliceExpr, "slice expression", struct { \
-		AstNode *expr; \
-		Token open, close; \
-		AstNode *low, *high, *max; \
-		bool triple_indexed; \
-	}) \
-	AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
-AST_NODE_KIND(_ExprEnd,       "", i32) \
-AST_NODE_KIND(_StmtBegin,     "", i32) \
-	AST_NODE_KIND(BadStmt,    "bad statement",                 struct { Token begin, end; }) \
-	AST_NODE_KIND(EmptyStmt,  "empty statement",               struct { Token token; }) \
-	AST_NODE_KIND(ExprStmt,   "expression statement",          struct { AstNode *expr; } ) \
-	AST_NODE_KIND(IncDecStmt, "increment/decrement statement", struct { Token op; AstNode *expr; }) \
-	AST_NODE_KIND(TagStmt,    "tag statement", struct { \
-		Token token; \
-		Token name; \
-		AstNode *stmt; \
-	}) \
-	AST_NODE_KIND(AssignStmt, "assign statement", struct { \
-		Token op; \
-		AstNodeArray lhs, rhs; \
-	}) \
-AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
-	AST_NODE_KIND(BlockStmt, "block statement", struct { \
-		AstNodeArray stmts; \
-		Token open, close; \
-	}) \
-	AST_NODE_KIND(IfStmt, "if statement", struct { \
-		Token token; \
-		AstNode *init; \
-		AstNode *cond; \
-		AstNode *body; \
-		AstNode *else_stmt; \
-	}) \
-	AST_NODE_KIND(ReturnStmt, "return statement", struct { \
-		Token token; \
-		AstNodeArray results; \
-	}) \
-	AST_NODE_KIND(ForStmt, "for statement", struct { \
-		Token token; \
-		AstNode *init, *cond, *post; \
-		AstNode *body; \
-	}) \
-	AST_NODE_KIND(CaseClause, "case clause", struct { \
-		Token token; \
-		AstNodeArray list, stmts; \
-	}) \
-	AST_NODE_KIND(MatchStmt, "match statement", struct { \
-		Token token; \
-		AstNode *init, *tag; \
-		AstNode *body; \
-	}) \
-	AST_NODE_KIND(TypeMatchStmt, "type match statement", struct { \
-		Token token; \
-		AstNode *tag, *var; \
-		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; }) \
-	AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
-		Token string; \
-		AstNode *operand; \
-	}) \
-	AST_NODE_KIND(AsmStmt,    "assembly statement", struct { \
-		Token token; \
-		bool is_volatile; \
-		Token open, close; \
-		Token code_string; \
-		AstNode *output_list; \
-		AstNode *input_list; \
-		AstNode *clobber_list; \
-		isize output_count, input_count, clobber_count; \
-	}) \
-	AST_NODE_KIND(PushAllocator, "push_allocator statement", struct { \
-		Token token; \
-		AstNode *expr; \
-		AstNode *body; \
-	}) \
-	AST_NODE_KIND(PushContext, "push_context statement", struct { \
-		Token token; \
-		AstNode *expr; \
-		AstNode *body; \
-	}) \
-\
-AST_NODE_KIND(_ComplexStmtEnd, "", i32) \
-AST_NODE_KIND(_StmtEnd,        "", i32) \
-AST_NODE_KIND(_DeclBegin,      "", i32) \
-	AST_NODE_KIND(BadDecl,  "bad declaration", struct { Token begin, end; }) \
-	AST_NODE_KIND(VarDecl,  "variable declaration", struct { \
-			u64          tags; \
-			bool          is_using; \
-			AstNodeArray names; \
-			AstNode *    type; \
-			AstNodeArray values; \
-			AstNode *    note; \
-	}) \
-	AST_NODE_KIND(ConstDecl,  "constant declaration", struct { \
-			u64          tags; \
-			AstNodeArray names; \
-			AstNode *    type; \
-			AstNodeArray values; \
-			AstNode *    note; \
-	}) \
-	AST_NODE_KIND(ProcDecl, "procedure declaration", struct { \
-			AstNode *name;         \
-			AstNode *type;         \
-			AstNode *body;         \
-			u64      tags;         \
-			String   foreign_name; \
-			String   link_name;    \
-			AstNode *note;          \
-	}) \
-	AST_NODE_KIND(TypeDecl,   "type declaration",   struct { \
-		Token token; \
-		AstNode *name, *type; \
-		AstNode *note; \
-	}) \
-	AST_NODE_KIND(ImportDecl, "import declaration", struct { \
-		Token token, relpath; \
-		String fullpath;      \
-		Token import_name;    \
-		bool is_load;          \
-		AstNode *note;        \
-	}) \
-	AST_NODE_KIND(ForeignLibrary, "foreign library", struct { \
-		Token token, filepath; \
-		bool is_system; \
-	}) \
-AST_NODE_KIND(_DeclEnd,   "", i32) \
-AST_NODE_KIND(_TypeBegin, "", i32) \
-	AST_NODE_KIND(Parameter, "parameter", struct { \
-		AstNodeArray names; \
-		AstNode *type; \
-		bool is_using; \
-	}) \
-	AST_NODE_KIND(ProcType, "procedure type", struct { \
-		Token token;          \
-		AstNodeArray params;  \
-		AstNodeArray results; \
-	}) \
-	AST_NODE_KIND(PointerType, "pointer type", struct { \
-		Token token; \
-		AstNode *type; \
-	}) \
-	AST_NODE_KIND(MaybeType, "maybe type", struct { \
-		Token token; \
-		AstNode *type; \
-	}) \
-	AST_NODE_KIND(ArrayType, "array type", struct { \
-		Token token; \
-		AstNode *count; \
-		AstNode *elem; \
-	}) \
-	AST_NODE_KIND(VectorType, "vector type", struct { \
-		Token token; \
-		AstNode *count; \
-		AstNode *elem; \
-	}) \
-	AST_NODE_KIND(StructType, "struct type", struct { \
-		Token token; \
-		AstNodeArray decls; \
-		isize decl_count; \
-		bool is_packed; \
-		bool is_ordered; \
-	}) \
-	AST_NODE_KIND(UnionType, "union type", struct { \
-		Token token; \
-		AstNodeArray decls; \
-		isize decl_count; \
-	}) \
-	AST_NODE_KIND(RawUnionType, "raw union type", struct { \
-		Token token; \
-		AstNodeArray decls; \
-		isize decl_count; \
-	}) \
-	AST_NODE_KIND(EnumType, "enum type", struct { \
-		Token token; \
-		AstNode *base_type; \
-		AstNodeArray fields; \
-	}) \
-AST_NODE_KIND(_TypeEnd,  "", i32)
-
-typedef enum AstNodeKind {
-	AstNode_Invalid,
-#define AST_NODE_KIND(_kind_name_, ...) GB_JOIN2(AstNode_, _kind_name_),
-	AST_NODE_KINDS
-#undef AST_NODE_KIND
-	AstNode_Count,
-} AstNodeKind;
-
-String const ast_node_strings[] = {
-	{cast(u8 *)"invalid node", gb_size_of("invalid node")},
-#define AST_NODE_KIND(_kind_name_, name, ...) {cast(u8 *)name, gb_size_of(name)-1},
-	AST_NODE_KINDS
-#undef AST_NODE_KIND
-};
-
-#define AST_NODE_KIND(_kind_name_, name, ...) typedef __VA_ARGS__ GB_JOIN2(AstNode, _kind_name_);
-	AST_NODE_KINDS
-#undef AST_NODE_KIND
-
-typedef struct AstNode {
-	AstNodeKind kind;
-	// AstNode *prev, *next; // NOTE(bill): allow for Linked list
-	u32 stmt_state_flags;
-	union {
-#define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_;
-	AST_NODE_KINDS
-#undef AST_NODE_KIND
-	};
-} AstNode;
-
-
-#define ast_node(n_, Kind_, node_) GB_JOIN2(AstNode, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(AstNode_, Kind_))
-#define case_ast_node(n_, Kind_, node_) case GB_JOIN2(AstNode_, Kind_): { ast_node(n_, Kind_, node_);
-#define case_end } break;
-
-
-
-
-gb_inline bool is_ast_node_expr(AstNode *node) {
-	return gb_is_between(node->kind, AstNode__ExprBegin+1, AstNode__ExprEnd-1);
-}
-gb_inline bool is_ast_node_stmt(AstNode *node) {
-	return gb_is_between(node->kind, AstNode__StmtBegin+1, AstNode__StmtEnd-1);
-}
-gb_inline bool is_ast_node_complex_stmt(AstNode *node) {
-	return gb_is_between(node->kind, AstNode__ComplexStmtBegin+1, AstNode__ComplexStmtEnd-1);
-}
-gb_inline bool is_ast_node_decl(AstNode *node) {
-	return gb_is_between(node->kind, AstNode__DeclBegin+1, AstNode__DeclEnd-1);
-}
-gb_inline bool is_ast_node_type(AstNode *node) {
-	return gb_is_between(node->kind, AstNode__TypeBegin+1, AstNode__TypeEnd-1);
-}
-
-
-Token ast_node_token(AstNode *node) {
-	switch (node->kind) {
-	case AstNode_BasicLit:
-		return node->BasicLit;
-	case AstNode_Ident:
-		return node->Ident;
-	case AstNode_ProcLit:
-		return ast_node_token(node->ProcLit.type);
-	case AstNode_CompoundLit:
-		if (node->CompoundLit.type != NULL) {
-			return ast_node_token(node->CompoundLit.type);
-		}
-		return node->CompoundLit.open;
-	case AstNode_TagExpr:
-		return node->TagExpr.token;
-	case AstNode_RunExpr:
-		return node->RunExpr.token;
-	case AstNode_BadExpr:
-		return node->BadExpr.begin;
-	case AstNode_UnaryExpr:
-		return node->UnaryExpr.op;
-	case AstNode_BinaryExpr:
-		return ast_node_token(node->BinaryExpr.left);
-	case AstNode_ParenExpr:
-		return node->ParenExpr.open;
-	case AstNode_CallExpr:
-		return ast_node_token(node->CallExpr.proc);
-	case AstNode_SelectorExpr:
-		return ast_node_token(node->SelectorExpr.selector);
-	case AstNode_IndexExpr:
-		return node->IndexExpr.open;
-	case AstNode_SliceExpr:
-		return node->SliceExpr.open;
-	case AstNode_Ellipsis:
-		return node->Ellipsis.token;
-	case AstNode_FieldValue:
-		return node->FieldValue.eq;
-	case AstNode_DerefExpr:
-		return node->DerefExpr.op;
-	case AstNode_DemaybeExpr:
-		return node->DemaybeExpr.op;
-	case AstNode_BadStmt:
-		return node->BadStmt.begin;
-	case AstNode_EmptyStmt:
-		return node->EmptyStmt.token;
-	case AstNode_ExprStmt:
-		return ast_node_token(node->ExprStmt.expr);
-	case AstNode_TagStmt:
-		return node->TagStmt.token;
-	case AstNode_IncDecStmt:
-		return node->IncDecStmt.op;
-	case AstNode_AssignStmt:
-		return node->AssignStmt.op;
-	case AstNode_BlockStmt:
-		return node->BlockStmt.open;
-	case AstNode_IfStmt:
-		return node->IfStmt.token;
-	case AstNode_ReturnStmt:
-		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:
-		return node->BranchStmt.token;
-	case AstNode_UsingStmt:
-		return node->UsingStmt.token;
-	case AstNode_AsmStmt:
-		return node->AsmStmt.token;
-	case AstNode_PushAllocator:
-		return node->PushAllocator.token;
-	case AstNode_PushContext:
-		return node->PushContext.token;
-	case AstNode_BadDecl:
-		return node->BadDecl.begin;
-	case AstNode_VarDecl:
-		return ast_node_token(node->VarDecl.names.e[0]);
-	case AstNode_ConstDecl:
-		return ast_node_token(node->ConstDecl.names.e[0]);
-	case AstNode_ProcDecl:
-		return node->ProcDecl.name->Ident;
-	case AstNode_TypeDecl:
-		return node->TypeDecl.token;
-	case AstNode_ImportDecl:
-		return node->ImportDecl.token;
-	case AstNode_ForeignLibrary:
-		return node->ForeignLibrary.token;
-	case AstNode_Parameter: {
-		if (node->Parameter.names.count > 0) {
-			return ast_node_token(node->Parameter.names.e[0]);
-		} else {
-			return ast_node_token(node->Parameter.type);
-		}
-	}
-	case AstNode_ProcType:
-		return node->ProcType.token;
-	case AstNode_PointerType:
-		return node->PointerType.token;
-	case AstNode_MaybeType:
-		return node->MaybeType.token;
-	case AstNode_ArrayType:
-		return node->ArrayType.token;
-	case AstNode_VectorType:
-		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;
-}
-
-// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
-AstNode *make_node(AstFile *f, AstNodeKind kind) {
-	gbArena *arena = &f->arena;
-	if (gb_arena_size_remaining(arena, GB_DEFAULT_MEMORY_ALIGNMENT) <= gb_size_of(AstNode)) {
-		// NOTE(bill): If a syntax error is so bad, just quit!
-		gb_exit(1);
-	}
-	AstNode *node = gb_alloc_item(gb_arena_allocator(arena), AstNode);
-	node->kind = kind;
-	return node;
-}
-
-AstNode *make_bad_expr(AstFile *f, Token begin, Token end) {
-	AstNode *result = make_node(f, AstNode_BadExpr);
-	result->BadExpr.begin = begin;
-	result->BadExpr.end   = end;
-	return result;
-}
-
-AstNode *make_tag_expr(AstFile *f, Token token, Token name, AstNode *expr) {
-	AstNode *result = make_node(f, AstNode_TagExpr);
-	result->TagExpr.token = token;
-	result->TagExpr.name = name;
-	result->TagExpr.expr = expr;
-	return result;
-}
-
-AstNode *make_run_expr(AstFile *f, Token token, Token name, AstNode *expr) {
-	AstNode *result = make_node(f, AstNode_RunExpr);
-	result->RunExpr.token = token;
-	result->RunExpr.name = name;
-	result->RunExpr.expr = expr;
-	return result;
-}
-
-
-AstNode *make_tag_stmt(AstFile *f, Token token, Token name, AstNode *stmt) {
-	AstNode *result = make_node(f, AstNode_TagStmt);
-	result->TagStmt.token = token;
-	result->TagStmt.name = name;
-	result->TagStmt.stmt = stmt;
-	return result;
-}
-
-AstNode *make_unary_expr(AstFile *f, Token op, AstNode *expr) {
-	AstNode *result = make_node(f, AstNode_UnaryExpr);
-	result->UnaryExpr.op = op;
-	result->UnaryExpr.expr = expr;
-	return result;
-}
-
-AstNode *make_binary_expr(AstFile *f, Token op, AstNode *left, AstNode *right) {
-	AstNode *result = make_node(f, AstNode_BinaryExpr);
-
-	if (left == NULL) {
-		syntax_error(op, "No lhs expression for binary expression `%.*s`", LIT(op.string));
-		left = make_bad_expr(f, op, op);
-	}
-	if (right == NULL) {
-		syntax_error(op, "No rhs expression for binary expression `%.*s`", LIT(op.string));
-		right = make_bad_expr(f, op, op);
-	}
-
-	result->BinaryExpr.op = op;
-	result->BinaryExpr.left = left;
-	result->BinaryExpr.right = right;
-
-	return result;
-}
-
-AstNode *make_paren_expr(AstFile *f, AstNode *expr, Token open, Token close) {
-	AstNode *result = make_node(f, AstNode_ParenExpr);
-	result->ParenExpr.expr = expr;
-	result->ParenExpr.open = open;
-	result->ParenExpr.close = close;
-	return result;
-}
-
-AstNode *make_call_expr(AstFile *f, AstNode *proc, AstNodeArray args, Token open, Token close, Token ellipsis) {
-	AstNode *result = make_node(f, AstNode_CallExpr);
-	result->CallExpr.proc = proc;
-	result->CallExpr.args = args;
-	result->CallExpr.open     = open;
-	result->CallExpr.close    = close;
-	result->CallExpr.ellipsis = ellipsis;
-	return result;
-}
-
-AstNode *make_selector_expr(AstFile *f, Token token, AstNode *expr, AstNode *selector) {
-	AstNode *result = make_node(f, AstNode_SelectorExpr);
-	result->SelectorExpr.expr = expr;
-	result->SelectorExpr.selector = selector;
-	return result;
-}
-
-AstNode *make_index_expr(AstFile *f, AstNode *expr, AstNode *index, Token open, Token close) {
-	AstNode *result = make_node(f, AstNode_IndexExpr);
-	result->IndexExpr.expr = expr;
-	result->IndexExpr.index = index;
-	result->IndexExpr.open = open;
-	result->IndexExpr.close = close;
-	return result;
-}
-
-
-AstNode *make_slice_expr(AstFile *f, AstNode *expr, Token open, Token close, AstNode *low, AstNode *high, AstNode *max, bool triple_indexed) {
-	AstNode *result = make_node(f, AstNode_SliceExpr);
-	result->SliceExpr.expr = expr;
-	result->SliceExpr.open = open;
-	result->SliceExpr.close = close;
-	result->SliceExpr.low = low;
-	result->SliceExpr.high = high;
-	result->SliceExpr.max = max;
-	result->SliceExpr.triple_indexed = triple_indexed;
-	return result;
-}
-
-AstNode *make_deref_expr(AstFile *f, AstNode *expr, Token op) {
-	AstNode *result = make_node(f, AstNode_DerefExpr);
-	result->DerefExpr.expr = expr;
-	result->DerefExpr.op = op;
-	return result;
-}
-
-AstNode *make_demaybe_expr(AstFile *f, AstNode *expr, Token op) {
-	AstNode *result = make_node(f, AstNode_DemaybeExpr);
-	result->DemaybeExpr.expr = expr;
-	result->DemaybeExpr.op = op;
-	return result;
-}
-
-
-AstNode *make_basic_lit(AstFile *f, Token basic_lit) {
-	AstNode *result = make_node(f, AstNode_BasicLit);
-	result->BasicLit = basic_lit;
-	return result;
-}
-
-AstNode *make_ident(AstFile *f, Token token) {
-	AstNode *result = make_node(f, AstNode_Ident);
-	result->Ident = token;
-	return result;
-}
-
-AstNode *make_ellipsis(AstFile *f, Token token, AstNode *expr) {
-	AstNode *result = make_node(f, AstNode_Ellipsis);
-	result->Ellipsis.token = token;
-	result->Ellipsis.expr = expr;
-	return result;
-}
-
-
-AstNode *make_proc_lit(AstFile *f, AstNode *type, AstNode *body, u64 tags) {
-	AstNode *result = make_node(f, AstNode_ProcLit);
-	result->ProcLit.type = type;
-	result->ProcLit.body = body;
-	result->ProcLit.tags = tags;
-	return result;
-}
-
-AstNode *make_field_value(AstFile *f, AstNode *field, AstNode *value, Token eq) {
-	AstNode *result = make_node(f, AstNode_FieldValue);
-	result->FieldValue.field = field;
-	result->FieldValue.value = value;
-	result->FieldValue.eq = eq;
-	return result;
-}
-
-AstNode *make_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token open, Token close) {
-	AstNode *result = make_node(f, AstNode_CompoundLit);
-	result->CompoundLit.type = type;
-	result->CompoundLit.elems = elems;
-	result->CompoundLit.open = open;
-	result->CompoundLit.close = close;
-	return result;
-}
-
-AstNode *make_bad_stmt(AstFile *f, Token begin, Token end) {
-	AstNode *result = make_node(f, AstNode_BadStmt);
-	result->BadStmt.begin = begin;
-	result->BadStmt.end   = end;
-	return result;
-}
-
-AstNode *make_empty_stmt(AstFile *f, Token token) {
-	AstNode *result = make_node(f, AstNode_EmptyStmt);
-	result->EmptyStmt.token = token;
-	return result;
-}
-
-AstNode *make_expr_stmt(AstFile *f, AstNode *expr) {
-	AstNode *result = make_node(f, AstNode_ExprStmt);
-	result->ExprStmt.expr = expr;
-	return result;
-}
-
-AstNode *make_inc_dec_stmt(AstFile *f, Token op, AstNode *expr) {
-	AstNode *result = make_node(f, AstNode_IncDecStmt);
-	result->IncDecStmt.op = op;
-	result->IncDecStmt.expr = expr;
-	return result;
-}
-
-AstNode *make_assign_stmt(AstFile *f, Token op, AstNodeArray lhs, AstNodeArray rhs) {
-	AstNode *result = make_node(f, AstNode_AssignStmt);
-	result->AssignStmt.op = op;
-	result->AssignStmt.lhs = lhs;
-	result->AssignStmt.rhs = rhs;
-	return result;
-}
-
-AstNode *make_block_stmt(AstFile *f, AstNodeArray stmts, Token open, Token close) {
-	AstNode *result = make_node(f, AstNode_BlockStmt);
-	result->BlockStmt.stmts = stmts;
-	result->BlockStmt.open = open;
-	result->BlockStmt.close = close;
-	return result;
-}
-
-AstNode *make_if_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *body, AstNode *else_stmt) {
-	AstNode *result = make_node(f, AstNode_IfStmt);
-	result->IfStmt.token = token;
-	result->IfStmt.init = init;
-	result->IfStmt.cond = cond;
-	result->IfStmt.body = body;
-	result->IfStmt.else_stmt = else_stmt;
-	return result;
-}
-
-AstNode *make_return_stmt(AstFile *f, Token token, AstNodeArray results) {
-	AstNode *result = make_node(f, AstNode_ReturnStmt);
-	result->ReturnStmt.token = token;
-	result->ReturnStmt.results = results;
-	return result;
-}
-
-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;
-	return result;
-}
-
-
-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;
-}
-
-
-AstNode *make_type_match_stmt(AstFile *f, Token token, AstNode *tag, AstNode *var, AstNode *body) {
-	AstNode *result = make_node(f, AstNode_TypeMatchStmt);
-	result->TypeMatchStmt.token = token;
-	result->TypeMatchStmt.tag   = tag;
-	result->TypeMatchStmt.var   = var;
-	result->TypeMatchStmt.body  = body;
-	return result;
-}
-
-AstNode *make_case_clause(AstFile *f, Token token, AstNodeArray list, AstNodeArray stmts) {
-	AstNode *result = make_node(f, AstNode_CaseClause);
-	result->CaseClause.token = token;
-	result->CaseClause.list  = list;
-	result->CaseClause.stmts = stmts;
-	return result;
-}
-
-
-AstNode *make_defer_stmt(AstFile *f, Token token, AstNode *stmt) {
-	AstNode *result = make_node(f, AstNode_DeferStmt);
-	result->DeferStmt.token = token;
-	result->DeferStmt.stmt = stmt;
-	return result;
-}
-
-AstNode *make_branch_stmt(AstFile *f, Token token) {
-	AstNode *result = make_node(f, AstNode_BranchStmt);
-	result->BranchStmt.token = token;
-	return result;
-}
-
-AstNode *make_using_stmt(AstFile *f, Token token, AstNode *node) {
-	AstNode *result = make_node(f, AstNode_UsingStmt);
-	result->UsingStmt.token = token;
-	result->UsingStmt.node  = node;
-	return result;
-}
-
-AstNode *make_asm_operand(AstFile *f, Token string, AstNode *operand) {
-	AstNode *result = make_node(f, AstNode_AsmOperand);
-	result->AsmOperand.string  = string;
-	result->AsmOperand.operand = operand;
-	return result;
-
-}
-
-AstNode *make_asm_stmt(AstFile *f, Token token, bool is_volatile, Token open, Token close, Token code_string,
-                                 AstNode *output_list, AstNode *input_list, AstNode *clobber_list,
-                                 isize output_count, isize input_count, isize clobber_count) {
-	AstNode *result = make_node(f, AstNode_AsmStmt);
-	result->AsmStmt.token = token;
-	result->AsmStmt.is_volatile = is_volatile;
-	result->AsmStmt.open  = open;
-	result->AsmStmt.close = close;
-	result->AsmStmt.code_string = code_string;
-	result->AsmStmt.output_list = output_list;
-	result->AsmStmt.input_list = input_list;
-	result->AsmStmt.clobber_list = clobber_list;
-	result->AsmStmt.output_count = output_count;
-	result->AsmStmt.input_count = input_count;
-	result->AsmStmt.clobber_count = clobber_count;
-	return result;
-}
-
-AstNode *make_push_allocator(AstFile *f, Token token, AstNode *expr, AstNode *body) {
-	AstNode *result = make_node(f, AstNode_PushAllocator);
-	result->PushAllocator.token = token;
-	result->PushAllocator.expr = expr;
-	result->PushAllocator.body = body;
-	return result;
-}
-
-AstNode *make_push_context(AstFile *f, Token token, AstNode *expr, AstNode *body) {
-	AstNode *result = make_node(f, AstNode_PushContext);
-	result->PushContext.token = token;
-	result->PushContext.expr = expr;
-	result->PushContext.body = body;
-	return result;
-}
-
-
-
-
-AstNode *make_bad_decl(AstFile *f, Token begin, Token end) {
-	AstNode *result = make_node(f, AstNode_BadDecl);
-	result->BadDecl.begin = begin;
-	result->BadDecl.end = end;
-	return result;
-}
-
-AstNode *make_var_decl(AstFile *f, AstNodeArray names, AstNode *type, AstNodeArray values) {
-	AstNode *result = make_node(f, AstNode_VarDecl);
-	result->VarDecl.names = names;
-	result->VarDecl.type = type;
-	result->VarDecl.values = values;
-	return result;
-}
-
-AstNode *make_const_decl(AstFile *f, AstNodeArray names, AstNode *type, AstNodeArray values) {
-	AstNode *result = make_node(f, AstNode_ConstDecl);
-	result->ConstDecl.names = names;
-	result->ConstDecl.type = type;
-	result->ConstDecl.values = values;
-	return result;
-}
-
-AstNode *make_parameter(AstFile *f, AstNodeArray names, AstNode *type, bool is_using) {
-	AstNode *result = make_node(f, AstNode_Parameter);
-	result->Parameter.names = names;
-	result->Parameter.type = type;
-	result->Parameter.is_using = is_using;
-	return result;
-}
-
-AstNode *make_proc_type(AstFile *f, Token token, AstNodeArray params, AstNodeArray results) {
-	AstNode *result = make_node(f, AstNode_ProcType);
-	result->ProcType.token = token;
-	result->ProcType.params = params;
-	result->ProcType.results = results;
-	return result;
-}
-
-AstNode *make_proc_decl(AstFile *f, AstNode *name, AstNode *proc_type, AstNode *body, u64 tags, String foreign_name, String link_name) {
-	AstNode *result = make_node(f, AstNode_ProcDecl);
-	result->ProcDecl.name = name;
-	result->ProcDecl.type = proc_type;
-	result->ProcDecl.body = body;
-	result->ProcDecl.tags = tags;
-	result->ProcDecl.foreign_name = foreign_name;
-	result->ProcDecl.link_name = link_name;
-	return result;
-}
-
-AstNode *make_pointer_type(AstFile *f, Token token, AstNode *type) {
-	AstNode *result = make_node(f, AstNode_PointerType);
-	result->PointerType.token = token;
-	result->PointerType.type = type;
-	return result;
-}
-
-AstNode *make_maybe_type(AstFile *f, Token token, AstNode *type) {
-	AstNode *result = make_node(f, AstNode_MaybeType);
-	result->MaybeType.token = token;
-	result->MaybeType.type = type;
-	return result;
-}
-
-AstNode *make_array_type(AstFile *f, Token token, AstNode *count, AstNode *elem) {
-	AstNode *result = make_node(f, AstNode_ArrayType);
-	result->ArrayType.token = token;
-	result->ArrayType.count = count;
-	result->ArrayType.elem = elem;
-	return result;
-}
-
-AstNode *make_vector_type(AstFile *f, Token token, AstNode *count, AstNode *elem) {
-	AstNode *result = make_node(f, AstNode_VectorType);
-	result->VectorType.token = token;
-	result->VectorType.count = count;
-	result->VectorType.elem  = elem;
-	return result;
-}
-
-AstNode *make_struct_type(AstFile *f, Token token, AstNodeArray decls, isize decl_count, bool is_packed, bool is_ordered) {
-	AstNode *result = make_node(f, AstNode_StructType);
-	result->StructType.token = token;
-	result->StructType.decls = decls;
-	result->StructType.decl_count = decl_count;
-	result->StructType.is_packed = is_packed;
-	result->StructType.is_ordered = is_ordered;
-	return result;
-}
-
-
-AstNode *make_union_type(AstFile *f, Token token, AstNodeArray decls, isize decl_count) {
-	AstNode *result = make_node(f, AstNode_UnionType);
-	result->UnionType.token = token;
-	result->UnionType.decls = decls;
-	result->UnionType.decl_count = decl_count;
-	return result;
-}
-
-AstNode *make_raw_union_type(AstFile *f, Token token, AstNodeArray decls, isize decl_count) {
-	AstNode *result = make_node(f, AstNode_RawUnionType);
-	result->RawUnionType.token = token;
-	result->RawUnionType.decls = decls;
-	result->RawUnionType.decl_count = decl_count;
-	return result;
-}
-
-
-AstNode *make_enum_type(AstFile *f, Token token, AstNode *base_type, AstNodeArray fields) {
-	AstNode *result = make_node(f, AstNode_EnumType);
-	result->EnumType.token = token;
-	result->EnumType.base_type = base_type;
-	result->EnumType.fields = fields;
-	return result;
-}
-
-AstNode *make_type_decl(AstFile *f, Token token, AstNode *name, AstNode *type) {
-	AstNode *result = make_node(f, AstNode_TypeDecl);
-	result->TypeDecl.token = token;
-	result->TypeDecl.name = name;
-	result->TypeDecl.type = type;
-	return result;
-}
-
-AstNode *make_import_decl(AstFile *f, Token token, Token relpath, Token import_name, bool is_load) {
-	AstNode *result = make_node(f, AstNode_ImportDecl);
-	result->ImportDecl.token = token;
-	result->ImportDecl.relpath = relpath;
-	result->ImportDecl.import_name = import_name;
-	result->ImportDecl.is_load = is_load;
-	return result;
-}
-
-AstNode *make_foreign_library(AstFile *f, Token token, Token filepath, bool is_system) {
-	AstNode *result = make_node(f, AstNode_ForeignLibrary);
-	result->ForeignLibrary.token = token;
-	result->ForeignLibrary.filepath = filepath;
-	result->ForeignLibrary.is_system = is_system;
-	return result;
-}
-
-bool next_token(AstFile *f) {
-	if (f->curr_token_index+1 < f->tokens.count) {
-		if (f->curr_token.kind != Token_Comment) {
-			f->prev_token = f->curr_token;
-		}
-
-		f->curr_token_index++;
-		f->curr_token = f->tokens.e[f->curr_token_index];
-		if (f->curr_token.kind == Token_Comment) {
-			return next_token(f);
-		}
-		return true;
-	}
-	syntax_error(f->curr_token, "Token is EOF");
-	return false;
-}
-
-Token expect_token(AstFile *f, TokenKind kind) {
-	Token prev = f->curr_token;
-	if (prev.kind != kind) {
-		syntax_error(f->curr_token, "Expected `%.*s`, got `%.*s`",
-		             LIT(token_strings[kind]),
-		             LIT(token_strings[prev.kind]));
-	}
-	next_token(f);
-	return prev;
-}
-
-Token expect_token_after(AstFile *f, TokenKind kind, char *msg) {
-	Token prev = f->curr_token;
-	if (prev.kind != kind) {
-		syntax_error(f->curr_token, "Expected `%.*s` after %s, got `%.*s`",
-		             LIT(token_strings[kind]),
-		             msg,
-		             LIT(token_strings[prev.kind]));
-	}
-	next_token(f);
-	return prev;
-}
-
-
-Token expect_operator(AstFile *f) {
-	Token prev = f->curr_token;
-	if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
-		syntax_error(f->curr_token, "Expected an operator, got `%.*s`",
-		             LIT(token_strings[prev.kind]));
-	}
-	next_token(f);
-	return prev;
-}
-
-Token expect_keyword(AstFile *f) {
-	Token prev = f->curr_token;
-	if (!gb_is_between(prev.kind, Token__KeywordBegin+1, Token__KeywordEnd-1)) {
-		syntax_error(f->curr_token, "Expected a keyword, got `%.*s`",
-		             LIT(token_strings[prev.kind]));
-	}
-	next_token(f);
-	return prev;
-}
-
-bool allow_token(AstFile *f, TokenKind kind) {
-	Token prev = f->curr_token;
-	if (prev.kind == kind) {
-		next_token(f);
-		return true;
-	}
-	return false;
-}
-
-
-bool is_blank_ident(String str) {
-	if (str.len == 1) {
-		return str.text[0] == '_';
-	}
-	return false;
-}
-
-
-// NOTE(bill): Go to next statement to prevent numerous error messages popping up
-void fix_advance_to_next_stmt(AstFile *f) {
-	// TODO(bill): fix_advance_to_next_stmt
-#if 1
-	for (;;) {
-		Token t = f->curr_token;
-		switch (t.kind) {
-		case Token_EOF:
-		case Token_Semicolon:
-			return;
-
-		case Token_if:
-		case Token_return:
-		case Token_for:
-		case Token_match:
-		case Token_defer:
-		case Token_asm:
-		case Token_using:
-
-		case Token_break:
-		case Token_continue:
-		case Token_fallthrough:
-
-		case Token_push_allocator:
-		case Token_push_context:
-
-		case Token_Hash:
-		{
-			if (token_pos_are_equal(t.pos, f->fix_prev_pos) &&
-			    f->fix_count < PARSER_MAX_FIX_COUNT) {
-				f->fix_count++;
-				return;
-			}
-			if (token_pos_cmp(f->fix_prev_pos, t.pos) < 0) {
-				f->fix_prev_pos = t.pos;
-				f->fix_count = 0; // NOTE(bill): Reset
-				return;
-			}
-			// NOTE(bill): Reaching here means there is a parsing bug
-		} break;
-		}
-		next_token(f);
-	}
-#endif
-}
-
-bool expect_semicolon_after_stmt(AstFile *f, AstNode *s) {
-	if (allow_token(f, Token_Semicolon)) {
-		return true;
-	}
-
-	if (f->curr_token.pos.line != f->prev_token.pos.line) {
-		return true;
-	}
-
-	switch (f->curr_token.kind) {
-	case Token_EOF:
-	case Token_CloseBrace:
-		return true;
-	}
-
-	syntax_error(f->curr_token,
-	             "Expected `;` after %.*s, got `%.*s`",
-	             LIT(ast_node_strings[s->kind]), LIT(token_strings[f->curr_token.kind]));
-	fix_advance_to_next_stmt(f);
-	return false;
-}
-
-
-AstNode *    parse_expr(AstFile *f, bool lhs);
-AstNode *    parse_proc_type(AstFile *f);
-AstNodeArray parse_stmt_list(AstFile *f);
-AstNode *    parse_stmt(AstFile *f);
-AstNode *    parse_body(AstFile *f);
-
-AstNode *parse_identifier(AstFile *f) {
-	Token token = f->curr_token;
-	if (token.kind == Token_Identifier) {
-		next_token(f);
-	} else {
-		token.string = str_lit("_");
-		expect_token(f, Token_Identifier);
-	}
-	return make_ident(f, token);
-}
-
-AstNode *parse_tag_expr(AstFile *f, AstNode *expression) {
-	Token token = expect_token(f, Token_Hash);
-	Token name  = expect_token(f, Token_Identifier);
-	return make_tag_expr(f, token, name, expression);
-}
-
-AstNode *parse_tag_stmt(AstFile *f, AstNode *statement) {
-	Token token = expect_token(f, Token_Hash);
-	Token name  = expect_token(f, Token_Identifier);
-	return make_tag_stmt(f, token, name, statement);
-}
-
-AstNode *unparen_expr(AstNode *node) {
-	for (;;) {
-		if (node->kind != AstNode_ParenExpr)
-			return node;
-		node = node->ParenExpr.expr;
-	}
-}
-
-AstNode *parse_value(AstFile *f);
-
-AstNodeArray parse_element_list(AstFile *f) {
-	AstNodeArray elems = make_ast_node_array(f);
-
-	while (f->curr_token.kind != Token_CloseBrace &&
-	       f->curr_token.kind != Token_EOF) {
-		AstNode *elem = parse_value(f);
-		if (f->curr_token.kind == Token_Eq) {
-			Token eq = expect_token(f, Token_Eq);
-			AstNode *value = parse_value(f);
-			elem = make_field_value(f, elem, value, eq);
-		}
-
-		array_add(&elems, elem);
-
-		if (f->curr_token.kind != Token_Comma) {
-			break;
-		}
-		next_token(f);
-	}
-
-	return elems;
-}
-
-AstNode *parse_literal_value(AstFile *f, AstNode *type) {
-	AstNodeArray elems = {0};
-	Token open = expect_token(f, Token_OpenBrace);
-	f->expr_level++;
-	if (f->curr_token.kind != Token_CloseBrace) {
-		elems = parse_element_list(f);
-	}
-	f->expr_level--;
-	Token close = expect_token(f, Token_CloseBrace);
-
-	return make_compound_lit(f, type, elems, open, close);
-}
-
-AstNode *parse_value(AstFile *f) {
-	if (f->curr_token.kind == Token_OpenBrace)
-		return parse_literal_value(f, NULL);
-
-	AstNode *value = parse_expr(f, false);
-	return value;
-}
-
-AstNode *parse_identifier_or_type(AstFile *f, u32 flags);
-
-
-void check_proc_add_tag(AstFile *f, AstNode *tag_expr, u64 *tags, ProcTag tag, String tag_name) {
-	if (*tags & tag) {
-		syntax_error(ast_node_token(tag_expr), "Procedure tag already used: %.*s", LIT(tag_name));
-	}
-	*tags |= tag;
-}
-
-bool is_foreign_name_valid(String name) {
-	// TODO(bill): is_foreign_name_valid
-	if (name.len == 0)
-		return false;
-	isize offset = 0;
-	while (offset < name.len) {
-		Rune rune;
-		isize remaining = name.len - offset;
-		isize width = gb_utf8_decode(name.text+offset, remaining, &rune);
-		if (rune == GB_RUNE_INVALID && width == 1) {
-			return false;
-		} else if (rune == GB_RUNE_BOM && remaining > 0) {
-			return false;
-		}
-
-		if (offset == 0) {
-			switch (rune) {
-			case '-':
-			case '$':
-			case '.':
-			case '_':
-				break;
-			default:
-				if (!gb_char_is_alpha(cast(char)rune))
-					return false;
-				break;
-			}
-		} else {
-			switch (rune) {
-			case '-':
-			case '$':
-			case '.':
-			case '_':
-				break;
-			default:
-				if (!gb_char_is_alphanumeric(cast(char)rune)) {
-					return false;
-				}
-				break;
-			}
-		}
-
-		offset += width;
-	}
-
-	return true;
-}
-
-void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_name) {
-	// TODO(bill): Add this to procedure literals too
-	GB_ASSERT(foreign_name != NULL);
-	GB_ASSERT(link_name    != NULL);
-
-	while (f->curr_token.kind == Token_Hash) {
-		AstNode *tag_expr = parse_tag_expr(f, NULL);
-		ast_node(te, TagExpr, tag_expr);
-		String tag_name = te->name.string;
-
-		#define ELSE_IF_ADD_TAG(name) \
-		else if (str_eq(tag_name, str_lit(#name))) { \
-			check_proc_add_tag(f, tag_expr, tags, ProcTag_##name, tag_name); \
-		}
-
-		if (str_eq(tag_name, str_lit("foreign"))) {
-			check_proc_add_tag(f, tag_expr, tags, ProcTag_foreign, tag_name);
-			if (f->curr_token.kind == Token_String) {
-				*foreign_name = f->curr_token.string;
-				// TODO(bill): Check if valid string
-				if (!is_foreign_name_valid(*foreign_name)) {
-					syntax_error(ast_node_token(tag_expr), "Invalid alternative foreign procedure name: `%.*s`", LIT(*foreign_name));
-				}
-
-				next_token(f);
-			}
-		} else if (str_eq(tag_name, str_lit("link_name"))) {
-			check_proc_add_tag(f, tag_expr, tags, ProcTag_link_name, tag_name);
-			if (f->curr_token.kind == Token_String) {
-				*link_name = f->curr_token.string;
-				// TODO(bill): Check if valid string
-				if (!is_foreign_name_valid(*link_name)) {
-					syntax_error(ast_node_token(tag_expr), "Invalid alternative link procedure name `%.*s`", LIT(*link_name));
-				}
-
-				next_token(f);
-			} else {
-				expect_token(f, Token_String);
-			}
-		}
-		ELSE_IF_ADD_TAG(bounds_check)
-		ELSE_IF_ADD_TAG(no_bounds_check)
-		ELSE_IF_ADD_TAG(inline)
-		ELSE_IF_ADD_TAG(no_inline)
-		ELSE_IF_ADD_TAG(dll_import)
-		ELSE_IF_ADD_TAG(dll_export)
-		ELSE_IF_ADD_TAG(stdcall)
-		ELSE_IF_ADD_TAG(fastcall)
-		// ELSE_IF_ADD_TAG(cdecl)
-		else {
-			syntax_error(ast_node_token(tag_expr), "Unknown procedure tag");
-		}
-
-		#undef ELSE_IF_ADD_TAG
-	}
-
-	if ((*tags & ProcTag_foreign) && (*tags & ProcTag_link_name)) {
-		syntax_error(f->curr_token, "You cannot apply both #foreign and #link_name to a procedure");
-	}
-
-	if ((*tags & ProcTag_inline) && (*tags & ProcTag_no_inline)) {
-		syntax_error(f->curr_token, "You cannot apply both #inline and #no_inline to a procedure");
-	}
-
-	if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) {
-		syntax_error(f->curr_token, "You cannot apply both #bounds_check and #no_bounds_check to a procedure");
-	}
-
-	if (((*tags & ProcTag_bounds_check) || (*tags & ProcTag_no_bounds_check)) && (*tags & ProcTag_foreign)) {
-		syntax_error(f->curr_token, "You cannot apply both #bounds_check or #no_bounds_check to a procedure without a body");
-	}
-
-	if ((*tags & ProcTag_stdcall) && (*tags & ProcTag_fastcall)) {
-		syntax_error(f->curr_token, "You cannot apply one calling convention to a procedure");
-	}
-}
-
-AstNode *parse_operand(AstFile *f, bool lhs) {
-	AstNode *operand = NULL; // Operand
-	switch (f->curr_token.kind) {
-	case Token_Identifier:
-		operand = parse_identifier(f);
-		if (!lhs) {
-			// TODO(bill): Handle?
-		}
-		return operand;
-
-	case Token_Integer:
-	case Token_Float:
-	case Token_String:
-	case Token_Rune:
-		operand = make_basic_lit(f, f->curr_token);
-		next_token(f);
-		return operand;
-
-	case Token_OpenParen: {
-		Token open, close;
-		// NOTE(bill): Skip the Paren Expression
-		open = expect_token(f, Token_OpenParen);
-		f->expr_level++;
-		operand = parse_expr(f, false);
-		f->expr_level--;
-		close = expect_token(f, Token_CloseParen);
-		return make_paren_expr(f, operand, open, close);
-	}
-
-	case Token_Hash: {
-		Token token = expect_token(f, Token_Hash);
-		Token name  = expect_token(f, Token_Identifier);
-		if (str_eq(name.string, str_lit("rune"))) {
-			if (f->curr_token.kind == Token_String) {
-				Token *s = &f->curr_token;
-
-				if (gb_utf8_strnlen(s->string.text, s->string.len) != 1) {
-					syntax_error(*s, "Invalid rune literal %.*s", LIT(s->string));
-				}
-				s->kind = Token_Rune; // NOTE(bill): Change it
-			} else {
-				expect_token(f, Token_String);
-			}
-			operand = parse_operand(f, lhs);
-		} else if (str_eq(name.string, str_lit("file"))) {
-			Token token = name;
-			token.kind = Token_String;
-			token.string = token.pos.file;
-			return make_basic_lit(f, token);
-		} else if (str_eq(name.string, str_lit("line"))) {
-			Token token = name;
-			token.kind = Token_Integer;
-			char *str = gb_alloc_array(gb_arena_allocator(&f->arena), char, 20);
-			gb_i64_to_str(token.pos.line, str, 10);
-			token.string = make_string_c(str);
-			return make_basic_lit(f, token);
-		} else if (str_eq(name.string, str_lit("run"))) {
-			AstNode *expr = parse_expr(f, false);
-			operand = make_run_expr(f, token, name, expr);
-			if (unparen_expr(expr)->kind != AstNode_CallExpr) {
-				error(ast_node_token(expr), "#run can only be applied to procedure calls");
-				operand = make_bad_expr(f, token, f->curr_token);
-			}
-			warning(token, "#run is not yet implemented");
-		} else {
-			operand = make_tag_expr(f, token, name, parse_expr(f, false));
-		}
-		return operand;
-	}
-
-	// Parse Procedure Type or Literal
-	case Token_proc: {
-		AstNode *curr_proc = f->curr_proc;
-		AstNode *type = parse_proc_type(f);
-		f->curr_proc = type;
-
-		u64 tags = 0;
-		String foreign_name = {0};
-		String link_name = {0};
-		parse_proc_tags(f, &tags, &foreign_name, &link_name);
-		if (tags & ProcTag_foreign) {
-			syntax_error(f->curr_token, "#foreign cannot be applied to procedure literals");
-		}
-		if (tags & ProcTag_link_name) {
-			syntax_error(f->curr_token, "#link_name cannot be applied to procedure literals");
-		}
-
-		if (f->curr_token.kind == Token_OpenBrace) {
-			AstNode *body;
-
-			f->expr_level++;
-			body = parse_body(f);
-			f->expr_level--;
-
-			type = make_proc_lit(f, type, body, tags);
-		}
-		f->curr_proc = curr_proc;
-		return type;
-	}
-
-	default: {
-		AstNode *type = parse_identifier_or_type(f, 0);
-		if (type != NULL) {
-			// NOTE(bill): Sanity check as identifiers should be handled already
-			GB_ASSERT_MSG(type->kind != AstNode_Ident, "Type Cannot be identifier");
-			return type;
-		}
-	}
-	}
-
-	Token begin = f->curr_token;
-	syntax_error(begin, "Expected an operand");
-	fix_advance_to_next_stmt(f);
-	return make_bad_expr(f, begin, f->curr_token);
-}
-
-bool is_literal_type(AstNode *node) {
-	switch (node->kind) {
-	case AstNode_BadExpr:
-	case AstNode_Ident:
-	case AstNode_SelectorExpr:
-	case AstNode_ArrayType:
-	case AstNode_VectorType:
-	case AstNode_StructType:
-		return true;
-	}
-	return false;
-}
-
-AstNode *parse_call_expr(AstFile *f, AstNode *operand) {
-	AstNodeArray args = make_ast_node_array(f);
-	Token open_paren, close_paren;
-	Token ellipsis = {0};
-
-	f->expr_level++;
-	open_paren = expect_token(f, Token_OpenParen);
-
-	while (f->curr_token.kind != Token_CloseParen &&
-	       f->curr_token.kind != Token_EOF &&
-	       ellipsis.pos.line == 0) {
-		if (f->curr_token.kind == Token_Comma)
-			syntax_error(f->curr_token, "Expected an expression not a ,");
-
-		if (f->curr_token.kind == Token_Ellipsis) {
-			ellipsis = f->curr_token;
-			next_token(f);
-		}
-
-		AstNode *arg = parse_expr(f, false);
-		array_add(&args, arg);
-
-		if (f->curr_token.kind != Token_Comma) {
-			if (f->curr_token.kind == Token_CloseParen)
-				break;
-		}
-
-		next_token(f);
-	}
-
-	f->expr_level--;
-	close_paren = expect_token(f, Token_CloseParen);
-
-	return make_call_expr(f, operand, args, open_paren, close_paren, ellipsis);
-}
-
-AstNode *parse_atom_expr(AstFile *f, bool lhs) {
-	AstNode *operand = parse_operand(f, lhs);
-
-	bool loop = true;
-	while (loop) {
-		switch (f->curr_token.kind) {
-
-		case Token_Prime: {
-			Token op = expect_token(f, Token_Prime);
-			if (lhs) {
-				// TODO(bill): Handle this
-			}
-			AstNode *proc = parse_identifier(f);
-			AstNodeArray args;
-			array_init_reserve(&args, gb_arena_allocator(&f->arena), 1);
-			array_add(&args, operand);
-			operand = make_call_expr(f, proc, args, ast_node_token(operand), op, empty_token);
-		} break;
-
-		case Token_OpenParen: {
-			if (lhs) {
-				// TODO(bill): Handle this shit! Is this even allowed in this language?!
-			}
-			operand = parse_call_expr(f, operand);
-		} break;
-
-		case Token_Period: {
-			Token token = f->curr_token;
-			next_token(f);
-			if (lhs) {
-				// TODO(bill): handle this
-			}
-			switch (f->curr_token.kind) {
-			case Token_Identifier:
-				operand = make_selector_expr(f, token, operand, parse_identifier(f));
-				break;
-			default: {
-				syntax_error(f->curr_token, "Expected a selector");
-				next_token(f);
-				operand = make_selector_expr(f, f->curr_token, operand, NULL);
-			} break;
-			}
-		} break;
-
-		case Token_OpenBracket: {
-			if (lhs) {
-				// TODO(bill): Handle this
-			}
-			Token open, close;
-			AstNode *indices[3] = {0};
-
-			f->expr_level++;
-			open = expect_token(f, Token_OpenBracket);
-
-			if (f->curr_token.kind != Token_Colon)
-				indices[0] = parse_expr(f, false);
-			isize colon_count = 0;
-			Token colons[2] = {0};
-
-			while (f->curr_token.kind == Token_Colon && colon_count < 2) {
-				colons[colon_count++] = f->curr_token;
-				next_token(f);
-				if (f->curr_token.kind != Token_Colon &&
-				    f->curr_token.kind != Token_CloseBracket &&
-				    f->curr_token.kind != Token_EOF) {
-					indices[colon_count] = parse_expr(f, false);
-				}
-			}
-
-			f->expr_level--;
-			close = expect_token(f, Token_CloseBracket);
-
-			if (colon_count == 0) {
-				operand = make_index_expr(f, operand, indices[0], open, close);
-			} else {
-				bool triple_indexed = false;
-				if (colon_count == 2) {
-					triple_indexed = true;
-					if (indices[1] == NULL) {
-						syntax_error(colons[0], "Second index is required in a triple indexed slice");
-						indices[1] = make_bad_expr(f, colons[0], colons[1]);
-					}
-					if (indices[2] == NULL) {
-						syntax_error(colons[1], "Third index is required in a triple indexed slice");
-						indices[2] = make_bad_expr(f, colons[1], close);
-					}
-				}
-				operand = make_slice_expr(f, operand, open, close, indices[0], indices[1], indices[2], triple_indexed);
-			}
-		} break;
-
-		case Token_Pointer: // Deference
-			operand = make_deref_expr(f, operand, expect_token(f, Token_Pointer));
-			break;
-
-		case Token_Maybe: // Demaybe
-			operand = make_demaybe_expr(f, operand, expect_token(f, Token_Maybe));
-			break;
-
-		case Token_OpenBrace: {
-			if (!lhs && is_literal_type(operand) && f->expr_level >= 0) {
-				if (f->curr_token.pos.line == f->prev_token.pos.line) {
-					// TODO(bill): This is a hack due to optional semicolons
-					// TODO(bill): It's probably much better to solve this by changing
-					// the syntax for struct literals and array literals
-					operand = parse_literal_value(f, operand);
-				} else {
-					loop = false;
-				}
-			} else {
-				loop = false;
-			}
-		} break;
-
-		default:
-			loop = false;
-			break;
-		}
-
-		lhs = false; // NOTE(bill): 'tis not lhs anymore
-	}
-
-	return operand;
-}
-
-AstNode *parse_type(AstFile *f);
-
-AstNode *parse_unary_expr(AstFile *f, bool lhs) {
-	switch (f->curr_token.kind) {
-	case Token_Pointer:
-	case Token_Maybe:
-	case Token_Add:
-	case Token_Sub:
-	case Token_Not:
-	case Token_Xor: {
-		AstNode *operand;
-		Token op = f->curr_token;
-		next_token(f);
-		operand = parse_unary_expr(f, lhs);
-		return make_unary_expr(f, op, operand);
-	} break;
-	}
-
-	return parse_atom_expr(f, lhs);
-}
-
-// NOTE(bill): result == priority
-i32 token_precedence(Token t) {
-	switch (t.kind) {
-	case Token_CmpOr:
-		return 1;
-	case Token_CmpAnd:
-		return 2;
-	case Token_CmpEq:
-	case Token_NotEq:
-	case Token_Lt:
-	case Token_Gt:
-	case Token_LtEq:
-	case Token_GtEq:
-		return 3;
-	case Token_Add:
-	case Token_Sub:
-	case Token_Or:
-	case Token_Xor:
-		return 4;
-	case Token_Mul:
-	case Token_Quo:
-	case Token_Mod:
-	case Token_And:
-	case Token_AndNot:
-	case Token_Shl:
-	case Token_Shr:
-		return 5;
-	case Token_DoublePrime:
-		return 6;
-	case Token_as:
-	case Token_transmute:
-	case Token_down_cast:
-	case Token_union_cast:
-		return 7;
-	}
-
-	return 0;
-}
-
-AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
-	AstNode *expression = parse_unary_expr(f, lhs);
-	for (i32 prec = token_precedence(f->curr_token); prec >= prec_in; prec--) {
-		for (;;) {
-			AstNode *right;
-			Token op = f->curr_token;
-			i32 op_prec = token_precedence(op);
-			if (op_prec != prec)
-				break;
-			expect_operator(f); // NOTE(bill): error checks too
-			if (lhs) {
-				// TODO(bill): error checking
-				lhs = false;
-			}
-
-			switch (op.kind) {
-			case Token_DoublePrime: {
-				// TODO(bill): Properly define semantic for in-fix and post-fix calls
-				AstNode *proc = parse_identifier(f);
-				/* if (f->curr_token.kind == Token_OpenParen) {
-					AstNode *call = parse_call_expr(f, proc);
-					array_add(&call->CallExpr.args, expression);
-					for (isize i = gb_array_count(call->CallExpr.args)-1; i > 0; i--) {
-						gb_swap(AstNode *, call->CallExpr.args[i], call->CallExpr.args[i-1]);
-					}
-
-					expression = call;
-				} else  */{
-					right = parse_binary_expr(f, false, prec+1);
-					AstNodeArray args = {0};
-					array_init_reserve(&args, gb_arena_allocator(&f->arena), 2);
-					array_add(&args, expression);
-					array_add(&args, right);
-					expression = make_call_expr(f, proc, args, op, ast_node_token(right), empty_token);
-				}
-				continue;
-			} break;
-
-			case Token_as:
-			case Token_transmute:
-			case Token_down_cast:
-			case Token_union_cast:
-				right = parse_type(f);
-				break;
-
-			default:
-				right = parse_binary_expr(f, false, prec+1);
-				if (!right) {
-					syntax_error(op, "Expected expression on the right hand side of the binary operator");
-				}
-				break;
-			}
-			expression = make_binary_expr(f, op, expression, right);
-		}
-	}
-	return expression;
-}
-
-AstNode *parse_expr(AstFile *f, bool lhs) {
-	return parse_binary_expr(f, lhs, 0+1);
-}
-
-
-AstNodeArray parse_expr_list(AstFile *f, bool lhs) {
-	AstNodeArray list = make_ast_node_array(f);
-	do {
-		AstNode *e = parse_expr(f, lhs);
-		array_add(&list, e);
-		if (f->curr_token.kind != Token_Comma ||
-		    f->curr_token.kind == Token_EOF) {
-		    break;
-		}
-		next_token(f);
-	} while (true);
-
-	return list;
-}
-
-AstNodeArray parse_lhs_expr_list(AstFile *f) {
-	return parse_expr_list(f, true);
-}
-
-AstNodeArray parse_rhs_expr_list(AstFile *f) {
-	return parse_expr_list(f, false);
-}
-
-AstNode *parse_decl(AstFile *f, AstNodeArray names);
-
-AstNode *parse_simple_stmt(AstFile *f) {
-	isize lhs_count = 0, rhs_count = 0;
-	AstNodeArray lhs = parse_lhs_expr_list(f);
-
-
-	AstNode *statement = NULL;
-	Token token = f->curr_token;
-	switch (token.kind) {
-	case Token_Eq:
-	case Token_AddEq:
-	case Token_SubEq:
-	case Token_MulEq:
-	case Token_QuoEq:
-	case Token_ModEq:
-	case Token_AndEq:
-	case Token_OrEq:
-	case Token_XorEq:
-	case Token_ShlEq:
-	case Token_ShrEq:
-	case Token_AndNotEq:
-	case Token_CmpAndEq:
-	case Token_CmpOrEq:
-	{
-		if (f->curr_proc == NULL) {
-			syntax_error(f->curr_token, "You cannot use a simple statement in the file scope");
-			return make_bad_stmt(f, f->curr_token, f->curr_token);
-		}
-		next_token(f);
-		AstNodeArray rhs = parse_rhs_expr_list(f);
-		if (rhs.count == 0) {
-			syntax_error(token, "No right-hand side in assignment statement.");
-			return make_bad_stmt(f, token, f->curr_token);
-		}
-		return make_assign_stmt(f, token, lhs, rhs);
-	} break;
-
-	case Token_Colon: // Declare
-		return parse_decl(f, lhs);
-	}
-
-	if (lhs_count > 1) {
-		syntax_error(token, "Expected 1 expression");
-		return make_bad_stmt(f, token, f->curr_token);
-	}
-
-	token = f->curr_token;
-	switch (token.kind) {
-	case Token_Increment:
-	case Token_Decrement:
-		if (f->curr_proc == NULL) {
-			syntax_error(f->curr_token, "You cannot use a simple statement in the file scope");
-			return make_bad_stmt(f, f->curr_token, f->curr_token);
-		}
-		statement = make_inc_dec_stmt(f, token, lhs.e[0]);
-		next_token(f);
-		return statement;
-	}
-
-	return make_expr_stmt(f, lhs.e[0]);
-}
-
-
-
-AstNode *parse_block_stmt(AstFile *f) {
-	if (f->curr_proc == NULL) {
-		syntax_error(f->curr_token, "You cannot use a block statement in the file scope");
-		return make_bad_stmt(f, f->curr_token, f->curr_token);
-	}
-	AstNode *block_stmt = parse_body(f);
-	return block_stmt;
-}
-
-AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) {
-	if (statement == NULL)
-		return NULL;
-
-	if (statement->kind == AstNode_ExprStmt)
-		return statement->ExprStmt.expr;
-
-	syntax_error(f->curr_token, "Expected `%.*s`, found a simple statement.", LIT(kind));
-	return make_bad_expr(f, f->curr_token, f->tokens.e[f->curr_token_index+1]);
-}
-
-AstNodeArray parse_identfier_list(AstFile *f) {
-	AstNodeArray list = make_ast_node_array(f);
-
-	do {
-		array_add(&list, parse_identifier(f));
-		if (f->curr_token.kind != Token_Comma ||
-		    f->curr_token.kind == Token_EOF) {
-		    break;
-		}
-		next_token(f);
-	} while (true);
-
-	return list;
-}
-
-
-
-AstNode *parse_type_attempt(AstFile *f) {
-	AstNode *type = parse_identifier_or_type(f, 0);
-	if (type != NULL) {
-		// TODO(bill): Handle?
-	}
-	return type;
-}
-
-AstNode *parse_type(AstFile *f) {
-	AstNode *type = parse_type_attempt(f);
-	if (type == NULL) {
-		Token token = f->curr_token;
-		syntax_error(token, "Expected a type");
-		next_token(f);
-		return make_bad_expr(f, token, f->curr_token);
-	}
-	return type;
-}
-
-
-Token parse_procedure_signature(AstFile *f,
-                                AstNodeArray *params, AstNodeArray *results);
-
-AstNode *parse_proc_type(AstFile *f) {
-	AstNodeArray params = {0};
-	AstNodeArray results = {0};
-
-	Token proc_token = parse_procedure_signature(f, &params, &results);
-
-	return make_proc_type(f, proc_token, params, results);
-}
-
-
-AstNodeArray parse_parameter_list(AstFile *f) {
-	AstNodeArray params = make_ast_node_array(f);
-
-	while (f->curr_token.kind == Token_Identifier ||
-	       f->curr_token.kind == Token_using) {
-		bool is_using = false;
-		if (allow_token(f, Token_using)) {
-			is_using = true;
-		}
-
-		AstNodeArray names = parse_lhs_expr_list(f);
-		if (names.count == 0) {
-			syntax_error(f->curr_token, "Empty parameter declaration");
-		}
-
-		if (names.count > 1 && is_using) {
-			syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type");
-			is_using = false;
-		}
-
-		expect_token_after(f, Token_Colon, "parameter list");
-
-		AstNode *type = NULL;
-		if (f->curr_token.kind == Token_Ellipsis) {
-			Token ellipsis = f->curr_token;
-			next_token(f);
-			type = parse_type_attempt(f);
-			if (type == NULL) {
-				syntax_error(f->curr_token, "variadic parameter is missing a type after `..`");
-				type = make_bad_expr(f, ellipsis, f->curr_token);
-			} else {
-				if (names.count > 1) {
-					syntax_error(f->curr_token, "mutliple variadic parameters, only  `..`");
-				} else {
-					type = make_ellipsis(f, ellipsis, type);
-				}
-			}
-		} else {
-			type = parse_type_attempt(f);
-		}
-
-
-		if (type == NULL) {
-			syntax_error(f->curr_token, "Expected a type for this parameter declaration");
-		}
-
-		array_add(&params, make_parameter(f, names, type, is_using));
-		if (f->curr_token.kind != Token_Comma) {
-			break;
-		}
-		next_token(f);
-	}
-
-	return params;
-}
-
-
-AstNodeArray parse_struct_params(AstFile *f, isize *decl_count_, bool using_allowed) {
-	AstNodeArray decls = make_ast_node_array(f);
-	isize decl_count = 0;
-
-	while (f->curr_token.kind == Token_Identifier ||
-	       f->curr_token.kind == Token_using) {
-		bool is_using = false;
-		if (allow_token(f, Token_using)) {
-			is_using = true;
-		}
-		AstNodeArray names = parse_lhs_expr_list(f);
-		if (names.count == 0) {
-			syntax_error(f->curr_token, "Empty field declaration");
-		}
-
-		if (!using_allowed && is_using) {
-			syntax_error(f->curr_token, "Cannot apply `using` to members of a union");
-			is_using = false;
-		}
-		if (names.count > 1 && is_using) {
-			syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type");
-		}
-
-		AstNode *decl = NULL;
-
-		if (f->curr_token.kind == Token_Colon) {
-			decl = parse_decl(f, names);
-
-			if (decl->kind == AstNode_ProcDecl) {
-				syntax_error(f->curr_token, "Procedure declarations are not allowed within a structure");
-				decl = make_bad_decl(f, ast_node_token(names.e[0]), f->curr_token);
-			}
-		} else {
-			syntax_error(f->curr_token, "Illegal structure field");
-			decl = make_bad_decl(f, ast_node_token(names.e[0]), f->curr_token);
-		}
-
-		expect_semicolon_after_stmt(f, decl);
-
-		if (is_ast_node_decl(decl)) {
-			array_add(&decls, decl);
-			if (decl->kind == AstNode_VarDecl) {
-				decl->VarDecl.is_using = is_using && using_allowed;
-				if (decl->VarDecl.values.count > 0) {
-					syntax_error(f->curr_token, "Default variable assignments within a structure will be ignored (at the moment)");
-				}
-			} else {
-				decl_count += 1;
-			}
-		}
-	}
-
-	if (decl_count_) *decl_count_ = decl_count;
-
-	return decls;
-}
-
-AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
-	switch (f->curr_token.kind) {
-	case Token_Identifier: {
-		AstNode *e = parse_identifier(f);
-		while (f->curr_token.kind == Token_Period) {
-			Token token = f->curr_token;
-			next_token(f);
-			AstNode *sel = parse_identifier(f);
-			e = make_selector_expr(f, token, e, sel);
-		}
-		if (f->curr_token.kind == Token_OpenParen) {
-			// HACK NOTE(bill): For type_of_val(expr)
-			e = parse_call_expr(f, e);
-		}
-		return e;
-	}
-
-	case Token_Pointer: {
-		Token token = expect_token(f, Token_Pointer);
-		AstNode *elem = parse_type(f);
-		return make_pointer_type(f, token, elem);
-	}
-
-	case Token_Maybe: {
-		Token token = expect_token(f, Token_Maybe);
-		AstNode *elem = parse_type(f);
-		return make_maybe_type(f, token, elem);
-	}
-
-	case Token_OpenBracket: {
-		f->expr_level++;
-		Token token = expect_token(f, Token_OpenBracket);
-		AstNode *count_expr = NULL;
-
-		if (f->curr_token.kind == Token_Ellipsis) {
-			count_expr = make_ellipsis(f, f->curr_token, NULL);
-			next_token(f);
-		} else if (f->curr_token.kind != Token_CloseBracket) {
-			count_expr = parse_expr(f, false);
-		}
-		expect_token(f, Token_CloseBracket);
-		f->expr_level--;
-		AstNode *e = make_array_type(f, token, count_expr, parse_type(f));
-		return e;
-	}
-
-	case Token_OpenBrace: {
-		f->expr_level++;
-		Token token = expect_token(f, Token_OpenBrace);
-		AstNode *count_expr = parse_expr(f, false);
-		expect_token(f, Token_CloseBrace);
-		f->expr_level--;
-		return make_vector_type(f, token, count_expr, parse_type(f));
-	}
-
-	case Token_struct: {
-		Token token = expect_token(f, Token_struct);
-		bool is_packed = false;
-		bool is_ordered = false;
-		while (allow_token(f, Token_Hash)) {
-			Token tag = expect_token_after(f, Token_Identifier, "`#`");
-			if (str_eq(tag.string, str_lit("packed"))) {
-				if (is_packed) {
-					syntax_error(tag, "Duplicate struct tag `#%.*s`", LIT(tag.string));
-				}
-				is_packed = true;
-			} else if (str_eq(tag.string, str_lit("ordered"))) {
-				if (is_ordered) {
-					syntax_error(tag, "Duplicate struct tag `#%.*s`", LIT(tag.string));
-				}
-				is_ordered = true;
-			} else {
-				syntax_error(tag, "Invalid struct tag `#%.*s`", LIT(tag.string));
-			}
-		}
-
-		if (is_packed && is_ordered) {
-			syntax_error(token, "`#ordered` is not needed with `#packed` which implies ordering");
-		}
-
-		Token open = expect_token_after(f, Token_OpenBrace, "`struct`");
-		isize decl_count = 0;
-		AstNodeArray decls = parse_struct_params(f, &decl_count, true);
-		Token close = expect_token(f, Token_CloseBrace);
-
-		return make_struct_type(f, token, decls, decl_count, is_packed, is_ordered);
-	} break;
-
-	case Token_union: {
-		Token token = expect_token(f, Token_union);
-		Token open = expect_token_after(f, Token_OpenBrace, "`union`");
-		isize decl_count = 0;
-		AstNodeArray decls = parse_struct_params(f, &decl_count, false);
-		Token close = expect_token(f, Token_CloseBrace);
-
-		return make_union_type(f, token, decls, decl_count);
-	}
-
-	case Token_raw_union: {
-		Token token = expect_token(f, Token_raw_union);
-		Token open = expect_token_after(f, Token_OpenBrace, "`raw_union`");
-		isize decl_count = 0;
-		AstNodeArray decls = parse_struct_params(f, &decl_count, true);
-		Token close = expect_token(f, Token_CloseBrace);
-
-		return make_raw_union_type(f, token, decls, decl_count);
-	}
-
-	case Token_enum: {
-		Token token = expect_token(f, Token_enum);
-		AstNode *base_type = NULL;
-		Token open, close;
-
-		if (f->curr_token.kind != Token_OpenBrace) {
-			base_type = parse_type(f);
-		}
-
-		AstNodeArray fields = make_ast_node_array(f);
-
-		open = expect_token_after(f, Token_OpenBrace, "`enum`");
-
-		while (f->curr_token.kind != Token_CloseBrace &&
-		       f->curr_token.kind != Token_EOF) {
-			AstNode *name = parse_identifier(f);
-			AstNode *value = NULL;
-			Token eq = empty_token;
-			if (f->curr_token.kind == Token_Eq) {
-				eq = expect_token(f, Token_Eq);
-				value = parse_value(f);
-			}
-			AstNode *field = make_field_value(f, name, value, eq);
-			array_add(&fields, field);
-			if (f->curr_token.kind != Token_Comma) {
-				break;
-			}
-			next_token(f);
-		}
-
-		close = expect_token(f, Token_CloseBrace);
-
-		return make_enum_type(f, token, base_type, fields);
-	}
-
-	case Token_proc:
-		return parse_proc_type(f);
-
-	case Token_OpenParen: {
-		// NOTE(bill): Skip the paren expression
-		AstNode *type;
-		Token open, close;
-		open = expect_token(f, Token_OpenParen);
-		type = parse_type(f);
-		close = expect_token(f, Token_CloseParen);
-		return type;
-		// return make_paren_expr(f, type, open, close);
-	}
-
-	// TODO(bill): Why is this even allowed? Is this a parsing error?
-	case Token_Colon:
-		break;
-
-	case Token_Eq:
-		if (f->prev_token.kind == Token_Colon)
-			break;
-		// fallthrough
-	default:
-		syntax_error(f->curr_token,
-		             "Expected a type or identifier after `%.*s`, got `%.*s`", LIT(f->prev_token.string), LIT(f->curr_token.string));
-		break;
-	}
-
-	return NULL;
-}
-
-
-AstNodeArray parse_results(AstFile *f) {
-	AstNodeArray results = make_ast_node_array(f);
-	if (allow_token(f, Token_ArrowRight)) {
-		if (f->curr_token.kind == Token_OpenParen) {
-			expect_token(f, Token_OpenParen);
-			while (f->curr_token.kind != Token_CloseParen &&
-			       f->curr_token.kind != Token_EOF) {
-				array_add(&results, parse_type(f));
-				if (f->curr_token.kind != Token_Comma) {
-					break;
-				}
-				next_token(f);
-			}
-			expect_token(f, Token_CloseParen);
-
-			return results;
-		}
-
-		array_add(&results, parse_type(f));
-		return results;
-	}
-	return results;
-}
-
-Token parse_procedure_signature(AstFile *f,
-                               AstNodeArray *params,
-                               AstNodeArray *results) {
-	Token proc_token = expect_token(f, Token_proc);
-	expect_token(f, Token_OpenParen);
-	*params = parse_parameter_list(f);
-	expect_token_after(f, Token_CloseParen, "parameter list");
-	*results = parse_results(f);
-	return proc_token;
-}
-
-AstNode *parse_body(AstFile *f) {
-	AstNodeArray stmts = {0};
-	Token open, close;
-	open = expect_token(f, Token_OpenBrace);
-	stmts = parse_stmt_list(f);
-	close = expect_token(f, Token_CloseBrace);
-
-	return make_block_stmt(f, stmts, open, close);
-}
-
-
-
-AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) {
-	AstNodeArray params = {0};
-	AstNodeArray results = {0};
-
-	parse_procedure_signature(f, &params, &results);
-	AstNode *proc_type = make_proc_type(f, proc_token, params, results);
-
-	AstNode *body = NULL;
-	u64 tags = 0;
-	String foreign_name = {0};
-	String link_name = {0};
-
-	parse_proc_tags(f, &tags, &foreign_name, &link_name);
-
-	AstNode *curr_proc = f->curr_proc;
-	f->curr_proc = proc_type;
-
-	if (f->curr_token.kind == Token_OpenBrace) {
-		if ((tags & ProcTag_foreign) != 0) {
-			syntax_error(f->curr_token, "A procedure tagged as `#foreign` cannot have a body");
-		}
-		body = parse_body(f);
-	}
-
-	f->curr_proc = curr_proc;
-	return make_proc_decl(f, name, proc_type, body, tags, foreign_name, link_name);
-}
-
-AstNode *parse_decl(AstFile *f, AstNodeArray names) {
-	AstNodeArray values = {0};
-	AstNode *type = NULL;
-
-	for_array(i, names) {
-		AstNode *name = names.e[i];
-		if (name->kind == AstNode_Ident) {
-			String n = name->Ident.string;
-			// NOTE(bill): Check for reserved identifiers
-			if (str_eq(n, str_lit("context"))) {
-				syntax_error(ast_node_token(name), "`context` is a reserved identifier");
-				break;
-			}
-		}
-	}
-
-	if (allow_token(f, Token_Colon)) {
-		if (!allow_token(f, Token_type)) {
-			type = parse_identifier_or_type(f, 0);
-		}
-	} else if (f->curr_token.kind != Token_Eq && f->curr_token.kind != Token_Semicolon) {
-		syntax_error(f->curr_token, "Expected type separator `:` or `=`");
-	}
-
-	bool is_mutable = true;
-
-	if (f->curr_token.kind == Token_Eq ||
-	    f->curr_token.kind == Token_Colon) {
-		if (f->curr_token.kind == Token_Colon) {
-			is_mutable = false;
-		}
-		next_token(f);
-
-		if (f->curr_token.kind == Token_type ||
-		    f->curr_token.kind == Token_struct ||
-		    f->curr_token.kind == Token_enum ||
-		    f->curr_token.kind == Token_union ||
-		    f->curr_token.kind == Token_raw_union) {
-			Token token = f->curr_token;
-			if (token.kind == Token_type) {
-				next_token(f);
-			}
-			if (names.count != 1) {
-				syntax_error(ast_node_token(names.e[0]), "You can only declare one type at a time");
-				return make_bad_decl(f, names.e[0]->Ident, token);
-			}
-
-			if (type != NULL) {
-				syntax_error(f->prev_token, "Expected either `type` or nothing between : and :");
-				// NOTE(bill): Do not fail though
-			}
-
-			return make_type_decl(f, token, names.e[0], parse_type(f));
-		} else if (f->curr_token.kind == Token_proc &&
-		    is_mutable == false) {
-		    // NOTE(bill): Procedure declarations
-			Token proc_token = f->curr_token;
-			AstNode *name = names.e[0];
-			if (names.count != 1) {
-				syntax_error(proc_token, "You can only declare one procedure at a time");
-				return make_bad_decl(f, name->Ident, proc_token);
-			}
-
-			return parse_proc_decl(f, proc_token, name);
-
-		} else {
-			values = parse_rhs_expr_list(f);
-			if (values.count > names.count) {
-				syntax_error(f->curr_token, "Too many values on the right hand side of the declaration");
-			} else if (values.count < names.count && !is_mutable) {
-				syntax_error(f->curr_token, "All constant declarations must be defined");
-			} else if (values.count == 0) {
-				syntax_error(f->curr_token, "Expected an expression for this declaration");
-			}
-		}
-	}
-
-	if (is_mutable) {
-		if (type == NULL && values.count == 0) {
-			syntax_error(f->curr_token, "Missing variable type or initialization");
-			return make_bad_decl(f, f->curr_token, f->curr_token);
-		}
-	} else {
-		if (type == NULL && values.count == 0 && names.count > 0) {
-			syntax_error(f->curr_token, "Missing constant value");
-			return make_bad_decl(f, f->curr_token, f->curr_token);
-		}
-	}
-
-	if (values.e == NULL) {
-		values = make_ast_node_array(f);
-	}
-
-	if (is_mutable) {
-		return make_var_decl(f, names, type, values);
-	}
-	return make_const_decl(f, names, type, values);
-}
-
-
-AstNode *parse_if_stmt(AstFile *f) {
-	if (f->curr_proc == NULL) {
-		syntax_error(f->curr_token, "You cannot use an if statement in the file scope");
-		return make_bad_stmt(f, f->curr_token, f->curr_token);
-	}
-
-	Token token = expect_token(f, Token_if);
-	AstNode *init = NULL;
-	AstNode *cond = NULL;
-	AstNode *body = NULL;
-	AstNode *else_stmt = NULL;
-
-	isize prev_level = f->expr_level;
-	f->expr_level = -1;
-
-
-	if (allow_token(f, Token_Semicolon)) {
-		cond = parse_expr(f, false);
-	} else {
-		init = parse_simple_stmt(f);
-		if (allow_token(f, Token_Semicolon)) {
-			cond = parse_expr(f, false);
-		} else {
-			cond = convert_stmt_to_expr(f, init, str_lit("boolean expression"));
-			init = NULL;
-		}
-	}
-
-	f->expr_level = prev_level;
-
-	if (cond == NULL) {
-		syntax_error(f->curr_token, "Expected condition for if statement");
-	}
-
-	body = parse_block_stmt(f);
-
-	if (allow_token(f, Token_else)) {
-		switch (f->curr_token.kind) {
-		case Token_if:
-			else_stmt = parse_if_stmt(f);
-			break;
-		case Token_OpenBrace:
-			else_stmt = parse_block_stmt(f);
-			break;
-		default:
-			syntax_error(f->curr_token, "Expected if statement block statement");
-			else_stmt = make_bad_stmt(f, f->curr_token, f->tokens.e[f->curr_token_index+1]);
-			break;
-		}
-	}
-
-	return make_if_stmt(f, token, init, cond, body, else_stmt);
-}
-
-AstNode *parse_return_stmt(AstFile *f) {
-	if (f->curr_proc == NULL) {
-		syntax_error(f->curr_token, "You cannot use a return statement in the file scope");
-		return make_bad_stmt(f, f->curr_token, f->curr_token);
-	}
-
-	Token token = expect_token(f, Token_return);
-	AstNodeArray results = make_ast_node_array(f);
-
-	if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace &&
-	    f->curr_token.pos.line == token.pos.line) {
-		results = parse_rhs_expr_list(f);
-	}
-	if (f->curr_token.kind != Token_CloseBrace) {
-		expect_semicolon_after_stmt(f, results.e[0]);
-	}
-
-	return make_return_stmt(f, token, results);
-}
-
-AstNode *parse_for_stmt(AstFile *f) {
-	if (f->curr_proc == NULL) {
-		syntax_error(f->curr_token, "You cannot use a for statement in the file scope");
-		return make_bad_stmt(f, f->curr_token, f->curr_token);
-	}
-
-	Token token = expect_token(f, Token_for);
-
-	AstNode *init = NULL;
-	AstNode *cond = NULL;
-	AstNode *end  = NULL;
-	AstNode *body = NULL;
-
-	if (f->curr_token.kind != Token_OpenBrace) {
-		isize prev_level = f->expr_level;
-		f->expr_level = -1;
-		if (f->curr_token.kind != Token_Semicolon) {
-			cond = parse_simple_stmt(f);
-			if (is_ast_node_complex_stmt(cond)) {
-				syntax_error(f->curr_token,
-				             "You are not allowed that type of statement in a for statement, it is too complex!");
-			}
-		}
-
-		if (allow_token(f, Token_Semicolon)) {
-			init = cond;
-			cond = NULL;
-			if (f->curr_token.kind != Token_Semicolon) {
-				cond = parse_simple_stmt(f);
-			}
-			expect_token(f, Token_Semicolon);
-			if (f->curr_token.kind != Token_OpenBrace) {
-				end = parse_simple_stmt(f);
-			}
-		}
-		f->expr_level = prev_level;
-	}
-	body = parse_block_stmt(f);
-
-	cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression"));
-
-	return make_for_stmt(f, token, init, cond, end, body);
-}
-
-AstNode *parse_case_clause(AstFile *f) {
-	Token token = f->curr_token;
-	AstNodeArray list = make_ast_node_array(f);
-	if (allow_token(f, Token_case)) {
-		list = parse_rhs_expr_list(f);
-	} else {
-		expect_token(f, Token_default);
-	}
-	expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax?
-	// expect_token(f, Token_ArrowRight); // TODO(bill): Is this the best syntax?
-	AstNodeArray stmts = parse_stmt_list(f);
-
-	return make_case_clause(f, token, list, stmts);
-}
-
-
-AstNode *parse_type_case_clause(AstFile *f) {
-	Token token = f->curr_token;
-	AstNodeArray clause = make_ast_node_array(f);
-	if (allow_token(f, Token_case)) {
-		array_add(&clause, parse_type(f));
-	} else {
-		expect_token(f, Token_default);
-	}
-	expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax?
-	// expect_token(f, Token_ArrowRight); // TODO(bill): Is this the best syntax?
-	AstNodeArray stmts = parse_stmt_list(f);
-
-	return make_case_clause(f, token, clause, stmts);
-}
-
-
-AstNode *parse_match_stmt(AstFile *f) {
-	if (f->curr_proc == NULL) {
-		syntax_error(f->curr_token, "You cannot use a match statement in the file scope");
-		return make_bad_stmt(f, f->curr_token, f->curr_token);
-	}
-
-	Token token = expect_token(f, Token_match);
-	AstNode *init = NULL;
-	AstNode *tag  = NULL;
-	AstNode *body = NULL;
-	Token open, close;
-
-	if (allow_token(f, Token_type)) {
-		isize prev_level = f->expr_level;
-		f->expr_level = -1;
-
-		AstNode *var = parse_identifier(f);
-		expect_token(f, Token_Colon);
-		tag = parse_simple_stmt(f);
-
-		f->expr_level = prev_level;
-
-		open = expect_token(f, Token_OpenBrace);
-		AstNodeArray list = make_ast_node_array(f);
-
-		while (f->curr_token.kind == Token_case ||
-		       f->curr_token.kind == Token_default) {
-			array_add(&list, parse_type_case_clause(f));
-		}
-
-		close = expect_token(f, Token_CloseBrace);
-		body = make_block_stmt(f, list, open, close);
-
-		tag = convert_stmt_to_expr(f, tag, str_lit("type match expression"));
-		return make_type_match_stmt(f, token, tag, var, body);
-	} else {
-		if (f->curr_token.kind != Token_OpenBrace) {
-			isize prev_level = f->expr_level;
-			f->expr_level = -1;
-			if (f->curr_token.kind != Token_Semicolon) {
-				tag = parse_simple_stmt(f);
-			}
-			if (allow_token(f, Token_Semicolon)) {
-				init = tag;
-				tag = NULL;
-				if (f->curr_token.kind != Token_OpenBrace) {
-					tag = parse_simple_stmt(f);
-				}
-			}
-
-			f->expr_level = prev_level;
-		}
-
-		open = expect_token(f, Token_OpenBrace);
-		AstNodeArray list = make_ast_node_array(f);
-
-		while (f->curr_token.kind == Token_case ||
-		       f->curr_token.kind == Token_default) {
-			array_add(&list, parse_case_clause(f));
-		}
-
-		close = expect_token(f, Token_CloseBrace);
-
-		body = make_block_stmt(f, list, open, close);
-
-		tag = convert_stmt_to_expr(f, tag, str_lit("match expression"));
-		return make_match_stmt(f, token, init, tag, body);
-	}
-}
-
-
-AstNode *parse_defer_stmt(AstFile *f) {
-	if (f->curr_proc == NULL) {
-		syntax_error(f->curr_token, "You cannot use a defer statement in the file scope");
-		return make_bad_stmt(f, f->curr_token, f->curr_token);
-	}
-
-	Token token = expect_token(f, Token_defer);
-	AstNode *statement = parse_stmt(f);
-	switch (statement->kind) {
-	case AstNode_EmptyStmt:
-		syntax_error(token, "Empty statement after defer (e.g. `;`)");
-		break;
-	case AstNode_DeferStmt:
-		syntax_error(token, "You cannot defer a defer statement");
-		break;
-	case AstNode_ReturnStmt:
-		syntax_error(token, "You cannot a return statement");
-		break;
-	}
-
-	return make_defer_stmt(f, token, statement);
-}
-
-AstNode *parse_asm_stmt(AstFile *f) {
-	Token token = expect_token(f, Token_asm);
-	bool is_volatile = false;
-	if (allow_token(f, Token_volatile)) {
-		is_volatile = true;
-	}
-	Token open, close, code_string;
-	open = expect_token(f, Token_OpenBrace);
-	code_string = expect_token(f, Token_String);
-	AstNode *output_list = NULL;
-	AstNode *input_list = NULL;
-	AstNode *clobber_list = NULL;
-	isize output_count = 0;
-	isize input_count = 0;
-	isize clobber_count = 0;
-
-	// TODO(bill): Finish asm statement and determine syntax
-
-	// if (f->curr_token.kind != Token_CloseBrace) {
-		// expect_token(f, Token_Colon);
-	// }
-
-	close = expect_token(f, Token_CloseBrace);
-
-	return make_asm_stmt(f, token, is_volatile, open, close, code_string,
-	                     output_list, input_list, clobber_list,
-	                     output_count, input_count, clobber_count);
-
-}
-
-
-
-AstNode *parse_stmt(AstFile *f) {
-	AstNode *s = NULL;
-	Token token = f->curr_token;
-	switch (token.kind) {
-	// Operands
-	case Token_Identifier:
-	case Token_Integer:
-	case Token_Float:
-	case Token_Rune:
-	case Token_String:
-	case Token_OpenParen:
-	case Token_proc:
-	// Unary Operators
-	case Token_Add:
-	case Token_Sub:
-	case Token_Xor:
-	case Token_Not:
-		s = parse_simple_stmt(f);
-		expect_semicolon_after_stmt(f, s);
-		return s;
-
-	// TODO(bill): other keywords
-	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_asm:    return parse_asm_stmt(f);
-
-	case Token_break:
-	case Token_continue:
-	case Token_fallthrough:
-		next_token(f);
-		s = make_branch_stmt(f, token);
-		expect_semicolon_after_stmt(f, s);
-		return s;
-
-
-	case Token_using: {
-		AstNode *node = NULL;
-
-		next_token(f);
-		node = parse_stmt(f);
-
-		bool valid = false;
-
-		switch (node->kind) {
-		case AstNode_ExprStmt: {
-			AstNode *e = unparen_expr(node->ExprStmt.expr);
-			while (e->kind == AstNode_SelectorExpr) {
-				e = unparen_expr(e->SelectorExpr.selector);
-			}
-			if (e->kind == AstNode_Ident) {
-				valid = true;
-			}
-		} break;
-		case AstNode_VarDecl:
-			valid = true;
-			break;
-		}
-
-		if (!valid) {
-			syntax_error(token, "Illegal use of `using` statement.");
-			return make_bad_stmt(f, token, f->curr_token);
-		}
-
-
-		return make_using_stmt(f, token, node);
-	} break;
-
-	case Token_push_allocator: {
-		next_token(f);
-		isize prev_level = f->expr_level;
-		f->expr_level = -1;
-		AstNode *expr = parse_expr(f, false);
-		f->expr_level = prev_level;
-
-		AstNode *body = parse_block_stmt(f);
-		return make_push_allocator(f, token, expr, body);
-	} break;
-
-	case Token_push_context: {
-		next_token(f);
-		isize prev_level = f->expr_level;
-		f->expr_level = -1;
-		AstNode *expr = parse_expr(f, false);
-		f->expr_level = prev_level;
-
-		AstNode *body = parse_block_stmt(f);
-		return make_push_context(f, token, expr, body);
-	} break;
-
-	case Token_Hash: {
-		s = parse_tag_stmt(f, NULL);
-		String tag = s->TagStmt.name.string;
-		if (str_eq(tag, str_lit("shared_global_scope"))) {
-			if (f->curr_proc == NULL) {
-				f->is_global_scope = true;
-				return make_empty_stmt(f, f->curr_token);
-			}
-			syntax_error(token, "You cannot use #shared_global_scope within a procedure. This must be done at the file scope");
-			return make_bad_decl(f, token, f->curr_token);
-		} else if (str_eq(tag, str_lit("import"))) {
-			// TODO(bill): better error messages
-			Token import_name = {0};
-			Token file_path = expect_token_after(f, Token_String, "#import");
-			if (allow_token(f, Token_as)) {
-				// NOTE(bill): Custom import name
-				if (f->curr_token.kind == Token_Period) {
-					import_name = f->curr_token;
-					import_name.kind = Token_Identifier;
-					next_token(f);
-				} else {
-					import_name = expect_token_after(f, Token_Identifier, "`as` for import declaration");
-				}
-
-				if (str_eq(import_name.string, str_lit("_"))) {
-					syntax_error(token, "Illegal import name: `_`");
-					return make_bad_decl(f, token, f->curr_token);
-				}
-			}
-
-			if (f->curr_proc == NULL) {
-				return make_import_decl(f, s->TagStmt.token, file_path, import_name, false);
-			}
-			syntax_error(token, "You cannot use #import within a procedure. This must be done at the file scope");
-			return make_bad_decl(f, token, file_path);
-		} else if (str_eq(tag, str_lit("load"))) {
-			// TODO(bill): better error messages
-			Token file_path = expect_token(f, Token_String);
-			Token import_name = file_path;
-			import_name.string = str_lit(".");
-
-			if (f->curr_proc == NULL) {
-				return make_import_decl(f, s->TagStmt.token, file_path, import_name, true);
-			}
-			syntax_error(token, "You cannot use #load within a procedure. This must be done at the file scope");
-			return make_bad_decl(f, token, file_path);
-		} else if (str_eq(tag, str_lit("foreign_system_library"))) {
-			Token file_path = expect_token(f, Token_String);
-			if (f->curr_proc == NULL) {
-				return make_foreign_library(f, s->TagStmt.token, file_path, true);
-			}
-			syntax_error(token, "You cannot use #foreign_system_library within a procedure. This must be done at the file scope");
-			return make_bad_decl(f, token, file_path);
-		} else if (str_eq(tag, str_lit("foreign_library"))) {
-			Token file_path = expect_token(f, Token_String);
-			if (f->curr_proc == NULL) {
-				return make_foreign_library(f, s->TagStmt.token, file_path, false);
-			}
-			syntax_error(token, "You cannot use #foreign_library within a procedure. This must be done at the file scope");
-			return make_bad_decl(f, token, file_path);
-		} else if (str_eq(tag, str_lit("thread_local"))) {
-			AstNode *var_decl = parse_simple_stmt(f);
-			if (var_decl->kind != AstNode_VarDecl) {
-				syntax_error(token, "#thread_local may only be applied to variable declarations");
-				return make_bad_decl(f, token, ast_node_token(var_decl));
-			}
-			if (f->curr_proc != NULL) {
-				syntax_error(token, "#thread_local is only allowed at the file scope");
-				return make_bad_decl(f, token, ast_node_token(var_decl));
-			}
-			var_decl->VarDecl.tags |= VarDeclTag_thread_local;
-			return var_decl;
-		} else if (str_eq(tag, str_lit("bounds_check"))) {
-			s = parse_stmt(f);
-			s->stmt_state_flags |= StmtStateFlag_bounds_check;
-			if ((s->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
-				syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
-			}
-			return s;
-		} else if (str_eq(tag, str_lit("no_bounds_check"))) {
-			s = parse_stmt(f);
-			s->stmt_state_flags |= StmtStateFlag_no_bounds_check;
-			if ((s->stmt_state_flags & StmtStateFlag_bounds_check) != 0) {
-				syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
-			}
-			return s;
-		}
-
-		s->TagStmt.stmt = parse_stmt(f); // TODO(bill): Find out why this doesn't work as an argument
-		return s;
-	} break;
-
-	case Token_OpenBrace:
-		return parse_block_stmt(f);
-
-	case Token_Semicolon:
-		s = make_empty_stmt(f, token);
-		next_token(f);
-		return s;
-	}
-
-	syntax_error(token,
-	             "Expected a statement, got `%.*s`",
-	             LIT(token_strings[token.kind]));
-	fix_advance_to_next_stmt(f);
-	return make_bad_stmt(f, token, f->curr_token);
-}
-
-AstNodeArray parse_stmt_list(AstFile *f) {
-	AstNodeArray list = make_ast_node_array(f);
-
-	while (f->curr_token.kind != Token_case &&
-	       f->curr_token.kind != Token_default &&
-	       f->curr_token.kind != Token_CloseBrace &&
-	       f->curr_token.kind != Token_EOF) {
-		AstNode *stmt = parse_stmt(f);
-		if (stmt && stmt->kind != AstNode_EmptyStmt) {
-			array_add(&list, stmt);
-		}
-	}
-
-	return list;
-}
-
-
-ParseFileError init_ast_file(AstFile *f, String fullpath) {
-	if (!string_has_extension(fullpath, str_lit("odin"))) {
-		return ParseFile_WrongExtension;
-	}
-	TokenizerInitError err = init_tokenizer(&f->tokenizer, fullpath);
-	if (err == TokenizerInit_None) {
-		array_init(&f->tokens, heap_allocator());
-		{
-			for (;;) {
-				Token token = tokenizer_get_token(&f->tokenizer);
-				if (token.kind == Token_Invalid) {
-					return ParseFile_InvalidToken;
-				}
-				array_add(&f->tokens, token);
-
-				if (token.kind == Token_EOF) {
-					break;
-				}
-			}
-		}
-
-		f->curr_token_index = 0;
-		f->prev_token = f->tokens.e[f->curr_token_index];
-		f->curr_token = f->tokens.e[f->curr_token_index];
-
-		// NOTE(bill): Is this big enough or too small?
-		isize arena_size = gb_size_of(AstNode);
-		arena_size *= 2*f->tokens.count;
-		gb_arena_init_from_allocator(&f->arena, heap_allocator(), arena_size);
-
-		f->curr_proc = NULL;
-
-		return ParseFile_None;
-	}
-
-	switch (err) {
-	case TokenizerInit_NotExists:
-		return ParseFile_NotFound;
-	case TokenizerInit_Permission:
-		return ParseFile_Permission;
-	case TokenizerInit_Empty:
-		return ParseFile_EmptyFile;
-	}
-
-	return ParseFile_InvalidFile;
-}
-
-void destroy_ast_file(AstFile *f) {
-	gb_arena_free(&f->arena);
-	array_free(&f->tokens);
-	gb_free(heap_allocator(), f->tokenizer.fullpath.text);
-	destroy_tokenizer(&f->tokenizer);
-}
-
-bool init_parser(Parser *p) {
-	array_init(&p->files, heap_allocator());
-	array_init(&p->imports, heap_allocator());
-	array_init(&p->foreign_libraries, heap_allocator());
-	gb_mutex_init(&p->mutex);
-	return true;
-}
-
-void destroy_parser(Parser *p) {
-	// TODO(bill): Fix memory leak
-	for_array(i, p->files) {
-		destroy_ast_file(&p->files.e[i]);
-	}
-#if 1
-	for_array(i, p->imports) {
-		// gb_free(heap_allocator(), p->imports[i].text);
-	}
-#endif
-	array_free(&p->files);
-	array_free(&p->imports);
-	array_free(&p->foreign_libraries);
-	gb_mutex_destroy(&p->mutex);
-}
-
-// NOTE(bill): Returns true if it's added
-bool try_add_import_path(Parser *p, String path, String rel_path, TokenPos pos) {
-	gb_mutex_lock(&p->mutex);
-
-	for_array(i, p->imports) {
-		String import = p->imports.e[i].path;
-		if (str_eq(import, path)) {
-			return false;
-		}
-	}
-
-	ImportedFile item;
-	item.path = path;
-	item.rel_path = rel_path;
-	item.pos = pos;
-	array_add(&p->imports, item);
-
-	gb_mutex_unlock(&p->mutex);
-
-	return true;
-}
-
-String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
-	String res = {0};
-	isize str_len = base_dir.len+path.len;
-
-	u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
-
-	isize i = 0;
-	gb_memmove(str+i, base_dir.text, base_dir.len); i += base_dir.len;
-	gb_memmove(str+i, path.text, path.len);
-	str[str_len] = '\0';
-	res = path_to_fullpath(a, make_string(str, str_len));
-	gb_free(heap_allocator(), str);
-	return res;
-}
-
-String get_fullpath_core(gbAllocator a, String path) {
-	String module_dir = get_module_dir();
-	String res = {0};
-
-	char core[] = "core/";
-	isize core_len = gb_size_of(core)-1;
-
-	isize str_len = module_dir.len + core_len + path.len;
-	u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
-
-	gb_memmove(str, module_dir.text, module_dir.len);
-	gb_memmove(str+module_dir.len, core, core_len);
-	gb_memmove(str+module_dir.len+core_len, path.text, path.len);
-	str[str_len] = '\0';
-
-	res = path_to_fullpath(a, make_string(str, str_len));
-	gb_free(heap_allocator(), str);
-	return res;
-}
-
-// NOTE(bill): Returns true if it's added
-bool try_add_foreign_library_path(Parser *p, String import_file) {
-	gb_mutex_lock(&p->mutex);
-
-	for_array(i, p->foreign_libraries) {
-		String import = p->foreign_libraries.e[i];
-		if (str_eq(import, import_file)) {
-			return false;
-		}
-	}
-	array_add(&p->foreign_libraries, import_file);
-	gb_mutex_unlock(&p->mutex);
-	return true;
-}
-
-gb_global Rune illegal_import_runes[] = {
-	'"', '\'', '`', ' ', '\t', '\r', '\n', '\v', '\f',
-	'\\', // NOTE(bill): Disallow windows style filepaths
-	'!', '$', '%', '^', '&', '*', '(', ')', '=', '+',
-	'[', ']', '{', '}',
-	';', ':', '#',
-	'|', ',',  '<', '>', '?',
-};
-
-bool is_import_path_valid(String path) {
-	if (path.len > 0) {
-		u8 *start = path.text;
-		u8 *end = path.text + path.len;
-		u8 *curr = start;
-		Rune r = -1;
-		while (curr < end) {
-			isize width = 1;
-			r = curr[0];
-			if (r >= 0x80) {
-				width = gb_utf8_decode(curr, end-curr, &r);
-				if (r == GB_RUNE_INVALID && width == 1)
-					return false;
-				else if (r == GB_RUNE_BOM && curr-start > 0)
-					return false;
-			}
-
-			for (isize i = 0; i < gb_count_of(illegal_import_runes); i++) {
-				if (r == illegal_import_runes[i])
-					return false;
-			}
-
-			curr += width;
-		}
-
-		return true;
-	}
-	return false;
-}
-
-String get_filepath_extension(String path) {
-	isize dot = 0;
-	bool seen_slash = false;
-	for (isize i = path.len-1; i >= 0; i--) {
-		u8 c = path.text[i];
-		if (c == '/' || c == '\\') {
-			seen_slash = true;
-		}
-
-		if (c == '.') {
-			if (seen_slash) {
-				return str_lit("");
-			}
-
-			dot = i;
-			break;
-		}
-	}
-	return make_string(path.text, dot);
-}
-
-void parse_file(Parser *p, AstFile *f) {
-	String filepath = f->tokenizer.fullpath;
-	String base_dir = filepath;
-	for (isize i = filepath.len-1; i >= 0; i--) {
-		if (base_dir.text[i] == '\\' ||
-		    base_dir.text[i] == '/') {
-			break;
-		}
-		base_dir.len--;
-	}
-
-
-	f->decls = parse_stmt_list(f);
-
-	for_array(i, f->decls) {
-		AstNode *node = f->decls.e[i];
-		if (!is_ast_node_decl(node) &&
-		    node->kind != AstNode_BadStmt &&
-		    node->kind != AstNode_EmptyStmt) {
-			// NOTE(bill): Sanity check
-			syntax_error(ast_node_token(node), "Only declarations are allowed at file scope");
-		} else {
-			if (node->kind == AstNode_ImportDecl) {
-				AstNodeImportDecl *id = &node->ImportDecl;
-				String file_str = id->relpath.string;
-
-				if (!is_import_path_valid(file_str)) {
-					if (id->is_load) {
-						syntax_error(ast_node_token(node), "Invalid #load path: `%.*s`", LIT(file_str));
-					} else {
-						syntax_error(ast_node_token(node), "Invalid #import path: `%.*s`", LIT(file_str));
-					}
-					// NOTE(bill): It's a naughty name
-					f->decls.e[i] = make_bad_decl(f, id->token, id->token);
-					continue;
-				}
-
-				gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator
-
-				String rel_path = get_fullpath_relative(allocator, base_dir, file_str);
-				String import_file = rel_path;
-				if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
-					String abs_path = get_fullpath_core(allocator, file_str);
-					if (gb_file_exists(cast(char *)abs_path.text)) {
-						import_file = abs_path;
-					}
-				}
-
-				id->fullpath = import_file;
-				try_add_import_path(p, import_file, file_str, ast_node_token(node).pos);
-
-			} else if (node->kind == AstNode_ForeignLibrary) {
-				AstNodeForeignLibrary *id = &node->ForeignLibrary;
-				String file_str = id->filepath.string;
-
-				if (!is_import_path_valid(file_str)) {
-					if (id->is_system) {
-						syntax_error(ast_node_token(node), "Invalid `foreign_system_library` path");
-					} else {
-						syntax_error(ast_node_token(node), "Invalid `foreign_library` path");
-					}
-					// NOTE(bill): It's a naughty name
-					f->decls.e[i] = make_bad_decl(f, id->token, id->token);
-					continue;
-				}
-
-				if (!id->is_system) {
-					gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator
-
-					String rel_path = get_fullpath_relative(allocator, base_dir, file_str);
-					String import_file = rel_path;
-					if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
-						String abs_path = get_fullpath_core(allocator, file_str);
-						if (gb_file_exists(cast(char *)abs_path.text)) {
-							import_file = abs_path;
-						}
-					}
-					file_str = import_file;
-				}
-
-				try_add_foreign_library_path(p, file_str);
-			}
-		}
-	}
-}
-
-
-
-ParseFileError parse_files(Parser *p, char *init_filename) {
-	char *fullpath_str = gb_path_get_full_name(heap_allocator(), init_filename);
-	String init_fullpath = make_string_c(fullpath_str);
-	TokenPos init_pos = {0};
-	ImportedFile init_imported_file = {init_fullpath, init_fullpath, init_pos};
-	array_add(&p->imports, init_imported_file);
-	p->init_fullpath = init_fullpath;
-
-	{
-		String s = get_fullpath_core(heap_allocator(), str_lit("_preload.odin"));
-		ImportedFile runtime_file = {s, s, init_pos};
-		array_add(&p->imports, runtime_file);
-	}
-	{
-		String s = get_fullpath_core(heap_allocator(), str_lit("_soft_numbers.odin"));
-		ImportedFile runtime_file = {s, s, init_pos};
-		array_add(&p->imports, runtime_file);
-	}
-
-	for_array(i, p->imports) {
-		ImportedFile imported_file = p->imports.e[i];
-		String import_path = imported_file.path;
-		String import_rel_path = imported_file.rel_path;
-		TokenPos pos = imported_file.pos;
-		AstFile file = {0};
-		ParseFileError err = init_ast_file(&file, import_path);
-
-		if (err != ParseFile_None) {
-			if (pos.line != 0) {
-				gb_printf_err("%.*s(%td:%td) ", LIT(pos.file), pos.line, pos.column);
-			}
-			gb_printf_err("Failed to parse file: %.*s\n\t", LIT(import_rel_path));
-			switch (err) {
-			case ParseFile_WrongExtension:
-				gb_printf_err("Invalid file extension: File must have the extension `.odin`");
-				break;
-			case ParseFile_InvalidFile:
-				gb_printf_err("Invalid file");
-				break;
-			case ParseFile_EmptyFile:
-				gb_printf_err("File is empty");
-				break;
-			case ParseFile_Permission:
-				gb_printf_err("File permissions problem");
-				break;
-			case ParseFile_NotFound:
-				gb_printf_err("File cannot be found");
-				break;
-			case ParseFile_InvalidToken:
-				gb_printf_err("Invalid token found in file");
-				break;
-			}
-			gb_printf_err("\n");
-			return err;
-		}
-		parse_file(p, &file);
-
-		{
-			gb_mutex_lock(&p->mutex);
-			file.id = p->files.count;
-			array_add(&p->files, file);
-			gb_mutex_unlock(&p->mutex);
-		}
-	}
-
-	for_array(i, p->files) {
-		p->total_token_count += p->files.e[i].tokens.count;
-	}
-
-
-	return ParseFile_None;
-}
-
-

+ 0 - 221
src/printer.cpp

@@ -1,221 +0,0 @@
-
-
-gb_inline void print_indent(isize indent) {
-	while (indent --> 0)
-		gb_printf("  ");
-}
-
-void print_ast(AstNode *node, isize indent) {
-	if (node == NULL)
-		return;
-
-	switch (node->kind) {
-	case AstNode_BasicLit:
-		print_indent(indent);
-		print_token(node->BasicLit);
-		break;
-	case AstNode_Ident:
-		print_indent(indent);
-		print_token(node->Ident);
-		break;
-	case AstNode_ProcLit:
-		print_indent(indent);
-		gb_printf("(proc lit)\n");
-		print_ast(node->ProcLit.type, indent+1);
-		print_ast(node->ProcLit.body, indent+1);
-		break;
-
-	case AstNode_CompoundLit:
-		print_indent(indent);
-		gb_printf("(compound lit)\n");
-		print_ast(node->CompoundLit.type, indent+1);
-		for_array(i, node->CompoundLit.elems) {
-			print_ast(node->CompoundLit.elems[i], indent+1);
-		}
-		break;
-
-
-	case AstNode_TagExpr:
-		print_indent(indent);
-		gb_printf("(tag)\n");
-		print_indent(indent+1);
-		print_token(node->TagExpr.name);
-		print_ast(node->TagExpr.expr, indent+1);
-		break;
-
-	case AstNode_UnaryExpr:
-		print_indent(indent);
-		print_token(node->UnaryExpr.op);
-		print_ast(node->UnaryExpr.expr, indent+1);
-		break;
-	case AstNode_BinaryExpr:
-		print_indent(indent);
-		print_token(node->BinaryExpr.op);
-		print_ast(node->BinaryExpr.left, indent+1);
-		print_ast(node->BinaryExpr.right, indent+1);
-		break;
-	case AstNode_CallExpr:
-		print_indent(indent);
-		gb_printf("(call)\n");
-		print_ast(node->CallExpr.proc, indent+1);
-		for_array(i, node->CallExpr.args) {
-			print_ast(node->CallExpr.args[i], indent+1);
-		}
-		break;
-	case AstNode_SelectorExpr:
-		print_indent(indent);
-		gb_printf(".\n");
-		print_ast(node->SelectorExpr.expr,  indent+1);
-		print_ast(node->SelectorExpr.selector, indent+1);
-		break;
-	case AstNode_IndexExpr:
-		print_indent(indent);
-		gb_printf("([])\n");
-		print_ast(node->IndexExpr.expr, indent+1);
-		print_ast(node->IndexExpr.index, indent+1);
-		break;
-	case AstNode_DerefExpr:
-		print_indent(indent);
-		gb_printf("(deref)\n");
-		print_ast(node->DerefExpr.expr, indent+1);
-		break;
-
-
-	case AstNode_ExprStmt:
-		print_ast(node->ExprStmt.expr, indent);
-		break;
-	case AstNode_IncDecStmt:
-		print_indent(indent);
-		print_token(node->IncDecStmt.op);
-		print_ast(node->IncDecStmt.expr, indent+1);
-		break;
-	case AstNode_AssignStmt:
-		print_indent(indent);
-		print_token(node->AssignStmt.op);
-		for_array(i, node->AssignStmt.lhs) {
-			print_ast(node->AssignStmt.lhs[i], indent+1);
-		}
-		for_array(i, node->AssignStmt.rhs) {
-			print_ast(node->AssignStmt.rhs[i], indent+1);
-		}
-		break;
-	case AstNode_BlockStmt:
-		print_indent(indent);
-		gb_printf("(block)\n");
-		for_array(i, node->BlockStmt.stmts) {
-			print_ast(node->BlockStmt.stmts[i], indent+1);
-		}
-		break;
-
-	case AstNode_IfStmt:
-		print_indent(indent);
-		gb_printf("(if)\n");
-		print_ast(node->IfStmt.cond, indent+1);
-		print_ast(node->IfStmt.body, indent+1);
-		if (node->IfStmt.else_stmt) {
-			print_indent(indent);
-			gb_printf("(else)\n");
-			print_ast(node->IfStmt.else_stmt, indent+1);
-		}
-		break;
-	case AstNode_ReturnStmt:
-		print_indent(indent);
-		gb_printf("(return)\n");
-		for_array(i, node->ReturnStmt.results) {
-			print_ast(node->ReturnStmt.results[i], indent+1);
-		}
-		break;
-	case AstNode_ForStmt:
-		print_indent(indent);
-		gb_printf("(for)\n");
-		print_ast(node->ForStmt.init, indent+1);
-		print_ast(node->ForStmt.cond, indent+1);
-		print_ast(node->ForStmt.post, indent+1);
-		print_ast(node->ForStmt.body, indent+1);
-		break;
-	case AstNode_DeferStmt:
-		print_indent(indent);
-		gb_printf("(defer)\n");
-		print_ast(node->DeferStmt.stmt, indent+1);
-		break;
-
-
-	case AstNode_VarDecl:
-		print_indent(indent);
-		gb_printf("(decl:var)\n");
-		for_array(i, node->VarDecl.names) {
-			print_ast(node->VarDecl.names[i], indent+1);
-		}
-		print_ast(node->VarDecl.type, indent+1);
-		for_array(i, node->VarDecl.values) {
-			print_ast(node->VarDecl.values[i], indent+1);
-		}
-		break;
-	case AstNode_ConstDecl:
-		print_indent(indent);
-		gb_printf("(decl:const)\n");
-		for_array(i, node->VarDecl.names) {
-			print_ast(node->VarDecl.names[i], indent+1);
-		}
-		print_ast(node->VarDecl.type, indent+1);
-		for_array(i, node->VarDecl.values) {
-			print_ast(node->VarDecl.values[i], indent+1);
-		}
-		break;
-	case AstNode_ProcDecl:
-		print_indent(indent);
-		gb_printf("(decl:proc)\n");
-		print_ast(node->ProcDecl.type, indent+1);
-		print_ast(node->ProcDecl.body, indent+1);
-		break;
-
-	case AstNode_TypeDecl:
-		print_indent(indent);
-		gb_printf("(type)\n");
-		print_ast(node->TypeDecl.name, indent+1);
-		print_ast(node->TypeDecl.type, indent+1);
-		break;
-
-	case AstNode_ProcType:
-		print_indent(indent);
-		gb_printf("(type:proc)(%td -> %td)\n", node->ProcType.params.count, node->ProcType.results.count);
-		for_array(i, node->ProcType.params) {
-			print_ast(node->ProcType.params[i], indent+1);
-		}
-		if (node->ProcType.results.count > 0) {
-			print_indent(indent+1);
-			gb_printf("->\n");
-			for_array(i, node->ProcType.results) {
-				print_ast(node->ProcType.results[i], indent+1);
-			}
-		}
-		break;
-	case AstNode_Parameter:
-		for_array(i, node->Parameter.names) {
-			print_ast(node->Parameter.names[i], indent+1);
-		}
-		print_ast(node->Parameter.type, indent);
-		break;
-	case AstNode_PointerType:
-		print_indent(indent);
-		print_token(node->PointerType.token);
-		print_ast(node->PointerType.type, indent+1);
-		break;
-	case AstNode_ArrayType:
-		print_indent(indent);
-		gb_printf("[]\n");
-		print_ast(node->ArrayType.count, indent+1);
-		print_ast(node->ArrayType.elem, indent+1);
-		break;
-	case AstNode_StructType:
-		print_indent(indent);
-		gb_printf("(struct)\n");
-		for_array(i, node->StructType.decls) {
-			print_ast(node->StructType.decls[i], indent+1);
-		}
-		break;
-	}
-
-	// if (node->next)
-		// print_ast(node->next, indent);
-}

+ 0 - 5419
src/ssa.cpp

@@ -1,5419 +0,0 @@
-typedef struct ssaProcedure ssaProcedure;
-typedef struct ssaBlock ssaBlock;
-typedef struct ssaValue ssaValue;
-typedef struct ssaDebugInfo ssaDebugInfo;
-
-typedef Array(ssaValue *) ssaValueArray;
-
-#define MAP_TYPE ssaValue *
-#define MAP_FUNC map_ssa_value_
-#define MAP_NAME MapSsaValue
-#include "map.c"
-
-#define MAP_TYPE ssaDebugInfo *
-#define MAP_FUNC map_ssa_debug_info_
-#define MAP_NAME MapSsaDebugInfo
-#include "map.c"
-
-typedef struct ssaModule {
-	CheckerInfo * info;
-	BaseTypeSizes sizes;
-	gbArena       arena;
-	gbArena       tmp_arena;
-	gbAllocator   allocator;
-	gbAllocator   tmp_allocator;
-	bool generate_debug_info;
-
-	u32 stmt_state_flags;
-
-	// String source_filename;
-	String layout;
-	// String triple;
-
-
-	MapEntity       min_dep_map; // Key: Entity *
-	MapSsaValue     values;      // Key: Entity *
-	MapSsaValue     members;     // Key: String
-	MapString       type_names;  // Key: Type *
-	MapSsaDebugInfo debug_info;  // Key: Unique pointer
-	i32             global_string_index;
-	i32             global_array_index; // For ConstantSlice
-
-	Array(ssaProcedure *) procs;             // NOTE(bill): All procedures with bodies
-	ssaValueArray         procs_to_generate; // NOTE(bill): Procedures to generate
-} ssaModule;
-
-// NOTE(bill): For more info, see https://en.wikipedia.org/wiki/Dominator_(graph_theory)
-typedef struct ssaDomNode {
-	ssaBlock *        idom; // Parent (Immediate Dominator)
-	Array(ssaBlock *) children;
-	i32               pre, post; // Ordering in tree
-} ssaDomNode;
-
-
-typedef struct ssaBlock {
-	i32           index;
-	String        label;
-	ssaProcedure *parent;
-	AstNode *     node; // Can be NULL
-	Scope *       scope;
-	isize         scope_index;
-	ssaDomNode    dom;
-	i32           gaps;
-
-	ssaValueArray instrs;
-	ssaValueArray locals;
-
-	Array(ssaBlock *) preds;
-	Array(ssaBlock *) succs;
-} ssaBlock;
-
-typedef struct ssaTargetList ssaTargetList;
-struct ssaTargetList {
-	ssaTargetList *prev;
-	ssaBlock *     break_;
-	ssaBlock *     continue_;
-	ssaBlock *     fallthrough_;
-};
-
-typedef enum ssaDeferExitKind {
-	ssaDeferExit_Default,
-	ssaDeferExit_Return,
-	ssaDeferExit_Branch,
-} ssaDeferExitKind;
-typedef enum ssaDeferKind {
-	ssaDefer_Node,
-	ssaDefer_Instr,
-} ssaDeferKind;
-
-typedef struct ssaDefer {
-	ssaDeferKind kind;
-	isize        scope_index;
-	ssaBlock *   block;
-	union {
-		AstNode *stmt;
-		// NOTE(bill): `instr` will be copied every time to create a new one
-		ssaValue *instr;
-	};
-} ssaDefer;
-
-typedef struct ssaProcedure ssaProcedure;
-struct ssaProcedure {
-	ssaProcedure *        parent;
-	Array(ssaProcedure *) children;
-
-	Entity *              entity;
-	ssaModule *           module;
-	String                name;
-	Type *                type;
-	AstNode *             type_expr;
-	AstNode *             body;
-	u64                   tags;
-
-	ssaValueArray     params;
-	Array(ssaDefer)       defer_stmts;
-	Array(ssaBlock *)     blocks;
-	i32                   scope_index;
-	ssaBlock *            decl_block;
-	ssaBlock *            entry_block;
-	ssaBlock *            curr_block;
-	ssaTargetList *       target_list;
-	ssaValueArray     referrers;
-
-	i32                   local_count;
-	i32                   instr_count;
-	i32                   block_count;
-};
-
-#define SSA_STARTUP_RUNTIME_PROC_NAME  "__$startup_runtime"
-#define SSA_TYPE_INFO_DATA_NAME        "__$type_info_data"
-#define SSA_TYPE_INFO_DATA_MEMBER_NAME "__$type_info_data_member"
-
-
-#define SSA_INSTR_KINDS \
-	SSA_INSTR_KIND(Comment, struct { String text; }) \
-	SSA_INSTR_KIND(Local,   struct { \
-		Entity *      entity; \
-		Type *        type; \
-		bool          zero_initialized; \
-		ssaValueArray referrers; \
-	}) \
-	SSA_INSTR_KIND(ZeroInit, struct { ssaValue *address; }) \
-	SSA_INSTR_KIND(Store,    struct { ssaValue *address, *value; }) \
-	SSA_INSTR_KIND(Load,     struct { Type *type; ssaValue *address; }) \
-	SSA_INSTR_KIND(PtrOffset, struct { \
-		ssaValue *address; \
-		ssaValue *offset; \
-	}) \
-	SSA_INSTR_KIND(ArrayElementPtr, struct { \
-		ssaValue *address; \
-		Type *    result_type; \
-		ssaValue *elem_index; \
-	}) \
-	SSA_INSTR_KIND(StructElementPtr, struct {  \
-		ssaValue *address; \
-		Type *    result_type; \
-		i32       elem_index; \
-	}) \
-	SSA_INSTR_KIND(ArrayExtractValue, struct { \
-		ssaValue *address; \
-		Type *    result_type; \
-		i32       index; \
-	}) \
-	SSA_INSTR_KIND(StructExtractValue, struct { \
-		ssaValue *address; \
-		Type *    result_type; \
-		i32       index; \
-	}) \
-	SSA_INSTR_KIND(UnionTagPtr, struct { \
-		ssaValue *address; \
-		Type     *type; /* ^int */  \
-	}) \
-	SSA_INSTR_KIND(UnionTagValue, struct { \
-		ssaValue *address; \
-		Type     *type; /* int */ \
-	}) \
-	SSA_INSTR_KIND(Conv, struct { \
-		ssaConvKind kind; \
-		ssaValue *value; \
-		Type *from, *to; \
-	}) \
-	SSA_INSTR_KIND(Jump, struct { ssaBlock *block; }) \
-	SSA_INSTR_KIND(If, struct { \
-		ssaValue *cond; \
-		ssaBlock *true_block; \
-		ssaBlock *false_block; \
-	}) \
-	SSA_INSTR_KIND(Return, struct { ssaValue *value; }) \
-	SSA_INSTR_KIND(Select, struct { \
-		ssaValue *cond; \
-		ssaValue *true_value; \
-		ssaValue *false_value; \
-	}) \
-	SSA_INSTR_KIND(Phi, struct { ssaValueArray edges; Type *type; }) \
-	SSA_INSTR_KIND(Unreachable, i32) \
-	SSA_INSTR_KIND(BinaryOp, struct { \
-		Type *    type; \
-		TokenKind op; \
-		ssaValue *left, *right; \
-	}) \
-	SSA_INSTR_KIND(Call, struct { \
-		Type *    type; /* return type */  \
-		ssaValue *value; \
-		ssaValue **args; \
-		isize      arg_count; \
-	}) \
-	SSA_INSTR_KIND(VectorExtractElement, struct { \
-		ssaValue *vector; \
-		ssaValue *index; \
-	}) \
-	SSA_INSTR_KIND(VectorInsertElement, struct { \
-		ssaValue *vector; \
-		ssaValue *elem; \
-		ssaValue *index; \
-	}) \
-	SSA_INSTR_KIND(VectorShuffle, struct { \
-		ssaValue *vector; \
-		i32 *     indices; \
-		i32       index_count; \
-		Type *    type; \
-	}) \
-	SSA_INSTR_KIND(StartupRuntime, i32) \
-	SSA_INSTR_KIND(BoundsCheck, struct { \
-		TokenPos  pos; \
-		ssaValue *index; \
-		ssaValue *len; \
-	}) \
-	SSA_INSTR_KIND(SliceBoundsCheck, struct { \
-		TokenPos  pos; \
-		ssaValue *low; \
-		ssaValue *high; \
-		ssaValue *max; \
-		bool      is_substring; \
-	})
-
-#define SSA_CONV_KINDS \
-	SSA_CONV_KIND(trunc) \
-	SSA_CONV_KIND(zext) \
-	SSA_CONV_KIND(fptrunc) \
-	SSA_CONV_KIND(fpext) \
-	SSA_CONV_KIND(fptoui) \
-	SSA_CONV_KIND(fptosi) \
-	SSA_CONV_KIND(uitofp) \
-	SSA_CONV_KIND(sitofp) \
-	SSA_CONV_KIND(ptrtoint) \
-	SSA_CONV_KIND(inttoptr) \
-	SSA_CONV_KIND(bitcast)
-
-typedef enum ssaInstrKind {
-	ssaInstr_Invalid,
-#define SSA_INSTR_KIND(x, ...) GB_JOIN2(ssaInstr_, x),
-	SSA_INSTR_KINDS
-#undef SSA_INSTR_KIND
-} ssaInstrKind;
-
-String const ssa_instr_strings[] = {
-	{cast(u8 *)"Invalid", gb_size_of("Invalid")-1},
-#define SSA_INSTR_KIND(x, ...) {cast(u8 *)#x, gb_size_of(#x)-1},
-	SSA_INSTR_KINDS
-#undef SSA_INSTR_KIND
-};
-
-typedef enum ssaConvKind {
-	ssaConv_Invalid,
-#define SSA_CONV_KIND(x) GB_JOIN2(ssaConv_, x),
-	SSA_CONV_KINDS
-#undef SSA_CONV_KIND
-} ssaConvKind;
-
-String const ssa_conv_strings[] = {
-	{cast(u8 *)"Invalid", gb_size_of("Invalid")-1},
-#define SSA_CONV_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1},
-	SSA_CONV_KINDS
-#undef SSA_CONV_KIND
-};
-
-#define SSA_INSTR_KIND(k, ...) typedef __VA_ARGS__ GB_JOIN2(ssaInstr, k);
-	SSA_INSTR_KINDS
-#undef SSA_INSTR_KIND
-
-typedef struct ssaInstr ssaInstr;
-struct ssaInstr {
-	ssaInstrKind kind;
-
-	ssaBlock *parent;
-	Type *type;
-
-	union {
-#define SSA_INSTR_KIND(k, ...) GB_JOIN2(ssaInstr, k) k;
-	SSA_INSTR_KINDS
-#undef SSA_INSTR_KIND
-	};
-};
-
-
-typedef enum ssaValueKind {
-	ssaValue_Invalid,
-
-	ssaValue_Constant,
-	ssaValue_ConstantSlice,
-	ssaValue_Nil,
-	ssaValue_TypeName,
-	ssaValue_Global,
-	ssaValue_Param,
-
-	ssaValue_Proc,
-	ssaValue_Block,
-	ssaValue_Instr,
-
-	ssaValue_Count,
-} ssaValueKind;
-
-typedef struct ssaValueConstant {
-	Type *     type;
-	ExactValue value;
-} ssaValueConstant;
-
-typedef struct ssaValueConstantSlice {
-	Type *    type;
-	ssaValue *backing_array;
-	i64       count;
-} ssaValueConstantSlice;
-
-typedef struct ssaValueNil {
-	Type *type;
-} ssaValueNil;
-
-typedef struct ssaValueTypeName {
-	Type * type;
-	String name;
-} ssaValueTypeName;
-
-typedef struct ssaValueGlobal {
-	Entity *      entity;
-	Type *        type;
-	ssaValue *    value;
-	ssaValueArray referrers;
-	bool          is_constant;
-	bool          is_private;
-	bool          is_thread_local;
-	bool          is_unnamed_addr;
-} ssaValueGlobal;
-
-typedef struct ssaValueParam {
-	ssaProcedure *parent;
-	Entity *      entity;
-	Type *        type;
-	ssaValueArray referrers;
-} ssaValueParam;
-
-typedef struct ssaValue {
-	ssaValueKind kind;
-	i32 index;
-	union {
-		ssaValueConstant      Constant;
-		ssaValueConstantSlice ConstantSlice;
-		ssaValueNil           Nil;
-		ssaValueTypeName      TypeName;
-		ssaValueGlobal        Global;
-		ssaValueParam         Param;
-		ssaProcedure          Proc;
-		ssaBlock              Block;
-		ssaInstr              Instr;
-	};
-} ssaValue;
-
-gb_global ssaValue *v_zero    = NULL;
-gb_global ssaValue *v_one     = NULL;
-gb_global ssaValue *v_zero32  = NULL;
-gb_global ssaValue *v_one32   = NULL;
-gb_global ssaValue *v_two32   = NULL;
-gb_global ssaValue *v_false   = NULL;
-gb_global ssaValue *v_true    = NULL;
-
-typedef enum ssaAddrKind {
-	ssaAddr_Default,
-	ssaAddr_Vector,
-} ssaAddrKind;
-
-typedef struct ssaAddr {
-	ssaValue *  addr;
-	AstNode *   expr; // NOTE(bill): Just for testing - probably remove later
-	ssaAddrKind kind;
-	union {
-		struct { ssaValue *index; } Vector;
-	};
-} ssaAddr;
-
-ssaAddr ssa_make_addr(ssaValue *addr, AstNode *expr) {
-	ssaAddr v = {addr, expr};
-	return v;
-}
-ssaAddr ssa_make_addr_vector(ssaValue *addr, ssaValue *index, AstNode *expr) {
-	ssaAddr v = ssa_make_addr(addr, expr);
-	v.kind = ssaAddr_Vector;
-	v.Vector.index = index;
-	return v;
-}
-
-
-
-typedef enum ssaDebugEncoding {
-	ssaDebugBasicEncoding_Invalid       = 0,
-
-	ssaDebugBasicEncoding_address       = 1,
-	ssaDebugBasicEncoding_boolean       = 2,
-	ssaDebugBasicEncoding_float         = 3,
-	ssaDebugBasicEncoding_signed        = 4,
-	ssaDebugBasicEncoding_signed_char   = 5,
-	ssaDebugBasicEncoding_unsigned      = 6,
-	ssaDebugBasicEncoding_unsigned_char = 7,
-
-	ssaDebugBasicEncoding_member       = 13,
-	ssaDebugBasicEncoding_pointer_type = 15,
-	ssaDebugBasicEncoding_typedef      = 22,
-
-	ssaDebugBasicEncoding_array_type       = 1,
-	ssaDebugBasicEncoding_enumeration_type = 4,
-	ssaDebugBasicEncoding_structure_type   = 19,
-	ssaDebugBasicEncoding_union_type       = 23,
-
-} ssaDebugEncoding;
-
-typedef enum ssaDebugInfoKind {
-	ssaDebugInfo_Invalid,
-
-	ssaDebugInfo_CompileUnit,
-	ssaDebugInfo_File,
-	ssaDebugInfo_Scope,
-	ssaDebugInfo_Proc,
-	ssaDebugInfo_AllProcs,
-
-	ssaDebugInfo_BasicType,      // basic types
-	ssaDebugInfo_ProcType,
-	ssaDebugInfo_DerivedType,    // pointer, typedef
-	ssaDebugInfo_CompositeType,  // array, struct, enum, (raw_)union
-	ssaDebugInfo_Enumerator,     // For ssaDebugInfo_CompositeType if enum
-	ssaDebugInfo_GlobalVariable,
-	ssaDebugInfo_LocalVariable,
-
-
-	ssaDebugInfo_Count,
-} ssaDebugInfoKind;
-
-typedef struct ssaDebugInfo ssaDebugInfo;
-struct ssaDebugInfo {
-	ssaDebugInfoKind kind;
-	i32 id;
-
-	union {
-		struct {
-			AstFile *     file;
-			String        producer;
-			ssaDebugInfo *all_procs;
-		} CompileUnit;
-		struct {
-			AstFile *file;
-			String   filename;
-			String   directory;
-		} File;
-		struct {
-			ssaDebugInfo *parent;
-			ssaDebugInfo *file;
-			TokenPos      pos;
-			Scope *       scope; // Actual scope
-		} Scope;
-		struct {
-			Entity *      entity;
-			String        name;
-			ssaDebugInfo *file;
-			TokenPos      pos;
-		} Proc;
-		struct {
-			Array(ssaDebugInfo *) procs;
-		} AllProcs;
-
-
-		struct {
-			String           name;
-			i32              size;
-			i32              align;
-			ssaDebugEncoding encoding;
-		} BasicType;
-		struct {
-			ssaDebugInfo *        return_type;
-			Array(ssaDebugInfo *) param_types;
-		} ProcType;
-		struct {
-			ssaDebugInfo *   base_type;
-			ssaDebugEncoding encoding;
-		} DerivedType;
-		struct {
-			ssaDebugEncoding      encoding;
-			String                name;
-			String                identifier;
-			ssaDebugInfo *        file;
-			TokenPos              pos;
-			i32                   size;
-			i32                   align;
-			Array(ssaDebugInfo *) elements;
-		} CompositeType;
-		struct {
-			String name;
-			i64    value;
-		} Enumerator;
-		struct {
-			String        name;
-			String        linkage_name;
-			ssaDebugInfo *scope;
-			ssaDebugInfo *file;
-			TokenPos      pos;
-			ssaValue     *variable;
-			ssaDebugInfo *declaration;
-		} GlobalVariable;
-		struct {
-			String        name;
-			ssaDebugInfo *scope;
-			ssaDebugInfo *file;
-			TokenPos      pos;
-			i32           arg; // Non-zero if proc parameter
-			ssaDebugInfo *type;
-		} LocalVariable;
-	};
-};
-
-typedef struct ssaGen {
-	ssaModule module;
-	gbFile    output_file;
-	bool       opt_called;
-} ssaGen;
-
-ssaValue *ssa_lookup_member(ssaModule *m, String name) {
-	ssaValue **v = map_ssa_value_get(&m->members, hash_string(name));
-	if (v != NULL) {
-		return *v;
-	}
-	return NULL;
-}
-
-
-Type *ssa_type(ssaValue *value);
-Type *ssa_instr_type(ssaInstr *instr) {
-	switch (instr->kind) {
-	case ssaInstr_Local:
-		return instr->Local.type;
-	case ssaInstr_Load:
-		return instr->Load.type;
-	case ssaInstr_StructElementPtr:
-		return instr->StructElementPtr.result_type;
-	case ssaInstr_ArrayElementPtr:
-		return instr->ArrayElementPtr.result_type;
-	case ssaInstr_PtrOffset:
-		return ssa_type(instr->PtrOffset.address);
-	case ssaInstr_Phi:
-		return instr->Phi.type;
-	case ssaInstr_ArrayExtractValue:
-		return instr->ArrayExtractValue.result_type;
-	case ssaInstr_StructExtractValue:
-		return instr->StructExtractValue.result_type;
-	case ssaInstr_UnionTagPtr:
-		return instr->UnionTagPtr.type;
-	case ssaInstr_UnionTagValue:
-		return instr->UnionTagValue.type;
-	case ssaInstr_BinaryOp:
-		return instr->BinaryOp.type;
-	case ssaInstr_Conv:
-		return instr->Conv.to;
-	case ssaInstr_Select:
-		return ssa_type(instr->Select.true_value);
-	case ssaInstr_Call: {
-		Type *pt = base_type(instr->Call.type);
-		if (pt != NULL) {
-			if (pt->kind == Type_Tuple && pt->Tuple.variable_count == 1) {
-				return pt->Tuple.variables[0]->type;
-			}
-			return pt;
-		}
-		return NULL;
-	} break;
-	case ssaInstr_VectorExtractElement: {
-		Type *vt = ssa_type(instr->VectorExtractElement.vector);
-		Type *bt = base_vector_type(vt);
-		GB_ASSERT(!is_type_vector(bt));
-		return bt;
-	} break;
-	case ssaInstr_VectorInsertElement:
-		return ssa_type(instr->VectorInsertElement.vector);
-	case ssaInstr_VectorShuffle:
-		return instr->VectorShuffle.type;
-	}
-	return NULL;
-}
-
-Type *ssa_type(ssaValue *value) {
-	switch (value->kind) {
-	case ssaValue_Constant:
-		return value->Constant.type;
-	case ssaValue_ConstantSlice:
-		return value->ConstantSlice.type;
-	case ssaValue_Nil:
-		return value->Nil.type;
-	case ssaValue_TypeName:
-		return value->TypeName.type;
-	case ssaValue_Global:
-		return value->Global.type;
-	case ssaValue_Param:
-		return value->Param.type;
-	case ssaValue_Proc:
-		return value->Proc.type;
-	case ssaValue_Instr:
-		return ssa_instr_type(&value->Instr);
-	}
-	return NULL;
-}
-
-Type *ssa_addr_type(ssaAddr lval) {
-	if (lval.addr != NULL) {
-		Type *t = ssa_type(lval.addr);
-		GB_ASSERT(is_type_pointer(t));
-		return type_deref(t);
-	}
-	return NULL;
-}
-
-
-
-bool ssa_is_blank_ident(AstNode *node) {
-	if (node->kind == AstNode_Ident) {
-		ast_node(i, Ident, node);
-		return is_blank_ident(i->string);
-	}
-	return false;
-}
-
-
-ssaInstr *ssa_get_last_instr(ssaBlock *block) {
-	if (block != NULL) {
-		isize len = block->instrs.count;
-		if (len > 0) {
-			ssaValue *v = block->instrs.e[len-1];
-			GB_ASSERT(v->kind == ssaValue_Instr);
-			return &v->Instr;
-		}
-	}
-	return NULL;
-
-}
-
-bool ssa_is_instr_terminating(ssaInstr *i) {
-	if (i != NULL) {
-		switch (i->kind) {
-		case ssaInstr_Return:
-		case ssaInstr_Unreachable:
-			return true;
-		}
-	}
-
-	return false;
-}
-
-
-void ssa_add_edge(ssaBlock *from, ssaBlock *to) {
-	array_add(&from->succs, to);
-	array_add(&to->preds, from);
-}
-
-void ssa_set_instr_parent(ssaValue *instr, ssaBlock *parent) {
-	if (instr->kind == ssaValue_Instr) {
-		instr->Instr.parent = parent;
-	}
-}
-
-ssaValueArray *ssa_value_referrers(ssaValue *v) {
-	switch (v->kind) {
-	case ssaValue_Global:
-		return &v->Global.referrers;
-	case ssaValue_Param:
-		return &v->Param.referrers;
-	case ssaValue_Proc: {
-		if (v->Proc.parent != NULL) {
-			return &v->Proc.referrers;
-		}
-		return NULL;
-	}
-	case ssaValue_Instr: {
-		ssaInstr *i = &v->Instr;
-		switch (i->kind) {
-		case ssaInstr_Local:
-			return &i->Local.referrers;
-		}
-	} break;
-	}
-
-	return NULL;
-}
-
-
-
-////////////////////////////////////////////////////////////////
-//
-// @Make
-//
-////////////////////////////////////////////////////////////////
-
-void      ssa_module_add_value    (ssaModule *m, Entity *e, ssaValue *v);
-ssaValue *ssa_emit_zero_init      (ssaProcedure *p, ssaValue *address);
-ssaValue *ssa_emit_comment        (ssaProcedure *p, String text);
-ssaValue *ssa_emit_store          (ssaProcedure *p, ssaValue *address, ssaValue *value);
-ssaValue *ssa_emit_load           (ssaProcedure *p, ssaValue *address);
-void      ssa_emit_jump           (ssaProcedure *proc, ssaBlock *block);
-ssaValue *ssa_emit_conv           (ssaProcedure *proc, ssaValue *value, Type *t);
-ssaValue *ssa_type_info           (ssaProcedure *proc, Type *type);
-ssaValue *ssa_build_expr          (ssaProcedure *proc, AstNode *expr);
-void      ssa_build_stmt          (ssaProcedure *proc, AstNode *node);
-void      ssa_build_cond          (ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block);
-void      ssa_build_defer_stmt    (ssaProcedure *proc, ssaDefer d);
-ssaAddr   ssa_build_addr          (ssaProcedure *proc, AstNode *expr);
-void      ssa_build_proc          (ssaValue *value, ssaProcedure *parent);
-void      ssa_gen_global_type_name(ssaModule *m, Entity *e, String name);
-
-
-
-
-ssaValue *ssa_alloc_value(gbAllocator a, ssaValueKind kind) {
-	ssaValue *v = gb_alloc_item(a, ssaValue);
-	v->kind = kind;
-	return v;
-}
-ssaValue *ssa_alloc_instr(ssaProcedure *proc, ssaInstrKind kind) {
-	ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Instr);
-	v->Instr.kind = kind;
-	proc->instr_count++;
-	return v;
-}
-ssaDebugInfo *ssa_alloc_debug_info(gbAllocator a, ssaDebugInfoKind kind) {
-	ssaDebugInfo *di = gb_alloc_item(a, ssaDebugInfo);
-	di->kind = kind;
-	return di;
-}
-
-
-
-
-ssaValue *ssa_make_value_type_name(gbAllocator a, String name, Type *type) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_TypeName);
-	v->TypeName.name = name;
-	v->TypeName.type = type;
-	return v;
-}
-
-ssaValue *ssa_make_value_global(gbAllocator a, Entity *e, ssaValue *value) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_Global);
-	v->Global.entity = e;
-	v->Global.type = make_type_pointer(a, e->type);
-	v->Global.value = value;
-	array_init(&v->Global.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
-	return v;
-}
-ssaValue *ssa_make_value_param(gbAllocator a, ssaProcedure *parent, Entity *e) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_Param);
-	v->Param.parent = parent;
-	v->Param.entity = e;
-	v->Param.type   = e->type;
-	array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
-	return v;
-}
-ssaValue *ssa_make_value_nil(gbAllocator a, Type *type) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_Nil);
-	v->Nil.type = type;
-	return v;
-}
-
-
-
-ssaValue *ssa_make_instr_local(ssaProcedure *p, Entity *e, bool zero_initialized) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Local);
-	ssaInstr *i = &v->Instr;
-	i->Local.entity = e;
-	i->Local.type = make_type_pointer(p->module->allocator, e->type);
-	i->Local.zero_initialized = zero_initialized;
-	array_init(&i->Local.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
-	ssa_module_add_value(p->module, e, v);
-	return v;
-}
-
-
-ssaValue *ssa_make_instr_store(ssaProcedure *p, ssaValue *address, ssaValue *value) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Store);
-	ssaInstr *i = &v->Instr;
-	i->Store.address = address;
-	i->Store.value = value;
-	return v;
-}
-
-ssaValue *ssa_make_instr_zero_init(ssaProcedure *p, ssaValue *address) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_ZeroInit);
-	ssaInstr *i = &v->Instr;
-	i->ZeroInit.address = address;
-	return v;
-}
-
-ssaValue *ssa_make_instr_load(ssaProcedure *p, ssaValue *address) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Load);
-	ssaInstr *i = &v->Instr;
-	i->Load.address = address;
-	i->Load.type = type_deref(ssa_type(address));
-	return v;
-}
-
-ssaValue *ssa_make_instr_array_element_ptr(ssaProcedure *p, ssaValue *address, ssaValue *elem_index) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayElementPtr);
-	ssaInstr *i = &v->Instr;
-	Type *t = ssa_type(address);
-	GB_ASSERT(is_type_pointer(t));
-	t = base_type(type_deref(t));
-	GB_ASSERT(is_type_array(t) || is_type_vector(t));
-
-	Type *result_type = make_type_pointer(p->module->allocator, t->Array.elem);
-
-	i->ArrayElementPtr.address = address;
-	i->ArrayElementPtr.elem_index = elem_index;
-	i->ArrayElementPtr.result_type = result_type;
-
-	GB_ASSERT_MSG(is_type_pointer(ssa_type(address)),
-	              "%s", type_to_string(ssa_type(address)));
-	return v;
-}
-ssaValue *ssa_make_instr_struct_element_ptr(ssaProcedure *p, ssaValue *address, i32 elem_index, Type *result_type) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructElementPtr);
-	ssaInstr *i = &v->Instr;
-	i->StructElementPtr.address     = address;
-	i->StructElementPtr.elem_index  = elem_index;
-	i->StructElementPtr.result_type = result_type;
-
-	GB_ASSERT_MSG(is_type_pointer(ssa_type(address)),
-	              "%s", type_to_string(ssa_type(address)));
-	return v;
-}
-ssaValue *ssa_make_instr_ptr_offset(ssaProcedure *p, ssaValue *address, ssaValue *offset) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_PtrOffset);
-	ssaInstr *i = &v->Instr;
-	i->PtrOffset.address = address;
-	i->PtrOffset.offset  = offset;
-
-	GB_ASSERT_MSG(is_type_pointer(ssa_type(address)),
-	              "%s", type_to_string(ssa_type(address)));
-	GB_ASSERT_MSG(is_type_integer(ssa_type(offset)),
-	              "%s", type_to_string(ssa_type(address)));
-
-	return v;
-}
-
-
-
-ssaValue *ssa_make_instr_array_extract_value(ssaProcedure *p, ssaValue *address, i32 index) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayExtractValue);
-	ssaInstr *i = &v->Instr;
-	i->ArrayExtractValue.address = address;
-	i->ArrayExtractValue.index = index;
-	Type *t = base_type(ssa_type(address));
-	GB_ASSERT(is_type_array(t));
-	i->ArrayExtractValue.result_type = t->Array.elem;
-	return v;
-}
-
-ssaValue *ssa_make_instr_struct_extract_value(ssaProcedure *p, ssaValue *address, i32 index, Type *result_type) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructExtractValue);
-	ssaInstr *i = &v->Instr;
-	i->StructExtractValue.address = address;
-	i->StructExtractValue.index = index;
-	i->StructExtractValue.result_type = result_type;
-	return v;
-}
-
-ssaValue *ssa_make_instr_union_tag_ptr(ssaProcedure *p, ssaValue *address) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_UnionTagPtr);
-	ssaInstr *i = &v->Instr;
-	i->UnionTagPtr.address = address;
-	i->UnionTagPtr.type = t_int_ptr;
-	return v;
-}
-
-ssaValue *ssa_make_instr_union_tag_value(ssaProcedure *p, ssaValue *address) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_UnionTagValue);
-	ssaInstr *i = &v->Instr;
-	i->UnionTagValue.address = address;
-	i->UnionTagValue.type = t_int_ptr;
-	return v;
-}
-
-
-ssaValue *ssa_make_instr_binary_op(ssaProcedure *p, TokenKind op, ssaValue *left, ssaValue *right, Type *type) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_BinaryOp);
-	ssaInstr *i = &v->Instr;
-	i->BinaryOp.op = op;
-	i->BinaryOp.left = left;
-	i->BinaryOp.right = right;
-	i->BinaryOp.type = type;
-	return v;
-}
-
-ssaValue *ssa_make_instr_jump(ssaProcedure *p, ssaBlock *block) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Jump);
-	ssaInstr *i = &v->Instr;
-	i->Jump.block = block;
-	return v;
-}
-ssaValue *ssa_make_instr_if(ssaProcedure *p, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_If);
-	ssaInstr *i = &v->Instr;
-	i->If.cond = cond;
-	i->If.true_block = true_block;
-	i->If.false_block = false_block;
-	return v;
-}
-
-
-ssaValue *ssa_make_instr_phi(ssaProcedure *p, ssaValueArray edges, Type *type) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Phi);
-	ssaInstr *i = &v->Instr;
-	i->Phi.edges = edges;
-	i->Phi.type = type;
-	return v;
-}
-
-ssaValue *ssa_make_instr_unreachable(ssaProcedure *p) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Unreachable);
-	return v;
-}
-
-ssaValue *ssa_make_instr_return(ssaProcedure *p, ssaValue *value) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Return);
-	v->Instr.Return.value = value;
-	return v;
-}
-
-ssaValue *ssa_make_instr_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Select);
-	v->Instr.Select.cond = cond;
-	v->Instr.Select.true_value = t;
-	v->Instr.Select.false_value = f;
-	return v;
-}
-
-ssaValue *ssa_make_instr_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count, Type *result_type) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Call);
-	v->Instr.Call.value = value;
-	v->Instr.Call.args = args;
-	v->Instr.Call.arg_count = arg_count;
-	v->Instr.Call.type = result_type;
-	return v;
-}
-
-ssaValue *ssa_make_instr_conv(ssaProcedure *p, ssaConvKind kind, ssaValue *value, Type *from, Type *to) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Conv);
-	v->Instr.Conv.kind = kind;
-	v->Instr.Conv.value = value;
-	v->Instr.Conv.from = from;
-	v->Instr.Conv.to = to;
-	return v;
-}
-
-ssaValue *ssa_make_instr_extract_element(ssaProcedure *p, ssaValue *vector, ssaValue *index) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorExtractElement);
-	v->Instr.VectorExtractElement.vector = vector;
-	v->Instr.VectorExtractElement.index = index;
-	return v;
-}
-
-ssaValue *ssa_make_instr_insert_element(ssaProcedure *p, ssaValue *vector, ssaValue *elem, ssaValue *index) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorInsertElement);
-	v->Instr.VectorInsertElement.vector = vector;
-	v->Instr.VectorInsertElement.elem   = elem;
-	v->Instr.VectorInsertElement.index  = index;
-	return v;
-}
-
-ssaValue *ssa_make_instr_vector_shuffle(ssaProcedure *p, ssaValue *vector, i32 *indices, isize index_count) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorShuffle);
-	v->Instr.VectorShuffle.vector      = vector;
-	v->Instr.VectorShuffle.indices     = indices;
-	v->Instr.VectorShuffle.index_count = index_count;
-
-	Type *vt = base_type(ssa_type(vector));
-	v->Instr.VectorShuffle.type = make_type_vector(p->module->allocator, vt->Vector.elem, index_count);
-
-	return v;
-}
-
-ssaValue *ssa_make_instr_comment(ssaProcedure *p, String text) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Comment);
-	v->Instr.Comment.text = text;
-	return v;
-}
-
-ssaValue *ssa_make_instr_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *index, ssaValue *len) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_BoundsCheck);
-	v->Instr.BoundsCheck.pos   = pos;
-	v->Instr.BoundsCheck.index = index;
-	v->Instr.BoundsCheck.len   = len;
-	return v;
-}
-ssaValue *ssa_make_instr_slice_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *low, ssaValue *high, ssaValue *max, bool is_substring) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_SliceBoundsCheck);
-	v->Instr.SliceBoundsCheck.pos  = pos;
-	v->Instr.SliceBoundsCheck.low  = low;
-	v->Instr.SliceBoundsCheck.high = high;
-	v->Instr.SliceBoundsCheck.max  = max;
-	v->Instr.SliceBoundsCheck.is_substring = is_substring;
-	return v;
-}
-
-
-
-ssaValue *ssa_make_value_constant(gbAllocator a, Type *type, ExactValue value) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_Constant);
-	v->Constant.type  = type;
-	v->Constant.value = value;
-	return v;
-}
-
-
-ssaValue *ssa_make_value_constant_slice(gbAllocator a, Type *type, ssaValue *backing_array, i64 count) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_ConstantSlice);
-	v->ConstantSlice.type = type;
-	v->ConstantSlice.backing_array = backing_array;
-	v->ConstantSlice.count = count;
-	return v;
-}
-
-ssaValue *ssa_make_const_int(gbAllocator a, i64 i) {
-	return ssa_make_value_constant(a, t_int, make_exact_value_integer(i));
-}
-ssaValue *ssa_make_const_i32(gbAllocator a, i64 i) {
-	return ssa_make_value_constant(a, t_i32, make_exact_value_integer(i));
-}
-ssaValue *ssa_make_const_i64(gbAllocator a, i64 i) {
-	return ssa_make_value_constant(a, t_i64, make_exact_value_integer(i));
-}
-ssaValue *ssa_make_const_bool(gbAllocator a, bool b) {
-	return ssa_make_value_constant(a, t_bool, make_exact_value_bool(b != 0));
-}
-ssaValue *ssa_make_const_string(gbAllocator a, String s) {
-	return ssa_make_value_constant(a, t_string, make_exact_value_string(s));
-}
-
-ssaValue *ssa_make_value_procedure(gbAllocator a, ssaModule *m, Entity *entity, Type *type, AstNode *type_expr, AstNode *body, String name) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_Proc);
-	v->Proc.module = m;
-	v->Proc.entity = entity;
-	v->Proc.type   = type;
-	v->Proc.type_expr = type_expr;
-	v->Proc.body   = body;
-	v->Proc.name   = name;
-	array_init(&v->Proc.referrers, heap_allocator()); // TODO(bill): replace heap allocator
-
-	Type *t = base_type(type);
-	GB_ASSERT(is_type_proc(t));
-	array_init_reserve(&v->Proc.params, heap_allocator(), t->Proc.param_count);
-
-	return v;
-}
-
-ssaBlock *ssa_add_block(ssaProcedure *proc, AstNode *node, char *label) {
-	Scope *scope = NULL;
-	if (node != NULL) {
-		Scope **found = map_scope_get(&proc->module->info->scopes, hash_pointer(node));
-		if (found) {
-			scope = *found;
-		} else {
-			GB_PANIC("Block scope not found for %.*s", LIT(ast_node_strings[node->kind]));
-		}
-	}
-
-	ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Block);
-	v->Block.label  = make_string_c(label);
-	v->Block.node   = node;
-	v->Block.scope  = scope;
-	v->Block.parent = proc;
-
-	array_init(&v->Block.instrs, heap_allocator());
-	array_init(&v->Block.locals, heap_allocator());
-
-	array_init(&v->Block.preds,  heap_allocator());
-	array_init(&v->Block.succs,  heap_allocator());
-
-	ssaBlock *block = &v->Block;
-
-	array_add(&proc->blocks, block);
-	proc->block_count++;
-
-	return block;
-}
-
-
-
-
-
-ssaDefer ssa_add_defer_node(ssaProcedure *proc, isize scope_index, AstNode *stmt) {
-	ssaDefer d = {ssaDefer_Node};
-	d.scope_index = scope_index;
-	d.block = proc->curr_block;
-	d.stmt = stmt;
-	array_add(&proc->defer_stmts, d);
-	return d;
-}
-
-
-ssaDefer ssa_add_defer_instr(ssaProcedure *proc, isize scope_index, ssaValue *instr) {
-	ssaDefer d = {ssaDefer_Instr};
-	d.scope_index = proc->scope_index;
-	d.block = proc->curr_block;
-	d.instr = instr; // NOTE(bill): It will make a copy everytime it is called
-	array_add(&proc->defer_stmts, d);
-	return d;
-}
-
-
-
-ssaValue *ssa_add_module_constant(ssaModule *m, Type *type, ExactValue value) {
-	gbAllocator a = m->allocator;
-	// gbAllocator a = gb_heap_allocator();
-
-	if (is_type_slice(type)) {
-		ast_node(cl, CompoundLit, value.value_compound);
-
-		isize count = cl->elems.count;
-		if (count == 0) {
-			return ssa_make_value_nil(a, type);
-		}
-		Type *elem = base_type(type)->Slice.elem;
-		Type *t = make_type_array(a, elem, count);
-		ssaValue *backing_array = ssa_add_module_constant(m, t, value);
-
-
-		isize max_len = 7+8+1;
-		u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len);
-		isize len = gb_snprintf(cast(char *)str, max_len, "__csba$%x", m->global_array_index);
-		m->global_array_index++;
-
-		String name = make_string(str, len-1);
-
-		Entity *e = make_entity_constant(a, NULL, make_token_ident(name), t, value);
-		ssaValue *g = ssa_make_value_global(a, e, backing_array);
-		ssa_module_add_value(m, e, g);
-		map_ssa_value_set(&m->members, hash_string(name), g);
-
-		return ssa_make_value_constant_slice(a, type, g, count);
-	}
-
-	return ssa_make_value_constant(a, type, value);
-}
-
-ssaValue *ssa_add_global_string_array(ssaModule *m, String string) {
-	// TODO(bill): Should this use the arena allocator or the heap allocator?
-	// Strings could be huge!
-	gbAllocator a = m->allocator;
-	// gbAllocator a = gb_heap_allocator();
-
-	isize max_len = 6+8+1;
-	u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len);
-	isize len = gb_snprintf(cast(char *)str, max_len, "__str$%x", m->global_string_index);
-	m->global_string_index++;
-
-	String name = make_string(str, len-1);
-	Token token = {Token_String};
-	token.string = name;
-	Type *type = make_type_array(a, t_u8, string.len);
-	ExactValue ev = make_exact_value_string(string);
-	Entity *entity = make_entity_constant(a, NULL, token, type, ev);
-	ssaValue *g = ssa_make_value_global(a, entity, ssa_add_module_constant(m, type, ev));
-	g->Global.is_private      = true;
-	// g->Global.is_unnamed_addr = true;
-	// g->Global.is_constant = true;
-
-	ssa_module_add_value(m, entity, g);
-	map_ssa_value_set(&m->members, hash_string(name), g);
-
-	return g;
-}
-
-
-
-
-ssaValue *ssa_add_local(ssaProcedure *proc, Entity *e) {
-	ssaBlock *b = proc->decl_block; // all variables must be in the first block
-	ssaValue *instr = ssa_make_instr_local(proc, e, true);
-	instr->Instr.parent = b;
-	array_add(&b->instrs, instr);
-	array_add(&b->locals, instr);
-	proc->local_count++;
-
-	// if (zero_initialized) {
-		ssa_emit_zero_init(proc, instr);
-	// }
-
-	return instr;
-}
-
-ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name, bool zero_initialized) {
-	Entity **found = map_entity_get(&proc->module->info->definitions, hash_pointer(name));
-	if (found) {
-		Entity *e = *found;
-		ssa_emit_comment(proc, e->token.string);
-		return ssa_add_local(proc, e);
-	}
-	return NULL;
-}
-
-ssaValue *ssa_add_local_generated(ssaProcedure *proc, Type *type) {
-	GB_ASSERT(type != NULL);
-
-	Scope *scope = NULL;
-	if (proc->curr_block) {
-		scope = proc->curr_block->scope;
-	}
-	Entity *e = make_entity_variable(proc->module->allocator,
-	                                 scope,
-	                                 empty_token,
-	                                 type);
-	return ssa_add_local(proc, e);
-}
-
-ssaValue *ssa_add_param(ssaProcedure *proc, Entity *e) {
-	ssaValue *v = ssa_make_value_param(proc->module->allocator, proc, e);
-#if 1
-	ssaValue *l = ssa_add_local(proc, e);
-	ssa_emit_store(proc, l, v);
-#else
-	ssa_module_add_value(proc->module, e, v);
-#endif
-	return v;
-}
-
-
-
-////////////////////////////////////////////////////////////////
-//
-// @Debug
-//
-////////////////////////////////////////////////////////////////
-
-ssaDebugInfo *ssa_add_debug_info_file(ssaProcedure *proc, AstFile *file) {
-	if (!proc->module->generate_debug_info) {
-		return NULL;
-	}
-
-	GB_ASSERT(file != NULL);
-	ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_File);
-	di->File.file = file;
-
-	String filename = file->tokenizer.fullpath;
-	String directory = filename;
-	isize slash_index = 0;
-	for (isize i = filename.len-1; i >= 0; i--) {
-		if (filename.text[i] == '\\' ||
-		    filename.text[i] == '/') {
-			break;
-		}
-		slash_index = i;
-	}
-	directory.len = slash_index-1;
-	filename.text = filename.text + slash_index;
-	filename.len -= slash_index;
-
-
-	di->File.filename = filename;
-	di->File.directory = directory;
-
-	map_ssa_debug_info_set(&proc->module->debug_info, hash_pointer(file), di);
-	return di;
-}
-
-
-ssaDebugInfo *ssa_add_debug_info_proc(ssaProcedure *proc, Entity *entity, String name, ssaDebugInfo *file) {
-	if (!proc->module->generate_debug_info) {
-		return NULL;
-	}
-
-	GB_ASSERT(entity != NULL);
-	ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_Proc);
-	di->Proc.entity = entity;
-	di->Proc.name = name;
-	di->Proc.file = file;
-	di->Proc.pos = entity->token.pos;
-
-	map_ssa_debug_info_set(&proc->module->debug_info, hash_pointer(entity), di);
-	return di;
-}
-
-////////////////////////////////////////////////////////////////
-//
-// @Emit
-//
-////////////////////////////////////////////////////////////////
-
-
-ssaValue *ssa_emit(ssaProcedure *proc, ssaValue *instr) {
-	GB_ASSERT(instr->kind == ssaValue_Instr);
-	ssaBlock *b = proc->curr_block;
-	instr->Instr.parent = b;
-	if (b != NULL) {
-		ssaInstr *i = ssa_get_last_instr(b);
-		if (!ssa_is_instr_terminating(i)) {
-			array_add(&b->instrs, instr);
-		}
-	}
-	return instr;
-}
-ssaValue *ssa_emit_store(ssaProcedure *p, ssaValue *address, ssaValue *value) {
-	return ssa_emit(p, ssa_make_instr_store(p, address, value));
-}
-ssaValue *ssa_emit_load(ssaProcedure *p, ssaValue *address) {
-	return ssa_emit(p, ssa_make_instr_load(p, address));
-}
-ssaValue *ssa_emit_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) {
-	return ssa_emit(p, ssa_make_instr_select(p, cond, t, f));
-}
-
-ssaValue *ssa_emit_zero_init(ssaProcedure *p, ssaValue *address)  {
-	return ssa_emit(p, ssa_make_instr_zero_init(p, address));
-}
-
-ssaValue *ssa_emit_comment(ssaProcedure *p, String text) {
-	return ssa_emit(p, ssa_make_instr_comment(p, text));
-}
-
-
-ssaValue *ssa_emit_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count) {
-	Type *pt = base_type(ssa_type(value));
-	GB_ASSERT(pt->kind == Type_Proc);
-	Type *results = pt->Proc.results;
-	return ssa_emit(p, ssa_make_instr_call(p, value, args, arg_count, results));
-}
-
-ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name_, ssaValue **args, isize arg_count) {
-	String name = make_string_c(name_);
-	ssaValue **found = map_ssa_value_get(&proc->module->members, hash_string(name));
-	GB_ASSERT_MSG(found != NULL, "%.*s", LIT(name));
-	ssaValue *gp = *found;
-	return ssa_emit_call(proc, gp, args, arg_count);
-}
-
-
-
-void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) {
-	isize count = proc->defer_stmts.count;
-	isize i = count;
-	while (i --> 0) {
-		ssaDefer d = proc->defer_stmts.e[i];
-		if (kind == ssaDeferExit_Default) {
-			if (proc->scope_index == d.scope_index &&
-			    d.scope_index > 1) {
-				ssa_build_defer_stmt(proc, d);
-				array_pop(&proc->defer_stmts);
-				continue;
-			} else {
-				break;
-			}
-		} else if (kind == ssaDeferExit_Return) {
-			ssa_build_defer_stmt(proc, d);
-		} else if (kind == ssaDeferExit_Branch) {
-			GB_ASSERT(block != NULL);
-			isize lower_limit = block->scope_index+1;
-			if (lower_limit < d.scope_index) {
-				ssa_build_defer_stmt(proc, d);
-			}
-		}
-	}
-}
-
-
-void ssa_open_scope(ssaProcedure *proc) {
-	proc->scope_index++;
-}
-
-void ssa_close_scope(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) {
-	ssa_emit_defer_stmts(proc, kind, block);
-	GB_ASSERT(proc->scope_index > 0);
-	proc->scope_index--;
-}
-
-
-
-void ssa_emit_unreachable(ssaProcedure *proc) {
-	ssa_emit(proc, ssa_make_instr_unreachable(proc));
-}
-
-void ssa_emit_return(ssaProcedure *proc, ssaValue *v) {
-	ssa_emit_defer_stmts(proc, ssaDeferExit_Return, NULL);
-	ssa_emit(proc, ssa_make_instr_return(proc, v));
-}
-
-void ssa_emit_jump(ssaProcedure *proc, ssaBlock *target_block) {
-	ssaBlock *b = proc->curr_block;
-	if (b == NULL) {
-		return;
-	}
-	ssa_emit(proc, ssa_make_instr_jump(proc, target_block));
-	ssa_add_edge(b, target_block);
-	proc->curr_block = NULL;
-}
-
-void ssa_emit_if(ssaProcedure *proc, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) {
-	ssaBlock *b = proc->curr_block;
-	if (b == NULL) {
-		return;
-	}
-	ssa_emit(proc, ssa_make_instr_if(proc, cond, true_block, false_block));
-	ssa_add_edge(b, true_block);
-	ssa_add_edge(b, false_block);
-	proc->curr_block = NULL;
-}
-
-void ssa_emit_startup_runtime(ssaProcedure *proc) {
-	GB_ASSERT(proc->parent == NULL && str_eq(proc->name, str_lit("main")));
-	ssa_emit(proc, ssa_alloc_instr(proc, ssaInstr_StartupRuntime));
-}
-
-
-
-
-ssaValue *ssa_addr_store(ssaProcedure *proc, ssaAddr addr, ssaValue *value) {
-	if (addr.addr == NULL) {
-		return NULL;
-	}
-
-	if (addr.kind == ssaAddr_Vector) {
-		ssaValue *v = ssa_emit_load(proc, addr.addr);
-		Type *elem_type = base_type(ssa_type(v))->Vector.elem;
-		ssaValue *elem = ssa_emit_conv(proc, value, elem_type);
-		ssaValue *out = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, elem, addr.Vector.index));
-		return ssa_emit_store(proc, addr.addr, out);
-	} else {
-		ssaValue *v = ssa_emit_conv(proc, value, ssa_addr_type(addr));
-		return ssa_emit_store(proc, addr.addr, v);
-	}
-}
-ssaValue *ssa_addr_load(ssaProcedure *proc, ssaAddr addr) {
-	if (addr.addr == NULL) {
-		GB_PANIC("Illegal addr load");
-		return NULL;
-	}
-
-	if (addr.kind == ssaAddr_Vector) {
-		ssaValue *v = ssa_emit_load(proc, addr.addr);
-		return ssa_emit(proc, ssa_make_instr_extract_element(proc, v, addr.Vector.index));
-	}
-	Type *t = base_type(ssa_type(addr.addr));
-	if (t->kind == Type_Proc) {
-		// NOTE(bill): Imported procedures don't require a load as they are pointers
-		return addr.addr;
-	}
-	return ssa_emit_load(proc, addr.addr);
-}
-
-
-
-
-ssaValue *ssa_emit_ptr_offset(ssaProcedure *proc, ssaValue *ptr, ssaValue *offset) {
-	offset = ssa_emit_conv(proc, offset, t_int);
-	return ssa_emit(proc, ssa_make_instr_ptr_offset(proc, ptr, offset));
-}
-
-ssaValue *ssa_emit_arith(ssaProcedure *proc, TokenKind op, ssaValue *left, ssaValue *right, Type *type) {
-	Type *t_left = ssa_type(left);
-	Type *t_right = ssa_type(right);
-
-	if (op == Token_Add) {
-		if (is_type_pointer(t_left)) {
-			ssaValue *ptr = ssa_emit_conv(proc, left, type);
-			ssaValue *offset = right;
-			return ssa_emit_ptr_offset(proc, ptr, offset);
-		} else if (is_type_pointer(ssa_type(right))) {
-			ssaValue *ptr = ssa_emit_conv(proc, right, type);
-			ssaValue *offset = left;
-			return ssa_emit_ptr_offset(proc, ptr, offset);
-		}
-	} else if (op == Token_Sub) {
-		if (is_type_pointer(t_left) && is_type_integer(t_right)) {
-			// ptr - int
-			ssaValue *ptr = ssa_emit_conv(proc, left, type);
-			ssaValue *offset = right;
-			return ssa_emit_ptr_offset(proc, ptr, offset);
-		} else if (is_type_pointer(t_left) && is_type_pointer(t_right)) {
-			GB_ASSERT(is_type_integer(type));
-			Type *ptr_type = t_left;
-			ssaModule *m = proc->module;
-			ssaValue *x = ssa_emit_conv(proc, left, type);
-			ssaValue *y = ssa_emit_conv(proc, right, type);
-			ssaValue *diff = ssa_emit_arith(proc, op, x, y, type);
-			ssaValue *elem_size = ssa_make_const_int(m->allocator, type_size_of(m->sizes, m->allocator, ptr_type));
-			return ssa_emit_arith(proc, Token_Quo, diff, elem_size, type);
-		}
-	}
-
-
-	switch (op) {
-	case Token_AndNot: {
-		// NOTE(bill): x &~ y == x & (~y) == x & (y ~ -1)
-		// NOTE(bill): "not" `x` == `x` "xor" `-1`
-		ssaValue *neg = ssa_add_module_constant(proc->module, type, make_exact_value_integer(-1));
-		op = Token_Xor;
-		right = ssa_emit_arith(proc, op, right, neg, type);
-		GB_ASSERT(right->Instr.kind == ssaInstr_BinaryOp);
-		right->Instr.BinaryOp.type = type;
-		op = Token_And;
-	} /* fallthrough */
-	case Token_Add:
-	case Token_Sub:
-	case Token_Mul:
-	case Token_Quo:
-	case Token_Mod:
-	case Token_And:
-	case Token_Or:
-	case Token_Xor:
-	case Token_Shl:
-	case Token_Shr:
-		left  = ssa_emit_conv(proc, left, type);
-		right = ssa_emit_conv(proc, right, type);
-		break;
-	}
-
-	return ssa_emit(proc, ssa_make_instr_binary_op(proc, op, left, right, type));
-}
-
-ssaValue *ssa_emit_comp(ssaProcedure *proc, TokenKind op_kind, ssaValue *left, ssaValue *right) {
-	Type *a = base_type(ssa_type(left));
-	Type *b = base_type(ssa_type(right));
-
-	if (are_types_identical(a, b)) {
-		// NOTE(bill): No need for a conversion
-	} else if (left->kind == ssaValue_Constant || left->kind == ssaValue_Nil) {
-		left = ssa_emit_conv(proc, left, ssa_type(right));
-	} else if (right->kind == ssaValue_Constant || right->kind == ssaValue_Nil) {
-		right = ssa_emit_conv(proc, right, ssa_type(left));
-	}
-
-	Type *result = t_bool;
-	if (is_type_vector(a)) {
-		result = make_type_vector(proc->module->allocator, t_bool, a->Vector.count);
-	}
-	return ssa_emit(proc, ssa_make_instr_binary_op(proc, op_kind, left, right, result));
-}
-
-ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, ssaValue *index) {
-	GB_ASSERT(index != NULL);
-	Type *st = base_type(type_deref(ssa_type(s)));
-	GB_ASSERT(is_type_array(st) || is_type_vector(st));
-
-	// NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32
-	index = ssa_emit_conv(proc, index, t_i32);
-	return ssa_emit(proc, ssa_make_instr_array_element_ptr(proc, s, index));
-}
-
-ssaValue *ssa_emit_array_epi(ssaProcedure *proc, ssaValue *s, i32 index) {
-	return ssa_emit_array_ep(proc, s, ssa_make_const_i32(proc->module->allocator, index));
-}
-
-ssaValue *ssa_emit_union_tag_ptr(ssaProcedure *proc, ssaValue *u) {
-	Type *t = ssa_type(u);
-	GB_ASSERT(is_type_pointer(t) &&
-	          is_type_union(type_deref(t)));
-	return ssa_emit(proc, ssa_make_instr_union_tag_ptr(proc, u));
-}
-
-ssaValue *ssa_emit_union_tag_value(ssaProcedure *proc, ssaValue *u) {
-	Type *t = ssa_type(u);
-	GB_ASSERT(is_type_union(t));
-	return ssa_emit(proc, ssa_make_instr_union_tag_value(proc, u));
-}
-
-
-
-ssaValue *ssa_emit_struct_ep(ssaProcedure *proc, ssaValue *s, i32 index) {
-	gbAllocator a = proc->module->allocator;
-	Type *t = base_type(type_deref(ssa_type(s)));
-	Type *result_type = NULL;
-	ssaValue *gep = NULL;
-
-	if (is_type_struct(t)) {
-		GB_ASSERT(t->Record.field_count > 0);
-		GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
-		result_type = make_type_pointer(a, t->Record.fields[index]->type);
-	} else if (is_type_tuple(t)) {
-		GB_ASSERT(t->Tuple.variable_count > 0);
-		GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
-		result_type = make_type_pointer(a, t->Tuple.variables[index]->type);
-	} else if (is_type_slice(t)) {
-		switch (index) {
-		case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break;
-		case 1: result_type = make_type_pointer(a, t_int); break;
-		case 2: result_type = make_type_pointer(a, t_int); break;
-		}
-	} else if (is_type_string(t)) {
-		switch (index) {
-		case 0: result_type = make_type_pointer(a, t_u8_ptr); break;
-		case 1: result_type = make_type_pointer(a, t_int);    break;
-		}
-	} else if (is_type_any(t)) {
-		switch (index) {
-		case 0: result_type = make_type_pointer(a, t_type_info_ptr); break;
-		case 1: result_type = make_type_pointer(a, t_rawptr);        break;
-		}
-	} else if (is_type_maybe(t)) {
-		switch (index) {
-		case 0: result_type = make_type_pointer(a, t->Maybe.elem); break;
-		case 1: result_type = make_type_pointer(a, t_bool);        break;
-		}
-	} else {
-		GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(ssa_type(s)), index);
-	}
-
-	GB_ASSERT(result_type != NULL);
-
-	gep = ssa_make_instr_struct_element_ptr(proc, s, index, result_type);
-	return ssa_emit(proc, gep);
-}
-
-
-
-ssaValue *ssa_emit_array_ev(ssaProcedure *proc, ssaValue *s, i32 index) {
-	Type *st = base_type(ssa_type(s));
-	GB_ASSERT(is_type_array(st));
-	return ssa_emit(proc, ssa_make_instr_array_extract_value(proc, s, index));
-}
-
-ssaValue *ssa_emit_struct_ev(ssaProcedure *proc, ssaValue *s, i32 index) {
-	// NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32
-
-	gbAllocator a = proc->module->allocator;
-	Type *t = base_type(ssa_type(s));
-	Type *result_type = NULL;
-
-	if (is_type_struct(t)) {
-		GB_ASSERT(t->Record.field_count > 0);
-		GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
-		result_type = t->Record.fields[index]->type;
-	} else if (is_type_tuple(t)) {
-		GB_ASSERT(t->Tuple.variable_count > 0);
-		GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
-		result_type = t->Tuple.variables[index]->type;
-	} else if (is_type_slice(t)) {
-		switch (index) {
-		case 0: result_type = make_type_pointer(a, t->Slice.elem); break;
-		case 1: result_type = t_int; break;
-		case 2: result_type = t_int; break;
-		}
-	} else if (is_type_string(t)) {
-		switch (index) {
-		case 0: result_type = t_u8_ptr; break;
-		case 1: result_type = t_int;    break;
-		}
-	} else if (is_type_any(t)) {
-		switch (index) {
-		case 0: result_type = t_type_info_ptr; break;
-		case 1: result_type = t_rawptr;        break;
-		}
-	} else if (is_type_maybe(t)) {
-		switch (index) {
-		case 0: result_type = t->Maybe.elem; break;
-		case 1: result_type = t_bool;        break;
-		}
-	} else {
-		GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(ssa_type(s)), index);
-	}
-
-	GB_ASSERT(result_type != NULL);
-
-	return ssa_emit(proc, ssa_make_instr_struct_extract_value(proc, s, index, result_type));
-}
-
-
-ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) {
-	GB_ASSERT(sel.index.count > 0);
-
-	for_array(i, sel.index) {
-		i32 index = cast(i32)sel.index.e[i];
-		if (is_type_pointer(type)) {
-			type = type_deref(type);
-			e = ssa_emit_load(proc, e);
-			e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies?
-		}
-		type = base_type(type);
-
-
-		if (is_type_raw_union(type)) {
-			type = type->Record.fields[index]->type;
-			e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type));
-		} else if (type->kind == Type_Record) {
-			type = type->Record.fields[index]->type;
-			e = ssa_emit_struct_ep(proc, e, index);
-		} else if (type->kind == Type_Basic) {
-			switch (type->Basic.kind) {
-			case Basic_any: {
-				if (index == 0) {
-					type = t_type_info_ptr;
-				} else if (index == 1) {
-					type = t_rawptr;
-				}
-				e = ssa_emit_struct_ep(proc, e, index);
-			} break;
-
-			case Basic_string:
-				e = ssa_emit_struct_ep(proc, e, index);
-				break;
-
-			default:
-				GB_PANIC("un-gep-able type");
-				break;
-			}
-		} else if (type->kind == Type_Slice) {
-			e = ssa_emit_struct_ep(proc, e, index);
-		} else if (type->kind == Type_Vector) {
-			e = ssa_emit_array_epi(proc, e, index);
-		} else if (type->kind == Type_Array) {
-			e = ssa_emit_array_epi(proc, e, index);
-		} else {
-			GB_PANIC("un-gep-able type");
-		}
-	}
-
-	return e;
-}
-
-
-ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) {
-	GB_ASSERT(sel.index.count > 0);
-
-	for_array(i, sel.index) {
-		i32 index = cast(i32)sel.index.e[i];
-		if (is_type_pointer(type)) {
-			type = type_deref(type);
-			e = ssa_emit_load(proc, e);
-			e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies?
-		}
-		type = base_type(type);
-
-
-		if (is_type_raw_union(type)) {
-			GB_PANIC("TODO(bill): IS THIS EVEN CORRECT?");
-			type = type->Record.fields[index]->type;
-			e = ssa_emit_conv(proc, e, type);
-		} else {
-			e = ssa_emit_struct_ev(proc, e, index);
-		}
-	}
-
-	return e;
-}
-
-
-
-
-ssaValue *ssa_array_elem(ssaProcedure *proc, ssaValue *array) {
-	return ssa_emit_array_ep(proc, array, v_zero32);
-}
-ssaValue *ssa_array_len(ssaProcedure *proc, ssaValue *array) {
-	Type *t = ssa_type(array);
-	GB_ASSERT(t->kind == Type_Array);
-	return ssa_make_const_int(proc->module->allocator, t->Array.count);
-}
-ssaValue *ssa_array_cap(ssaProcedure *proc, ssaValue *array) {
-	return ssa_array_len(proc, array);
-}
-
-ssaValue *ssa_slice_elem(ssaProcedure *proc, ssaValue *slice) {
-	Type *t = ssa_type(slice);
-	GB_ASSERT(t->kind == Type_Slice);
-	return ssa_emit_struct_ev(proc, slice, 0);
-}
-ssaValue *ssa_slice_len(ssaProcedure *proc, ssaValue *slice) {
-	Type *t = ssa_type(slice);
-	GB_ASSERT(t->kind == Type_Slice);
-	return ssa_emit_struct_ev(proc, slice, 1);
-}
-ssaValue *ssa_slice_cap(ssaProcedure *proc, ssaValue *slice) {
-	Type *t = ssa_type(slice);
-	GB_ASSERT(t->kind == Type_Slice);
-	return ssa_emit_struct_ev(proc, slice, 2);
-}
-
-ssaValue *ssa_string_elem(ssaProcedure *proc, ssaValue *string) {
-	Type *t = ssa_type(string);
-	GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
-	return ssa_emit_struct_ev(proc, string, 0);
-}
-ssaValue *ssa_string_len(ssaProcedure *proc, ssaValue *string) {
-	Type *t = ssa_type(string);
-	GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
-	return ssa_emit_struct_ev(proc, string, 1);
-}
-
-
-
-ssaValue *ssa_add_local_slice(ssaProcedure *proc, Type *slice_type, ssaValue *base, ssaValue *low, ssaValue *high, ssaValue *max) {
-	// TODO(bill): array bounds checking for slice creation
-	// TODO(bill): check that low < high <= max
-	gbAllocator a = proc->module->allocator;
-	Type *bt = base_type(ssa_type(base));
-
-	if (low == NULL) {
-		low = v_zero;
-	}
-	if (high == NULL) {
-		switch (bt->kind) {
-		case Type_Array:   high = ssa_array_len(proc, base); break;
-		case Type_Slice:   high = ssa_slice_len(proc, base); break;
-		case Type_Pointer: high = v_one;                     break;
-		}
-	}
-	if (max == NULL) {
-		switch (bt->kind) {
-		case Type_Array:   max = ssa_array_cap(proc, base); break;
-		case Type_Slice:   max = ssa_slice_cap(proc, base); break;
-		case Type_Pointer: max = high;                      break;
-		}
-	}
-	GB_ASSERT(max != NULL);
-
-	ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
-	ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max,  low, t_int);
-
-	ssaValue *elem = NULL;
-	switch (bt->kind) {
-	case Type_Array:   elem = ssa_array_elem(proc, base); break;
-	case Type_Slice:   elem = ssa_slice_elem(proc, base); break;
-	case Type_Pointer: elem = ssa_emit_load(proc, base);  break;
-	}
-
-	elem = ssa_emit_ptr_offset(proc, elem, low);
-
-	ssaValue *slice = ssa_add_local_generated(proc, slice_type);
-
-	ssaValue *gep = NULL;
-	gep = ssa_emit_struct_ep(proc, slice, 0);
-	ssa_emit_store(proc, gep, elem);
-
-	gep = ssa_emit_struct_ep(proc, slice, 1);
-	ssa_emit_store(proc, gep, len);
-
-	gep = ssa_emit_struct_ep(proc, slice, 2);
-	ssa_emit_store(proc, gep, cap);
-
-	return slice;
-}
-
-ssaValue *ssa_emit_string(ssaProcedure *proc, ssaValue *elem, ssaValue *len) {
-	ssaValue *str = ssa_add_local_generated(proc, t_string);
-	ssaValue *str_elem = ssa_emit_struct_ep(proc, str, 0);
-	ssaValue *str_len = ssa_emit_struct_ep(proc, str, 1);
-	ssa_emit_store(proc, str_elem, elem);
-	ssa_emit_store(proc, str_len, len);
-	return ssa_emit_load(proc, str);
-}
-
-
-
-
-String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
-	Type *prev_src = src;
-	// Type *prev_dst = dst;
-	src = base_type(type_deref(src));
-	// dst = base_type(type_deref(dst));
-	bool src_is_ptr = src != prev_src;
-	// bool dst_is_ptr = dst != prev_dst;
-
-	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->flags & EntityFlag_Anonymous) {
-			if (are_types_identical(dst, f->type)) {
-				return f->token.string;
-			}
-			if (src_is_ptr && is_type_pointer(dst)) {
-				if (are_types_identical(type_deref(dst), f->type)) {
-					return f->token.string;
-				}
-			}
-			if (is_type_struct(f->type)) {
-				String name = lookup_polymorphic_field(info, dst, f->type);
-				if (name.len > 0) {
-					return name;
-				}
-			}
-		}
-	}
-	return str_lit("");
-}
-
-ssaValue *ssa_emit_bitcast(ssaProcedure *proc, ssaValue *data, Type *type) {
-	return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, data, ssa_type(data), type));
-}
-
-
-ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t) {
-	Type *src_type = ssa_type(value);
-	if (are_types_identical(t, src_type)) {
-		return value;
-	}
-
-	Type *src = base_type(get_enum_base_type(src_type));
-	Type *dst = base_type(get_enum_base_type(t));
-
-	if (value->kind == ssaValue_Constant) {
-		if (is_type_any(dst)) {
-			ssaValue *default_value = ssa_add_local_generated(proc, default_type(src_type));
-			ssa_emit_store(proc, default_value, value);
-			return ssa_emit_conv(proc, ssa_emit_load(proc, default_value), t_any);
-		} else if (dst->kind == Type_Basic) {
-			ExactValue ev = value->Constant.value;
-			if (is_type_float(dst)) {
-				ev = exact_value_to_float(ev);
-			} else if (is_type_string(dst)) {
-				// Handled elsewhere
-				GB_ASSERT(ev.kind == ExactValue_String);
-			} else if (is_type_integer(dst)) {
-				ev = exact_value_to_integer(ev);
-			} else if (is_type_pointer(dst)) {
-				// IMPORTANT NOTE(bill): LLVM doesn't support pointer constants expect `null`
-				ssaValue *i = ssa_add_module_constant(proc->module, t_uint, ev);
-				return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, i, t_uint, dst));
-			}
-			return ssa_add_module_constant(proc->module, t, ev);
-		}
-	}
-
-	if (are_types_identical(src, dst)) {
-		return value;
-	}
-
-	if (is_type_maybe(dst)) {
-		ssaValue *maybe = ssa_add_local_generated(proc, dst);
-		ssaValue *val = ssa_emit_struct_ep(proc, maybe, 0);
-		ssaValue *set = ssa_emit_struct_ep(proc, maybe, 1);
-		ssa_emit_store(proc, val, value);
-		ssa_emit_store(proc, set, v_true);
-		return ssa_emit_load(proc, maybe);
-	}
-
-	// integer -> integer
-	if (is_type_integer(src) && is_type_integer(dst)) {
-		GB_ASSERT(src->kind == Type_Basic &&
-		          dst->kind == Type_Basic);
-		i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
-		i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
-		if (sz == dz) {
-			// NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment
-			return value;
-		}
-
-		ssaConvKind kind = ssaConv_trunc;
-		if (dz >= sz) {
-			kind = ssaConv_zext;
-		}
-		return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
-	}
-
-	// boolean -> integer
-	if (is_type_boolean(src) && is_type_integer(dst)) {
-		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_zext, value, src, dst));
-	}
-
-	// integer -> boolean
-	if (is_type_integer(src) && is_type_boolean(dst)) {
-		return ssa_emit_comp(proc, Token_NotEq, value, v_zero);
-	}
-
-
-	// float -> float
-	if (is_type_float(src) && is_type_float(dst)) {
-		i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
-		i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
-		ssaConvKind kind = ssaConv_fptrunc;
-		if (dz >= sz) {
-			kind = ssaConv_fpext;
-		}
-		return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
-	}
-
-	// float <-> integer
-	if (is_type_float(src) && is_type_integer(dst)) {
-		ssaConvKind kind = ssaConv_fptosi;
-		if (is_type_unsigned(dst)) {
-			kind = ssaConv_fptoui;
-		}
-		return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
-	}
-	if (is_type_integer(src) && is_type_float(dst)) {
-		ssaConvKind kind = ssaConv_sitofp;
-		if (is_type_unsigned(src)) {
-			kind = ssaConv_uitofp;
-		}
-		return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
-	}
-
-	// Pointer <-> int
-	if (is_type_pointer(src) && is_type_int_or_uint(dst)) {
-		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_ptrtoint, value, src, dst));
-	}
-	if (is_type_int_or_uint(src) && is_type_pointer(dst)) {
-		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, value, src, dst));
-	}
-
-	if (is_type_union(dst)) {
-		for (isize i = 0; i < dst->Record.field_count; i++) {
-			Entity *f = dst->Record.fields[i];
-			if (are_types_identical(f->type, src_type)) {
-				ssa_emit_comment(proc, str_lit("union - child to parent"));
-				gbAllocator allocator = proc->module->allocator;
-				ssaValue *parent = ssa_add_local_generated(proc, t);
-				ssaValue *tag = ssa_make_const_int(allocator, i);
-				ssa_emit_store(proc, ssa_emit_union_tag_ptr(proc, parent), tag);
-
-				ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr);
-
-				Type *tag_type = src_type;
-				Type *tag_type_ptr = make_type_pointer(allocator, tag_type);
-				ssaValue *underlying = ssa_emit_bitcast(proc, data, tag_type_ptr);
-				ssa_emit_store(proc, underlying, value);
-
-				return ssa_emit_load(proc, parent);
-			}
-		}
-	}
-
-	// NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's
-	// subtype polymorphism casting
-	{
-		Type *sb = base_type(type_deref(src));
-		bool src_is_ptr = src != sb;
-		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) {
-				// NOTE(bill): It can be casted
-				Selection sel = lookup_field(proc->module->allocator, sb, field_name, false);
-				if (sel.entity != NULL) {
-					ssa_emit_comment(proc, str_lit("cast - polymorphism"));
-					if (src_is_ptr) {
-						value = ssa_emit_load(proc, value);
-					}
-					return ssa_emit_deep_field_ev(proc, sb, value, sel);
-				}
-			}
-		}
-	}
-
-
-
-	// Pointer <-> Pointer
-	if (is_type_pointer(src) && is_type_pointer(dst)) {
-		return ssa_emit_bitcast(proc, value, dst);
-	}
-
-
-
-	// proc <-> proc
-	if (is_type_proc(src) && is_type_proc(dst)) {
-		return ssa_emit_bitcast(proc, value, dst);
-	}
-
-	// pointer -> proc
-	if (is_type_pointer(src) && is_type_proc(dst)) {
-		return ssa_emit_bitcast(proc, value, dst);
-	}
-	// proc -> pointer
-	if (is_type_proc(src) && is_type_pointer(dst)) {
-		return ssa_emit_bitcast(proc, value, dst);
-	}
-
-
-
-	// []byte/[]u8 <-> string
-	if (is_type_u8_slice(src) && is_type_string(dst)) {
-		ssaValue *elem = ssa_slice_elem(proc, value);
-		ssaValue *len  = ssa_slice_len(proc, value);
-		return ssa_emit_string(proc, elem, len);
-	}
-	if (is_type_string(src) && is_type_u8_slice(dst)) {
-		ssaValue *elem = ssa_string_elem(proc, value);
-		ssaValue *elem_ptr = ssa_add_local_generated(proc, ssa_type(elem));
-		ssa_emit_store(proc, elem_ptr, elem);
-
-		ssaValue *len  = ssa_string_len(proc, value);
-		ssaValue *slice = ssa_add_local_slice(proc, dst, elem_ptr, v_zero, len, len);
-		return ssa_emit_load(proc, slice);
-	}
-
-	if (is_type_vector(dst)) {
-		Type *dst_elem = dst->Vector.elem;
-		value = ssa_emit_conv(proc, value, dst_elem);
-		ssaValue *v = ssa_add_local_generated(proc, t);
-		v = ssa_emit_load(proc, v);
-		v = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, value, v_zero32));
-		// NOTE(bill): Broadcast lowest value to all values
-		isize index_count = dst->Vector.count;
-		i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count);
-		for (isize i = 0; i < index_count; i++) {
-			indices[i] = 0;
-		}
-
-		v = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, v, indices, index_count));
-		return v;
-	}
-
-	if (is_type_any(dst)) {
-		ssaValue *result = ssa_add_local_generated(proc, t_any);
-
-		if (is_type_untyped_nil(src)) {
-			return ssa_emit_load(proc, result);
-		}
-
-		ssaValue *data = NULL;
-		if (value->kind == ssaValue_Instr &&
-		    value->Instr.kind == ssaInstr_Load) {
-			// NOTE(bill): Addressable value
-			data = value->Instr.Load.address;
-		} else {
-			// NOTE(bill): Non-addressable value
-			data = ssa_add_local_generated(proc, src_type);
-			ssa_emit_store(proc, data, value);
-		}
-		GB_ASSERT(is_type_pointer(ssa_type(data)));
-		GB_ASSERT(is_type_typed(src_type));
-		data = ssa_emit_conv(proc, data, t_rawptr);
-
-
-		ssaValue *ti = ssa_type_info(proc, src_type);
-
-		ssaValue *gep0 = ssa_emit_struct_ep(proc, result, 0);
-		ssaValue *gep1 = ssa_emit_struct_ep(proc, result, 1);
-		ssa_emit_store(proc, gep0, ti);
-		ssa_emit_store(proc, gep1, data);
-
-		return ssa_emit_load(proc, result);
-	}
-
-	if (is_type_untyped_nil(src) && type_has_nil(dst)) {
-		return ssa_make_value_nil(proc->module->allocator, t);
-	}
-
-
-	gb_printf_err("ssa_emit_conv: src -> dst\n");
-	gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t));
-	gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst));
-
-
-	GB_PANIC("Invalid type conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t));
-
-	return NULL;
-}
-
-bool ssa_is_type_aggregate(Type *t) {
-	t = base_type(get_enum_base_type(t));
-	switch (t->kind) {
-	case Type_Basic:
-		switch (t->Basic.kind) {
-		case Basic_string:
-		case Basic_any:
-			return true;
-		}
-		break;
-
-	case Type_Pointer:
-	case Type_Vector:
-		return false;
-
-	case Type_Array:
-	case Type_Slice:
-	case Type_Maybe:
-	case Type_Record:
-	case Type_Tuple:
-		return true;
-
-	case Type_Named:
-		return ssa_is_type_aggregate(t->Named.base);
-	}
-
-	return false;
-}
-
-ssaValue *ssa_emit_transmute(ssaProcedure *proc, ssaValue *value, Type *t) {
-	Type *src_type = ssa_type(value);
-	if (are_types_identical(t, src_type)) {
-		return value;
-	}
-
-	Type *src = base_type(src_type);
-	Type *dst = base_type(t);
-	if (are_types_identical(t, src_type)) {
-		return value;
-	}
-
-	ssaModule *m = proc->module;
-
-	i64 sz = type_size_of(m->sizes, m->allocator, src);
-	i64 dz = type_size_of(m->sizes, m->allocator, dst);
-
-	GB_ASSERT_MSG(sz == dz, "Invalid transmute conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t));
-
-	if (ssa_is_type_aggregate(src) || ssa_is_type_aggregate(dst)) {
-		ssaValue *s = ssa_add_local_generated(proc, src);
-		ssa_emit_store(proc, s, value);
-
-		ssaValue *d = ssa_emit_bitcast(proc, s, make_type_pointer(m->allocator, dst));
-		return ssa_emit_load(proc, d);
-	}
-
-	// TODO(bill): Actually figure out what the conversion needs to be correctly 'cause LLVM
-
-	return ssa_emit_bitcast(proc, value, dst);
-}
-
-ssaValue *ssa_emit_down_cast(ssaProcedure *proc, ssaValue *value, Type *t) {
-	GB_ASSERT(is_type_pointer(ssa_type(value)));
-	gbAllocator allocator = proc->module->allocator;
-
-	String field_name = check_down_cast_name(t, type_deref(ssa_type(value)));
-	GB_ASSERT(field_name.len > 0);
-	Selection sel = lookup_field(proc->module->allocator, t, field_name, false);
-	ssaValue *bytes = ssa_emit_conv(proc, value, t_u8_ptr);
-
-	i64 offset_ = type_offset_of_from_selection(proc->module->sizes, allocator, type_deref(t), sel);
-	ssaValue *offset = ssa_make_const_int(allocator, -offset_);
-	ssaValue *head = ssa_emit_ptr_offset(proc, bytes, offset);
-	return ssa_emit_conv(proc, head, t);
-}
-
-ssaValue *ssa_emit_union_cast(ssaProcedure *proc, ssaValue *value, Type *tuple) {
-	GB_ASSERT(tuple->kind == Type_Tuple);
-	gbAllocator a = proc->module->allocator;
-
-	Type *src_type = ssa_type(value);
-	bool is_ptr = is_type_pointer(src_type);
-
-	ssaValue *v = ssa_add_local_generated(proc, tuple);
-
-	if (is_ptr) {
-		Type *src = base_type(type_deref(src_type));
-		Type *src_ptr = src_type;
-		GB_ASSERT(is_type_union(src));
-		Type *dst_ptr = tuple->Tuple.variables[0]->type;
-		Type *dst = type_deref(dst_ptr);
-
-		ssaValue *tag = ssa_emit_load(proc, ssa_emit_union_tag_ptr(proc, value));
-		ssaValue *dst_tag = NULL;
-		for (isize i = 1; i < src->Record.field_count; i++) {
-			Entity *f = src->Record.fields[i];
-			if (are_types_identical(f->type, dst)) {
-				dst_tag = ssa_make_const_int(a, i);
-				break;
-			}
-		}
-		GB_ASSERT(dst_tag != NULL);
-
-		ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok");
-		ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end");
-		ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag);
-		ssa_emit_if(proc, cond, ok_block, end_block);
-		proc->curr_block = ok_block;
-
-		ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0);
-		ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1);
-
-		ssaValue *data = ssa_emit_conv(proc, value, dst_ptr);
-		ssa_emit_store(proc, gep0, data);
-		ssa_emit_store(proc, gep1, v_true);
-
-		ssa_emit_jump(proc, end_block);
-		proc->curr_block = end_block;
-
-	} else {
-		Type *src = base_type(src_type);
-		GB_ASSERT(is_type_union(src));
-		Type *dst = tuple->Tuple.variables[0]->type;
-		Type *dst_ptr = make_type_pointer(a, dst);
-
-		ssaValue *tag = ssa_emit_union_tag_value(proc, value);
-		ssaValue *dst_tag = NULL;
-		for (isize i = 1; i < src->Record.field_count; i++) {
-			Entity *f = src->Record.fields[i];
-			if (are_types_identical(f->type, dst)) {
-				dst_tag = ssa_make_const_int(a, i);
-				break;
-			}
-		}
-		GB_ASSERT(dst_tag != NULL);
-
-		// HACK(bill): This is probably not very efficient
-		ssaValue *union_copy = ssa_add_local_generated(proc, src_type);
-		ssa_emit_store(proc, union_copy, value);
-
-		ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok");
-		ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end");
-		ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag);
-		ssa_emit_if(proc, cond, ok_block, end_block);
-		proc->curr_block = ok_block;
-
-		ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0);
-		ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1);
-
-		ssaValue *data = ssa_emit_load(proc, ssa_emit_conv(proc, union_copy, dst_ptr));
-		ssa_emit_store(proc, gep0, data);
-		ssa_emit_store(proc, gep1, v_true);
-
-		ssa_emit_jump(proc, end_block);
-		proc->curr_block = end_block;
-
-	}
-	return ssa_emit_load(proc, v);
-}
-
-
-isize ssa_type_info_index(CheckerInfo *info, Type *type) {
-	type = default_type(type);
-
-	isize entry_index = -1;
-	HashKey key = hash_pointer(type);
-	isize *found_entry_index = map_isize_get(&info->type_info_map, key);
-	if (found_entry_index) {
-		entry_index = *found_entry_index;
-	}
-	if (entry_index < 0) {
-		// NOTE(bill): Do manual search
-		// TODO(bill): This is O(n) and can be very slow
-		for_array(i, info->type_info_map.entries){
-			MapIsizeEntry *e = &info->type_info_map.entries.e[i];
-			Type *prev_type = cast(Type *)e->key.ptr;
-			if (are_types_identical(prev_type, type)) {
-				entry_index = e->value;
-				// NOTE(bill): Add it to the search map
-				map_isize_set(&info->type_info_map, key, entry_index);
-				break;
-			}
-		}
-	}
-
-	if (entry_index < 0) {
-		compiler_error("Type_Info for `%s` could not be found", type_to_string(type));
-	}
-	return entry_index;
-}
-
-ssaValue *ssa_type_info(ssaProcedure *proc, Type *type) {
-	ssaValue **found = map_ssa_value_get(&proc->module->members, hash_string(str_lit(SSA_TYPE_INFO_DATA_NAME)));
-	GB_ASSERT(found != NULL);
-	ssaValue *type_info_data = *found;
-	CheckerInfo *info = proc->module->info;
-
-	type = default_type(type);
-
-	i32 entry_index = ssa_type_info_index(info, type);
-
-	// gb_printf_err("%d %s\n", entry_index, type_to_string(type));
-
-	return ssa_emit_array_ep(proc, type_info_data, ssa_make_const_i32(proc->module->allocator, entry_index));
-}
-
-
-
-ssaValue *ssa_emit_logical_binary_expr(ssaProcedure *proc, AstNode *expr) {
-	ast_node(be, BinaryExpr, expr);
-#if 0
-	ssaBlock *true_   = ssa_add_block(proc, NULL, "logical.cmp.true");
-	ssaBlock *false_  = ssa_add_block(proc, NULL, "logical.cmp.false");
-	ssaBlock *done  = ssa_add_block(proc, NULL, "logical.cmp.done");
-
-	ssaValue *result = ssa_add_local_generated(proc, t_bool);
-	ssa_build_cond(proc, expr, true_, false_);
-
-	proc->curr_block = true_;
-	ssa_emit_store(proc, result, v_true);
-	ssa_emit_jump(proc, done);
-
-	proc->curr_block = false_;
-	ssa_emit_store(proc, result, v_false);
-	ssa_emit_jump(proc, done);
-
-	proc->curr_block = done;
-
-	return ssa_emit_load(proc, result);
-#else
-	ssaBlock *rhs = ssa_add_block(proc, NULL, "logical.cmp.rhs");
-	ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done");
-
-	Type *type = type_of_expr(proc->module->info, expr);
-	type = default_type(type);
-
-	ssaValue *short_circuit = NULL;
-	if (be->op.kind == Token_CmpAnd) {
-		ssa_build_cond(proc, be->left, rhs, done);
-		short_circuit = v_false;
-	} else if (be->op.kind == Token_CmpOr) {
-		ssa_build_cond(proc, be->left, done, rhs);
-		short_circuit = v_true;
-	}
-
-	if (rhs->preds.count == 0) {
-		proc->curr_block = done;
-		return short_circuit;
-	}
-
-	if (done->preds.count == 0) {
-		proc->curr_block = rhs;
-		return ssa_build_expr(proc, be->right);
-	}
-
-	ssaValueArray edges = {0};
-	array_init_reserve(&edges, proc->module->allocator, done->preds.count+1);
-	for_array(i, done->preds) {
-		array_add(&edges, short_circuit);
-	}
-
-	proc->curr_block = rhs;
-	array_add(&edges, ssa_build_expr(proc, be->right));
-	ssa_emit_jump(proc, done);
-	proc->curr_block = done;
-
-	return ssa_emit(proc, ssa_make_instr_phi(proc, edges, type));
-#endif
-}
-
-
-void ssa_emit_bounds_check(ssaProcedure *proc, Token token, ssaValue *index, ssaValue *len) {
-	if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
-		return;
-	}
-
-	index = ssa_emit_conv(proc, index, t_int);
-	len = ssa_emit_conv(proc, len, t_int);
-
-	ssa_emit(proc, ssa_make_instr_bounds_check(proc, token.pos, index, len));
-
-	// gbAllocator a = proc->module->allocator;
-	// ssaValue **args = gb_alloc_array(a, ssaValue *, 5);
-	// args[0] = ssa_emit_global_string(proc, token.pos.file);
-	// args[1] = ssa_make_const_int(a, token.pos.line);
-	// args[2] = ssa_make_const_int(a, token.pos.column);
-	// args[3] = ssa_emit_conv(proc, index, t_int);
-	// args[4] = ssa_emit_conv(proc, len, t_int);
-
-	// ssa_emit_global_call(proc, "__bounds_check_error", args, 5);
-}
-
-void ssa_emit_slice_bounds_check(ssaProcedure *proc, Token token, ssaValue *low, ssaValue *high, ssaValue *max, bool is_substring) {
-	if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
-		return;
-	}
-
-
-	low  = ssa_emit_conv(proc, low,  t_int);
-	high = ssa_emit_conv(proc, high, t_int);
-	max  = ssa_emit_conv(proc, max,  t_int);
-
-	ssa_emit(proc, ssa_make_instr_slice_bounds_check(proc, token.pos, low, high, max, is_substring));
-
-	// gbAllocator a = proc->module->allocator;
-	// ssaValue **args = gb_alloc_array(a, ssaValue *, 6);
-	// args[0] = ssa_emit_global_string(proc, token.pos.file);
-	// args[1] = ssa_make_const_int(a, token.pos.line);
-	// args[2] = ssa_make_const_int(a, token.pos.column);
-	// args[3] = ssa_emit_conv(proc, low, t_int);
-	// args[4] = ssa_emit_conv(proc, high, t_int);
-	// args[5] = ssa_emit_conv(proc, max, t_int);
-
-	// if (!is_substring) {
-	// 	ssa_emit_global_call(proc, "__slice_expr_error", args, 6);
-	// } else {
-	// 	ssa_emit_global_call(proc, "__substring_expr_error", args, 5);
-	// }
-}
-
-
-////////////////////////////////////////////////////////////////
-//
-// @Build
-//
-////////////////////////////////////////////////////////////////
-
-
-void ssa_push_target_list(ssaProcedure *proc, ssaBlock *break_, ssaBlock *continue_, ssaBlock *fallthrough_) {
-	ssaTargetList *tl = gb_alloc_item(proc->module->allocator, ssaTargetList);
-	tl->prev          = proc->target_list;
-	tl->break_        = break_;
-	tl->continue_     = continue_;
-	tl->fallthrough_  = fallthrough_;
-	proc->target_list = tl;
-}
-
-void ssa_pop_target_list(ssaProcedure *proc) {
-	proc->target_list = proc->target_list->prev;
-}
-
-
-void ssa_mangle_sub_type_name(ssaModule *m, Entity *field, String parent) {
-	if (field->kind != Entity_TypeName) {
-		return;
-	}
-	String cn = field->token.string;
-
-	isize len = parent.len + 1 + cn.len;
-	String child = {NULL, len};
-	child.text = gb_alloc_array(m->allocator, u8, len);
-
-	isize i = 0;
-	gb_memmove(child.text+i, parent.text, parent.len);
-	i += parent.len;
-	child.text[i++] = '.';
-	gb_memmove(child.text+i, cn.text, cn.len);
-
-	map_string_set(&m->type_names, hash_pointer(field->type), child);
-	ssa_gen_global_type_name(m, field, child);
-}
-
-void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) {
-	ssaValue *t = ssa_make_value_type_name(m->allocator, name, e->type);
-	ssa_module_add_value(m, e, t);
-	map_ssa_value_set(&m->members, hash_string(name), t);
-
-	Type *bt = base_type(e->type);
-	if (bt->kind == Type_Record) {
-		TypeRecord *s = &bt->Record;
-		for (isize j = 0; j < s->other_field_count; j++) {
-			ssa_mangle_sub_type_name(m, s->other_fields[j], name);
-		}
-	}
-
-	if (is_type_union(bt)) {
-		TypeRecord *s = &bt->Record;
-		// NOTE(bill): Zeroth entry is null (for `match type` stmts)
-		for (isize j = 1; j < s->field_count; j++) {
-			ssa_mangle_sub_type_name(m, s->fields[j], name);
-		}
-	}
-}
-
-
-
-
-void ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d) {
-	ssaBlock *b = ssa_add_block(proc, NULL, "defer");
-	// NOTE(bill): The prev block may defer injection before it's terminator
-	ssaInstr *last_instr = ssa_get_last_instr(proc->curr_block);
-	if (last_instr == NULL || !ssa_is_instr_terminating(last_instr)) {
-		ssa_emit_jump(proc, b);
-	}
-	proc->curr_block = b;
-	ssa_emit_comment(proc, str_lit("defer"));
-	if (d.kind == ssaDefer_Node) {
-		ssa_build_stmt(proc, d.stmt);
-	} else if (d.kind == ssaDefer_Instr) {
-		// NOTE(bill): Need to make a new copy
-		ssaValue *instr = cast(ssaValue *)gb_alloc_copy(proc->module->allocator, d.instr, gb_size_of(ssaValue));
-		ssa_emit(proc, instr);
-	}
-}
-
-
-
-ssaValue *ssa_find_global_variable(ssaProcedure *proc, String name) {
-	ssaValue **value = map_ssa_value_get(&proc->module->members, hash_string(name));
-	GB_ASSERT_MSG(value != NULL, "Unable to find global variable `%.*s`", LIT(name));
-	return *value;
-}
-
-ssaValue *ssa_find_implicit_value_backing(ssaProcedure *proc, ImplicitValueId id) {
-	Entity *e = proc->module->info->implicit_values[id];
-	GB_ASSERT(e->kind == Entity_ImplicitValue);
-	Entity *backing = e->ImplicitValue.backing;
-	ssaValue **value = map_ssa_value_get(&proc->module->values, hash_pointer(backing));
-	GB_ASSERT_MSG(value != NULL, "Unable to find implicit value backing `%.*s`", LIT(backing->token.string));
-	return *value;
-}
-
-
-
-ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) {
-	expr = unparen_expr(expr);
-	switch (expr->kind) {
-	case_ast_node(bl, BasicLit, expr);
-		GB_PANIC("Non-constant basic literal");
-	case_end;
-
-	case_ast_node(i, Ident, expr);
-		Entity *e = *map_entity_get(&proc->module->info->uses, hash_pointer(expr));
-		if (e->kind == Entity_Builtin) {
-			Token token = ast_node_token(expr);
-			GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin `%.*s`\n"
-			         "\t at %.*s(%td:%td)", LIT(builtin_procs[e->Builtin.id].name),
-			         LIT(token.pos.file), token.pos.line, token.pos.column);
-			return NULL;
-		} else if (e->kind == Entity_Nil) {
-			return ssa_make_value_nil(proc->module->allocator, tv->type);
-		} else if (e->kind == Entity_ImplicitValue) {
-			return ssa_emit_load(proc, ssa_find_implicit_value_backing(proc, e->ImplicitValue.id));
-		}
-
-		ssaValue **found = map_ssa_value_get(&proc->module->values, hash_pointer(e));
-		if (found) {
-			ssaValue *v = *found;
-			if (v->kind == ssaValue_Proc) {
-				return v;
-			}
-			// if (e->kind == Entity_Variable && e->Variable.param) {
-				// return v;
-			// }
-			return ssa_emit_load(proc, v);
-		}
-		return NULL;
-	case_end;
-
-	case_ast_node(re, RunExpr, expr);
-		// TODO(bill): Run Expression
-		return ssa_build_single_expr(proc, re->expr, tv);
-	case_end;
-
-	case_ast_node(de, DerefExpr, expr);
-		return ssa_addr_load(proc, ssa_build_addr(proc, expr));
-	case_end;
-
-	case_ast_node(se, SelectorExpr, expr);
-		TypeAndValue *tav = map_tav_get(&proc->module->info->types, hash_pointer(expr));
-		GB_ASSERT(tav != NULL);
-		return ssa_addr_load(proc, ssa_build_addr(proc, expr));
-	case_end;
-
-	case_ast_node(ue, UnaryExpr, expr);
-		switch (ue->op.kind) {
-		case Token_Pointer:
-			return ssa_emit_ptr_offset(proc, ssa_build_addr(proc, ue->expr).addr, v_zero); // Make a copy of the pointer
-
-		case Token_Maybe:
-			return ssa_emit_conv(proc, ssa_build_expr(proc, ue->expr), type_of_expr(proc->module->info, expr));
-
-		case Token_Add:
-			return ssa_build_expr(proc, ue->expr);
-
-		case Token_Sub: // NOTE(bill): -`x` == 0 - `x`
-			return ssa_emit_arith(proc, ue->op.kind, v_zero, ssa_build_expr(proc, ue->expr), tv->type);
-
-		case Token_Not:   // Boolean not
-		case Token_Xor: { // Bitwise not
-			// NOTE(bill): "not" `x` == `x` "xor" `-1`
-			ssaValue *left = ssa_build_expr(proc, ue->expr);
-			ssaValue *right = ssa_add_module_constant(proc->module, tv->type, make_exact_value_integer(-1));
-			return ssa_emit_arith(proc, ue->op.kind,
-			                      left, right,
-			                      tv->type);
-		} break;
-		}
-	case_end;
-
-	case_ast_node(be, BinaryExpr, expr);
-		ssaValue *left = ssa_build_expr(proc, be->left);
-		Type *type = default_type(tv->type);
-
-		switch (be->op.kind) {
-		case Token_Add:
-		case Token_Sub:
-		case Token_Mul:
-		case Token_Quo:
-		case Token_Mod:
-		case Token_And:
-		case Token_Or:
-		case Token_Xor:
-		case Token_AndNot:
-		case Token_Shl:
-		case Token_Shr: {
-			ssaValue *right = ssa_build_expr(proc, be->right);
-			return ssa_emit_arith(proc, be->op.kind, left, right, type);
-		}
-
-
-		case Token_CmpEq:
-		case Token_NotEq:
-		case Token_Lt:
-		case Token_LtEq:
-		case Token_Gt:
-		case Token_GtEq: {
-			ssaValue *right = ssa_build_expr(proc, be->right);
-			ssaValue *cmp = ssa_emit_comp(proc, be->op.kind, left, right);
-			return ssa_emit_conv(proc, cmp, type);
-		} break;
-
-		case Token_CmpAnd:
-		case Token_CmpOr:
-			return ssa_emit_logical_binary_expr(proc, expr);
-
-		case Token_as:
-			ssa_emit_comment(proc, str_lit("cast - as"));
-			return ssa_emit_conv(proc, left, type);
-
-		case Token_transmute:
-			ssa_emit_comment(proc, str_lit("cast - transmute"));
-			return ssa_emit_transmute(proc, left, type);
-
-		case Token_down_cast:
-			ssa_emit_comment(proc, str_lit("cast - down_cast"));
-			return ssa_emit_down_cast(proc, left, type);
-
-		case Token_union_cast:
-			ssa_emit_comment(proc, str_lit("cast - union_cast"));
-			return ssa_emit_union_cast(proc, left, type);
-
-		default:
-			GB_PANIC("Invalid binary expression");
-			break;
-		}
-	case_end;
-
-	case_ast_node(pl, ProcLit, expr);
-		// NOTE(bill): Generate a new name
-		// parent$count
-		isize name_len = proc->name.len + 1 + 8 + 1;
-		u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
-		name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s$%d", LIT(proc->name), cast(i32)proc->children.count);
-		String name = make_string(name_text, name_len-1);
-
-		Type *type = type_of_expr(proc->module->info, expr);
-		ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
-		                                           proc->module, NULL, type, pl->type, pl->body, name);
-
-		value->Proc.tags = pl->tags;
-
-		array_add(&proc->children, &value->Proc);
-		ssa_build_proc(value, proc);
-
-		return value;
-	case_end;
-
-
-	case_ast_node(cl, CompoundLit, expr);
-		return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr);
-	case_end;
-
-
-	case_ast_node(ce, CallExpr, expr);
-		AstNode *p = unparen_expr(ce->proc);
-		if (p->kind == AstNode_Ident) {
-			Entity **found = map_entity_get(&proc->module->info->uses, hash_pointer(p));
-			if (found && (*found)->kind == Entity_Builtin) {
-				Entity *e = *found;
-				switch (e->Builtin.id) {
-				case BuiltinProc_type_info: {
-					Type *t = default_type(type_of_expr(proc->module->info, ce->args.e[0]));
-					return ssa_type_info(proc, t);
-				} break;
-				case BuiltinProc_type_info_of_val: {
-					Type *t = default_type(type_of_expr(proc->module->info, ce->args.e[0]));
-					return ssa_type_info(proc, t);
-				} break;
-
-				case BuiltinProc_new: {
-					ssa_emit_comment(proc, str_lit("new"));
-					// new :: proc(Type) -> ^Type
-					gbAllocator allocator = proc->module->allocator;
-
-					Type *type = type_of_expr(proc->module->info, ce->args.e[0]);
-					Type *ptr_type = make_type_pointer(allocator, type);
-
-					i64 s = type_size_of(proc->module->sizes, allocator, type);
-					i64 a = type_align_of(proc->module->sizes, allocator, type);
-
-					ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2);
-					args[0] = ssa_make_const_int(allocator, s);
-					args[1] = ssa_make_const_int(allocator, a);
-					ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2);
-					ssaValue *v = ssa_emit_conv(proc, call, ptr_type);
-					return v;
-				} break;
-
-				case BuiltinProc_new_slice: {
-					ssa_emit_comment(proc, str_lit("new_slice"));
-					// new_slice :: proc(Type, len: int[, cap: int]) -> ^Type
-					gbAllocator allocator = proc->module->allocator;
-
-					Type *type = type_of_expr(proc->module->info, ce->args.e[0]);
-					Type *ptr_type = make_type_pointer(allocator, type);
-					Type *slice_type = make_type_slice(allocator, type);
-
-					i64 s = type_size_of(proc->module->sizes, allocator, type);
-					i64 a = type_align_of(proc->module->sizes, allocator, type);
-
-					ssaValue *elem_size  = ssa_make_const_int(allocator, s);
-					ssaValue *elem_align = ssa_make_const_int(allocator, a);
-
-					ssaValue *len = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args.e[1]), t_int);
-					ssaValue *cap = len;
-					if (ce->args.count == 3) {
-						cap = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args.e[2]), t_int);
-					}
-
-					ssa_emit_slice_bounds_check(proc, ast_node_token(ce->args.e[1]), v_zero, len, cap, false);
-
-					ssaValue *slice_size = ssa_emit_arith(proc, Token_Mul, elem_size, cap, t_int);
-
-					ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2);
-					args[0] = slice_size;
-					args[1] = elem_align;
-					ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2);
-
-					ssaValue *ptr = ssa_emit_conv(proc, call, ptr_type);
-					ssaValue *slice = ssa_add_local_generated(proc, slice_type);
-
-					ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0);
-					ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1);
-					ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2);
-					ssa_emit_store(proc, gep0, ptr);
-					ssa_emit_store(proc, gep1, len);
-					ssa_emit_store(proc, gep2, cap);
-					return ssa_emit_load(proc, slice);
-				} break;
-
-				case BuiltinProc_assert: {
-					ssa_emit_comment(proc, str_lit("assert"));
-					ssaValue *cond = ssa_build_expr(proc, ce->args.e[0]);
-					GB_ASSERT(is_type_boolean(ssa_type(cond)));
-
-					cond = ssa_emit_comp(proc, Token_CmpEq, cond, v_false);
-					ssaBlock *err  = ssa_add_block(proc, NULL, "builtin.assert.err");
-					ssaBlock *done = ssa_add_block(proc, NULL, "builtin.assert.done");
-
-					ssa_emit_if(proc, cond, err, done);
-					proc->curr_block = err;
-
-					// TODO(bill): Cleanup allocations here
-					Token token = ast_node_token(ce->args.e[0]);
-					TokenPos pos = token.pos;
-					gbString expr = expr_to_string(ce->args.e[0]);
-					isize expr_len = gb_string_length(expr);
-					String expr_str = {0};
-					expr_str.text = cast(u8 *)gb_alloc_copy_align(proc->module->allocator, expr, expr_len, 1);
-					expr_str.len = expr_len;
-					gb_string_free(expr);
-
-
-					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4);
-					args[0] = ssa_make_const_string(proc->module->allocator, pos.file);
-					args[1] = ssa_make_const_int(proc->module->allocator, pos.line);
-					args[2] = ssa_make_const_int(proc->module->allocator, pos.column);
-					args[3] = ssa_make_const_string(proc->module->allocator, expr_str);
-					ssa_emit_global_call(proc, "__assert", args, 4);
-
-					ssa_emit_jump(proc, done);
-					proc->curr_block = done;
-
-					return NULL;
-				} break;
-
-				case BuiltinProc_panic: {
-					ssa_emit_comment(proc, str_lit("panic"));
-					ssaValue *msg = ssa_build_expr(proc, ce->args.e[0]);
-					GB_ASSERT(is_type_string(ssa_type(msg)));
-
-					Token token = ast_node_token(ce->args.e[0]);
-					TokenPos pos = token.pos;
-
-					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4);
-					args[0] = ssa_make_const_string(proc->module->allocator, pos.file);
-					args[1] = ssa_make_const_int(proc->module->allocator, pos.line);
-					args[2] = ssa_make_const_int(proc->module->allocator, pos.column);
-					args[3] = msg;
-					ssa_emit_global_call(proc, "__assert", args, 4);
-
-					return NULL;
-				} break;
-
-
-				case BuiltinProc_copy: {
-					ssa_emit_comment(proc, str_lit("copy"));
-					// copy :: proc(dst, src: []Type) -> int
-					AstNode *dst_node = ce->args.e[0];
-					AstNode *src_node = ce->args.e[1];
-					ssaValue *dst_slice = ssa_build_expr(proc, dst_node);
-					ssaValue *src_slice = ssa_build_expr(proc, src_node);
-					Type *slice_type = base_type(ssa_type(dst_slice));
-					GB_ASSERT(slice_type->kind == Type_Slice);
-					Type *elem_type = slice_type->Slice.elem;
-					i64 size_of_elem = type_size_of(proc->module->sizes, proc->module->allocator, elem_type);
-
-
-					ssaValue *dst = ssa_emit_conv(proc, ssa_slice_elem(proc, dst_slice), t_rawptr);
-					ssaValue *src = ssa_emit_conv(proc, ssa_slice_elem(proc, src_slice), t_rawptr);
-
-					ssaValue *len_dst = ssa_slice_len(proc, dst_slice);
-					ssaValue *len_src = ssa_slice_len(proc, src_slice);
-
-					ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len_dst, len_src);
-					ssaValue *len = ssa_emit_select(proc, cond, len_dst, len_src);
-
-					ssaValue *elem_size = ssa_make_const_int(proc->module->allocator, size_of_elem);
-					ssaValue *byte_count = ssa_emit_arith(proc, Token_Mul, len, elem_size, t_int);
-
-					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3);
-					args[0] = dst;
-					args[1] = src;
-					args[2] = byte_count;
-
-					ssa_emit_global_call(proc, "__mem_copy", args, 3);
-
-					return len;
-				} break;
-				case BuiltinProc_append: {
-					ssa_emit_comment(proc, str_lit("append"));
-					// append :: proc(s: ^[]Type, item: Type) -> bool
-					AstNode *sptr_node = ce->args.e[0];
-					AstNode *item_node = ce->args.e[1];
-					ssaValue *slice_ptr = ssa_build_expr(proc, sptr_node);
-					ssaValue *slice = ssa_emit_load(proc, slice_ptr);
-
-					ssaValue *elem = ssa_slice_elem(proc, slice);
-					ssaValue *len  = ssa_slice_len(proc,  slice);
-					ssaValue *cap  = ssa_slice_cap(proc,  slice);
-
-					Type *elem_type = type_deref(ssa_type(elem));
-
-					ssaValue *item_value = ssa_build_expr(proc, item_node);
-					item_value = ssa_emit_conv(proc, item_value, elem_type);
-
-					ssaValue *item = ssa_add_local_generated(proc, elem_type);
-					ssa_emit_store(proc, item, item_value);
-
-
-					// NOTE(bill): Check if can append is possible
-					ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len, cap);
-					ssaBlock *able = ssa_add_block(proc, NULL, "builtin.append.able");
-					ssaBlock *done = ssa_add_block(proc, NULL, "builtin.append.done");
-
-					ssa_emit_if(proc, cond, able, done);
-					proc->curr_block = able;
-
-					// Add new slice item
-					i64 item_size = type_size_of(proc->module->sizes, proc->module->allocator, elem_type);
-					ssaValue *byte_count = ssa_make_const_int(proc->module->allocator, item_size);
-
-					ssaValue *offset = ssa_emit_ptr_offset(proc, elem, len);
-					offset = ssa_emit_conv(proc, offset, t_rawptr);
-
-					item = ssa_emit_ptr_offset(proc, item, v_zero);
-					item = ssa_emit_conv(proc, item, t_rawptr);
-
-					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3);
-					args[0] = offset;
-					args[1] = item;
-					args[2] = byte_count;
-
-					ssa_emit_global_call(proc, "__mem_copy", args, 3);
-
-					// Increment slice length
-					ssaValue *new_len = ssa_emit_arith(proc, Token_Add, len, v_one, t_int);
-					ssaValue *gep = ssa_emit_struct_ep(proc, slice_ptr, 1);
-					ssa_emit_store(proc, gep, new_len);
-
-					ssa_emit_jump(proc, done);
-					proc->curr_block = done;
-
-					return ssa_emit_conv(proc, cond, t_bool);
-				} break;
-
-				case BuiltinProc_swizzle: {
-					ssa_emit_comment(proc, str_lit("swizzle"));
-					ssaValue *vector = ssa_build_expr(proc, ce->args.e[0]);
-					isize index_count = ce->args.count-1;
-					if (index_count == 0) {
-						return vector;
-					}
-
-					i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count);
-					isize index = 0;
-					for_array(i, ce->args) {
-						if (i == 0) continue;
-						TypeAndValue *tv = type_and_value_of_expression(proc->module->info, ce->args.e[i]);
-						GB_ASSERT(is_type_integer(tv->type));
-						GB_ASSERT(tv->value.kind == ExactValue_Integer);
-						indices[index++] = cast(i32)tv->value.value_integer;
-					}
-
-					return ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, vector, indices, index_count));
-
-				} break;
-
-#if 0
-				case BuiltinProc_ptr_offset: {
-					ssa_emit_comment(proc, str_lit("ptr_offset"));
-					ssaValue *ptr = ssa_build_expr(proc, ce->args.e[0]);
-					ssaValue *offset = ssa_build_expr(proc, ce->args.e[1]);
-					return ssa_emit_ptr_offset(proc, ptr, offset);
-				} break;
-
-				case BuiltinProc_ptr_sub: {
-					ssa_emit_comment(proc, str_lit("ptr_sub"));
-					ssaValue *ptr_a = ssa_build_expr(proc, ce->args.e[0]);
-					ssaValue *ptr_b = ssa_build_expr(proc, ce->args.e[1]);
-					Type *ptr_type = base_type(ssa_type(ptr_a));
-					GB_ASSERT(ptr_type->kind == Type_Pointer);
-					isize elem_size = type_size_of(proc->module->sizes, proc->module->allocator, ptr_type->Pointer.elem);
-
-					ssaValue *v = ssa_emit_arith(proc, Token_Sub, ptr_a, ptr_b, t_int);
-					if (elem_size > 1) {
-						ssaValue *ez = ssa_make_const_int(proc->module->allocator, elem_size);
-						v = ssa_emit_arith(proc, Token_Quo, v, ez, t_int);
-					}
-
-					return v;
-				} break;
-#endif
-
-				case BuiltinProc_slice_ptr: {
-					ssa_emit_comment(proc, str_lit("slice_ptr"));
-					ssaValue *ptr = ssa_build_expr(proc, ce->args.e[0]);
-					ssaValue *len = ssa_build_expr(proc, ce->args.e[1]);
-					ssaValue *cap = len;
-
-					len = ssa_emit_conv(proc, len, t_int);
-
-					if (ce->args.count == 3) {
-						cap = ssa_build_expr(proc, ce->args.e[2]);
-						cap = ssa_emit_conv(proc, cap, t_int);
-					}
-
-
-					Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ssa_type(ptr)));
-					ssaValue *slice = ssa_add_local_generated(proc, slice_type);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 0), ptr);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), cap);
-					return ssa_emit_load(proc, slice);
-				} break;
-
-				case BuiltinProc_min: {
-					ssa_emit_comment(proc, str_lit("min"));
-					ssaValue *x = ssa_build_expr(proc, ce->args.e[0]);
-					ssaValue *y = ssa_build_expr(proc, ce->args.e[1]);
-					Type *t = base_type(ssa_type(x));
-					ssaValue *cond = ssa_emit_comp(proc, Token_Lt, x, y);
-					return ssa_emit_select(proc, cond, x, y);
-				} break;
-
-				case BuiltinProc_max: {
-					ssa_emit_comment(proc, str_lit("max"));
-					ssaValue *x = ssa_build_expr(proc, ce->args.e[0]);
-					ssaValue *y = ssa_build_expr(proc, ce->args.e[1]);
-					Type *t = base_type(ssa_type(x));
-					ssaValue *cond = ssa_emit_comp(proc, Token_Gt, x, y);
-					return ssa_emit_select(proc, cond, x, y);
-				} break;
-
-				case BuiltinProc_abs: {
-					ssa_emit_comment(proc, str_lit("abs"));
-					gbAllocator a = proc->module->allocator;
-
-					ssaValue *x = ssa_build_expr(proc, ce->args.e[0]);
-					Type *original_type = ssa_type(x);
-					Type *t = original_type;
-					i64 sz = type_size_of(proc->module->sizes, a, t);
-					GB_ASSERT(is_type_integer(t) || is_type_float(t));
-					if (is_type_float(t)) {
-						if (sz == 4) {
-							t = t_i32;
-						} else if (sz == 8) {
-							t = t_i64;
-						} else {
-							GB_PANIC("unknown float type for `abs`");
-						}
-
-						x = ssa_emit_bitcast(proc, x, t);
-					}
-
-					/*
-						NOTE(bill): See Hacker's Delight, section 2-4.
-						m := x >> (int_size-1)
-						b := x ^ m
-						return b - m
-					*/
-
-					ssaValue *m = ssa_emit_arith(proc, Token_Shr,
-					                             x,
-					                             ssa_make_value_constant(a, t, make_exact_value_integer(sz-1)),
-					                             t);
-					ssaValue *b = ssa_emit_arith(proc, Token_Xor, x, m, t);
-					ssaValue *v = ssa_emit_arith(proc, Token_Sub, b, m, t);
-
-					if (is_type_float(t)) {
-						v = ssa_emit_bitcast(proc, v, original_type);
-					}
-					return v;
-				} break;
-
-				case BuiltinProc_enum_to_string: {
-					ssa_emit_comment(proc, str_lit("enum_to_string"));
-					ssaValue *x = ssa_build_expr(proc, ce->args.e[0]);
-					Type *t = ssa_type(x);
-					ssaValue *ti = ssa_type_info(proc, t);
-
-
-					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 2);
-					args[0] = ti;
-					args[1] = ssa_emit_conv(proc, x, t_i64);
-					return ssa_emit_global_call(proc, "__enum_to_string", args, 2);
-				} break;
-				}
-			}
-		}
-
-
-		// NOTE(bill): Regular call
-		ssaValue *value = ssa_build_expr(proc, ce->proc);
-		Type *proc_type_ = base_type(ssa_type(value));
-		GB_ASSERT(proc_type_->kind == Type_Proc);
-		TypeProc *type = &proc_type_->Proc;
-
-		isize arg_index = 0;
-
-		isize arg_count = 0;
-		for_array(i, ce->args) {
-			AstNode *a = ce->args.e[i];
-			Type *at = base_type(type_of_expr(proc->module->info, a));
-			if (at->kind == Type_Tuple) {
-				arg_count += at->Tuple.variable_count;
-			} else {
-				arg_count++;
-			}
-		}
-		ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, arg_count);
-		bool variadic = proc_type_->Proc.variadic;
-		bool vari_expand = ce->ellipsis.pos.line != 0;
-
-		for_array(i, ce->args) {
-			ssaValue *a = ssa_build_expr(proc, ce->args.e[i]);
-			Type *at = ssa_type(a);
-			if (at->kind == Type_Tuple) {
-				for (isize i = 0; i < at->Tuple.variable_count; i++) {
-					Entity *e = at->Tuple.variables[i];
-					ssaValue *v = ssa_emit_struct_ev(proc, a, i);
-					args[arg_index++] = v;
-				}
-			} else {
-				args[arg_index++] = a;
-			}
-		}
-
-		TypeTuple *pt = &type->params->Tuple;
-
-		if (variadic) {
-			isize i = 0;
-			for (; i < type->param_count-1; i++) {
-				args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type);
-			}
-			if (!vari_expand) {
-				Type *variadic_type = pt->variables[i]->type;
-				GB_ASSERT(is_type_slice(variadic_type));
-				variadic_type = base_type(variadic_type)->Slice.elem;
-				for (; i < arg_count; i++) {
-					args[i] = ssa_emit_conv(proc, args[i], variadic_type);
-				}
-			}
-		} else {
-			for (isize i = 0; i < arg_count; i++) {
-				args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type);
-			}
-		}
-
-		if (variadic && !vari_expand) {
-			ssa_emit_comment(proc, str_lit("variadic call argument generation"));
-			gbAllocator allocator = proc->module->allocator;
-			Type *slice_type = pt->variables[type->param_count-1]->type;
-			Type *elem_type  = base_type(slice_type)->Slice.elem;
-			ssaValue *slice = ssa_add_local_generated(proc, slice_type);
-			isize slice_len = arg_count+1 - type->param_count;
-
-			if (slice_len > 0) {
-				ssaValue *base_array = ssa_add_local_generated(proc, make_type_array(allocator, elem_type, slice_len));
-
-				for (isize i = type->param_count-1, j = 0; i < arg_count; i++, j++) {
-					ssaValue *addr = ssa_emit_array_epi(proc, base_array, j);
-					ssa_emit_store(proc, addr, args[i]);
-				}
-
-				ssaValue *base_elem  = ssa_emit_array_epi(proc, base_array, 0);
-				ssaValue *slice_elem = ssa_emit_struct_ep(proc, slice,      0);
-				ssa_emit_store(proc, slice_elem, base_elem);
-				ssaValue *len = ssa_make_const_int(allocator, slice_len);
-				ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len);
-				ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), len);
-			}
-
-			if (args[0]->kind == ssaValue_Constant) {
-				ssaValueConstant *c = &args[0]->Constant;
-				gb_printf_err("%s %d\n", type_to_string(c->type), c->value.kind);
-			}
-
-			arg_count = type->param_count;
-			args[arg_count-1] = ssa_emit_load(proc, slice);
-		}
-
-		return ssa_emit_call(proc, value, args, arg_count);
-	case_end;
-
-	case_ast_node(de, DemaybeExpr, expr);
-		return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr);
-	case_end;
-
-	case_ast_node(se, SliceExpr, expr);
-		return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr);
-	case_end;
-
-	case_ast_node(ie, IndexExpr, expr);
-		return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr);
-	case_end;
-	}
-
-	GB_PANIC("Unexpected expression: %.*s", LIT(ast_node_strings[expr->kind]));
-	return NULL;
-}
-
-
-ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) {
-	expr = unparen_expr(expr);
-
-	TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(expr));
-	GB_ASSERT_NOT_NULL(tv);
-
-	if (tv->value.kind != ExactValue_Invalid) {
-		return ssa_add_module_constant(proc->module, tv->type, tv->value);
-	}
-
-	ssaValue *value = NULL;
-	if (tv->mode == Addressing_Variable) {
-		value = ssa_addr_load(proc, ssa_build_addr(proc, expr));
-	} else {
-		value = ssa_build_single_expr(proc, expr, tv);
-	}
-
-	return value;
-}
-
-ssaValue *ssa_add_using_variable(ssaProcedure *proc, Entity *e) {
-	GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous);
-	String name = e->token.string;
-	Entity *parent = e->using_parent;
-	Selection sel = lookup_field(proc->module->allocator, parent->type, name, false);
-	GB_ASSERT(sel.entity != NULL);
-	ssaValue **pv = map_ssa_value_get(&proc->module->values, hash_pointer(parent));
-	ssaValue *v = NULL;
-	if (pv != NULL) {
-		v = *pv;
-	} else {
-		v = ssa_build_addr(proc, e->using_expr).addr;
-	}
-	GB_ASSERT(v != NULL);
-	ssaValue *var = ssa_emit_deep_field_gep(proc, parent->type, v, sel);
-	map_ssa_value_set(&proc->module->values, hash_pointer(e), var);
-	return var;
-}
-
-bool ssa_is_elem_const(ssaModule *m, AstNode *elem, Type *elem_type) {
-	if (base_type(elem_type) == t_any) {
-		return false;
-	}
-	if (elem->kind == AstNode_FieldValue) {
-		elem = elem->FieldValue.value;
-	}
-	TypeAndValue *tav = type_and_value_of_expression(m->info, elem);
-	GB_ASSERT(tav != NULL);
-	return tav->value.kind != ExactValue_Invalid;
-}
-
-ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
-	switch (expr->kind) {
-	case_ast_node(i, Ident, expr);
-		if (ssa_is_blank_ident(expr)) {
-			ssaAddr val = {0};
-			return val;
-		}
-
-		Entity *e = entity_of_ident(proc->module->info, expr);
-		TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(expr));
-
-		GB_ASSERT(e->kind != Entity_Constant);
-
-		ssaValue *v = NULL;
-		ssaValue **found = map_ssa_value_get(&proc->module->values, hash_pointer(e));
-		if (found) {
-			v = *found;
-		} else if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) {
-			v = ssa_add_using_variable(proc, e);
-		} else if (e->kind == Entity_ImplicitValue) {
-			// TODO(bill): Should a copy be made?
-			v = ssa_find_implicit_value_backing(proc, e->ImplicitValue.id);
-		}
-
-		if (v == NULL) {
-			GB_PANIC("Unknown value: %s, entity: %p %.*s\n", expr_to_string(expr), e, LIT(entity_strings[e->kind]));
-		}
-
-		return ssa_make_addr(v, expr);
-	case_end;
-
-	case_ast_node(pe, ParenExpr, expr);
-		return ssa_build_addr(proc, unparen_expr(expr));
-	case_end;
-
-	case_ast_node(se, SelectorExpr, expr);
-		ssa_emit_comment(proc, str_lit("SelectorExpr"));
-		String selector = unparen_expr(se->selector)->Ident.string;
-		Type *type = base_type(type_of_expr(proc->module->info, se->expr));
-
-		if (type == t_invalid) {
-			// NOTE(bill): Imports
-			Entity *imp = entity_of_ident(proc->module->info, se->expr);
-			if (imp != NULL) {
-				GB_ASSERT(imp->kind == Entity_ImportName);
-			}
-			return ssa_build_addr(proc, unparen_expr(se->selector));
-		} else {
-			Selection sel = lookup_field(proc->module->allocator, type, selector, false);
-			GB_ASSERT(sel.entity != NULL);
-
-			ssaValue *a = ssa_build_addr(proc, se->expr).addr;
-			a = ssa_emit_deep_field_gep(proc, type, a, sel);
-			return ssa_make_addr(a, expr);
-		}
-	case_end;
-
-	case_ast_node(ue, UnaryExpr, expr);
-		switch (ue->op.kind) {
-		case Token_Pointer: {
-			return ssa_build_addr(proc, ue->expr);
-		}
-		default:
-			GB_PANIC("Invalid unary expression for ssa_build_addr");
-		}
-	case_end;
-
-	case_ast_node(be, BinaryExpr, expr);
-		switch (be->op.kind) {
-		case Token_as: {
-			ssa_emit_comment(proc, str_lit("Cast - as"));
-			// NOTE(bill): Needed for dereference of pointer conversion
-			Type *type = type_of_expr(proc->module->info, expr);
-			ssaValue *v = ssa_add_local_generated(proc, type);
-			ssa_emit_store(proc, v, ssa_emit_conv(proc, ssa_build_expr(proc, be->left), type));
-			return ssa_make_addr(v, expr);
-		}
-		case Token_transmute: {
-			ssa_emit_comment(proc, str_lit("Cast - transmute"));
-			// NOTE(bill): Needed for dereference of pointer conversion
-			Type *type = type_of_expr(proc->module->info, expr);
-			ssaValue *v = ssa_add_local_generated(proc, type);
-			ssa_emit_store(proc, v, ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), type));
-			return ssa_make_addr(v, expr);
-		}
-		default:
-			GB_PANIC("Invalid binary expression for ssa_build_addr: %.*s\n", LIT(be->op.string));
-			break;
-		}
-	case_end;
-
-	case_ast_node(ie, IndexExpr, expr);
-		ssa_emit_comment(proc, str_lit("IndexExpr"));
-		Type *t = base_type(type_of_expr(proc->module->info, ie->expr));
-		gbAllocator a = proc->module->allocator;
-
-
-		bool deref = is_type_pointer(t);
-		t = type_deref(t);
-
-		ssaValue *using_addr = NULL;
-		if (!is_type_indexable(t)) {
-			// Using index expression
-			Entity *using_field = find_using_index_expr(t);
-			if (using_field != NULL) {
-				Selection sel = lookup_field(a, t, using_field->token.string, false);
-				ssaValue *e = ssa_build_addr(proc, ie->expr).addr;
-				using_addr = ssa_emit_deep_field_gep(proc, t, e, sel);
-
-				t = using_field->type;
-			}
-		}
-
-
-		switch (t->kind) {
-		case Type_Vector: {
-			ssaValue *vector = NULL;
-			if (using_addr != NULL) {
-				vector = using_addr;
-			} else {
-				vector = ssa_build_addr(proc, ie->expr).addr;
-				if (deref) {
-					vector = ssa_emit_load(proc, vector);
-				}
-			}
-			ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
-			ssaValue *len = ssa_make_const_int(a, t->Vector.count);
-			ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
-			return ssa_make_addr_vector(vector, index, expr);
-		} break;
-
-		case Type_Array: {
-			ssaValue *array = NULL;
-			if (using_addr != NULL) {
-				array = using_addr;
-			} else {
-				array = ssa_build_addr(proc, ie->expr).addr;
-				if (deref) {
-					array = ssa_emit_load(proc, array);
-				}
-			}
-			ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
-			ssaValue *elem = ssa_emit_array_ep(proc, array, index);
-			ssaValue *len = ssa_make_const_int(a, t->Vector.count);
-			ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
-			return ssa_make_addr(elem, expr);
-		} break;
-
-		case Type_Slice: {
-			ssaValue *slice = NULL;
-			if (using_addr != NULL) {
-				slice = ssa_emit_load(proc, using_addr);
-			} else {
-				slice = ssa_build_expr(proc, ie->expr);
-				if (deref) {
-					slice = ssa_emit_load(proc, slice);
-				}
-			}
-			ssaValue *elem = ssa_slice_elem(proc, slice);
-			ssaValue *len = ssa_slice_len(proc, slice);
-			ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
-			ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
-			ssaValue *v = ssa_emit_ptr_offset(proc, elem, index);
-			return ssa_make_addr(v, expr);
-
-		} break;
-
-		case Type_Basic: { // Basic_string
-			TypeAndValue *tv = map_tav_get(&proc->module->info->types, hash_pointer(ie->expr));
-			ssaValue *str;
-			ssaValue *elem;
-			ssaValue *len;
-			ssaValue *index;
-
-			if (using_addr != NULL) {
-				str = ssa_emit_load(proc, using_addr);
-			} else {
-				str = ssa_build_expr(proc, ie->expr);
-				if (deref) {
-					str = ssa_emit_load(proc, str);
-				}
-			}
-			elem = ssa_string_elem(proc, str);
-			len = ssa_string_len(proc, str);
-
-			index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
-			ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
-
-			return ssa_make_addr(ssa_emit_ptr_offset(proc, elem, index), expr);
-		} break;
-		}
-	case_end;
-
-	case_ast_node(se, SliceExpr, expr);
-		ssa_emit_comment(proc, str_lit("SliceExpr"));
-		gbAllocator a = proc->module->allocator;
-		ssaValue *low  = v_zero;
-		ssaValue *high = NULL;
-		ssaValue *max  = NULL;
-
-		if (se->low  != NULL)    low  = ssa_build_expr(proc, se->low);
-		if (se->high != NULL)    high = ssa_build_expr(proc, se->high);
-		if (se->triple_indexed)  max  = ssa_build_expr(proc, se->max);
-		ssaValue *addr = ssa_build_addr(proc, se->expr).addr;
-		ssaValue *base = ssa_emit_load(proc, addr);
-		Type *type = base_type(ssa_type(base));
-
-		if (is_type_pointer(type)) {
-			type = type_deref(type);
-			addr = base;
-			base = ssa_emit_load(proc, base);
-		}
-
-		// TODO(bill): Cleanup like mad!
-
-		switch (type->kind) {
-		case Type_Slice: {
-			Type *slice_type = type;
-
-			if (high == NULL) high = ssa_slice_len(proc, base);
-			if (max == NULL)  max  = ssa_slice_cap(proc, base);
-			GB_ASSERT(max != NULL);
-
-			ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false);
-
-			ssaValue *elem = ssa_slice_elem(proc, base);
-			ssaValue *len  = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
-			ssaValue *cap  = ssa_emit_arith(proc, Token_Sub, max,  low, t_int);
-			ssaValue *slice = ssa_add_local_generated(proc, slice_type);
-
-			ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0);
-			ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1);
-			ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2);
-			ssa_emit_store(proc, gep0, elem);
-			ssa_emit_store(proc, gep1, len);
-			ssa_emit_store(proc, gep2, cap);
-
-			return ssa_make_addr(slice, expr);
-		}
-
-		case Type_Array: {
-			Type *slice_type = make_type_slice(a, type->Array.elem);
-
-			if (high == NULL) high = ssa_array_len(proc, base);
-			if (max == NULL)  max  = ssa_array_cap(proc, base);
-			GB_ASSERT(max != NULL);
-
-			ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false);
-
-			ssaValue *elem = ssa_array_elem(proc, addr);
-			ssaValue *len  = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
-			ssaValue *cap  = ssa_emit_arith(proc, Token_Sub, max,  low, t_int);
-			ssaValue *slice = ssa_add_local_generated(proc, slice_type);
-
-			ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0);
-			ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1);
-			ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2);
-			ssa_emit_store(proc, gep0, elem);
-			ssa_emit_store(proc, gep1, len);
-			ssa_emit_store(proc, gep2, cap);
-
-			return ssa_make_addr(slice, expr);
-		}
-
-		case Type_Basic: {
-			GB_ASSERT(type == t_string);
-			if (high == NULL) {
-				high = ssa_string_len(proc, base);
-			}
-
-			ssa_emit_slice_bounds_check(proc, se->open, low, high, high, true);
-
-			ssaValue *elem, *len;
-			len = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
-
-			elem = ssa_string_elem(proc, base);
-			elem = ssa_emit_ptr_offset(proc, elem, low);
-
-			ssaValue *str = ssa_add_local_generated(proc, t_string);
-			ssaValue *gep0 = ssa_emit_struct_ep(proc, str, 0);
-			ssaValue *gep1 = ssa_emit_struct_ep(proc, str, 1);
-			ssa_emit_store(proc, gep0, elem);
-			ssa_emit_store(proc, gep1, len);
-
-			return ssa_make_addr(str, expr);
-		} break;
-		}
-
-		GB_PANIC("Unknown slicable type");
-	case_end;
-
-	case_ast_node(de, DerefExpr, expr);
-		// TODO(bill): Is a ptr copy needed?
-		ssaValue *addr = ssa_build_expr(proc, de->expr);
-		addr = ssa_emit_ptr_offset(proc, addr, v_zero);
-		return ssa_make_addr(addr, expr);
-	case_end;
-
-	case_ast_node(de, DemaybeExpr, expr);
-		ssa_emit_comment(proc, str_lit("DemaybeExpr"));
-		ssaValue *maybe = ssa_build_expr(proc, de->expr);
-		Type *t = default_type(type_of_expr(proc->module->info, expr));
-		GB_ASSERT(is_type_tuple(t));
-
-		ssaValue *result = ssa_add_local_generated(proc, t);
-		ssa_emit_store(proc, result, maybe);
-
-		return ssa_make_addr(result, expr);
-	case_end;
-
-	case_ast_node(ce, CallExpr, expr);
-		ssaValue *e = ssa_build_expr(proc, expr);
-		ssaValue *v = ssa_add_local_generated(proc, ssa_type(e));
-		ssa_emit_store(proc, v, e);
-		return ssa_make_addr(v, expr);
-	case_end;
-
-
-	case_ast_node(cl, CompoundLit, expr);
-		ssa_emit_comment(proc, str_lit("CompoundLit"));
-		Type *type = type_of_expr(proc->module->info, expr);
-		Type *bt = base_type(type);
-		ssaValue *v = ssa_add_local_generated(proc, type);
-
-		Type *et = NULL;
-		switch (bt->kind) {
-		case Type_Vector: et = bt->Vector.elem; break;
-		case Type_Array:  et = bt->Array.elem;  break;
-		case Type_Slice:  et = bt->Slice.elem;  break;
-		}
-
-		switch (bt->kind) {
-		default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
-
-		case Type_Vector: {
-			ssaValue *result = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr));
-			for_array(index, cl->elems) {
-				AstNode *elem = cl->elems.e[index];
-				if (ssa_is_elem_const(proc->module, elem, et)) {
-					continue;
-				}
-				ssaValue *field_elem = ssa_build_expr(proc, elem);
-				Type *t = ssa_type(field_elem);
-				GB_ASSERT(t->kind != Type_Tuple);
-				ssaValue *ev = ssa_emit_conv(proc, field_elem, et);
-				ssaValue *i = ssa_make_const_int(proc->module->allocator, index);
-				result = ssa_emit(proc, ssa_make_instr_insert_element(proc, result, ev, i));
-			}
-
-			if (cl->elems.count == 1 && bt->Vector.count > 1) {
-				isize index_count = bt->Vector.count;
-				i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count);
-				for (isize i = 0; i < index_count; i++) {
-					indices[i] = 0;
-				}
-				ssaValue *sv = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, result, indices, index_count));
-				ssa_emit_store(proc, v, sv);
-				return ssa_make_addr(v, expr);
-			}
-			ssa_emit_store(proc, v, result);
-		} break;
-
-		case Type_Record: {
-			GB_ASSERT(is_type_struct(bt));
-			TypeRecord *st = &bt->Record;
-			if (cl->elems.count > 0) {
-				ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)));
-				for_array(field_index, cl->elems) {
-					AstNode *elem = cl->elems.e[field_index];
-
-					ssaValue *field_expr = NULL;
-					Entity *field = NULL;
-					isize index = field_index;
-
-					if (elem->kind == AstNode_FieldValue) {
-						ast_node(fv, FieldValue, elem);
-						Selection sel = lookup_field(proc->module->allocator, bt, fv->field->Ident.string, false);
-						index = sel.index.e[0];
-						elem = fv->value;
-					} else {
-						TypeAndValue *tav = type_and_value_of_expression(proc->module->info, elem);
-						Selection sel = lookup_field(proc->module->allocator, bt, st->fields_in_src_order[field_index]->token.string, false);
-						index = sel.index.e[0];
-					}
-
-					field = st->fields[index];
-					if (ssa_is_elem_const(proc->module, elem, field->type)) {
-						continue;
-					}
-
-					field_expr = ssa_build_expr(proc, elem);
-
-					GB_ASSERT(ssa_type(field_expr)->kind != Type_Tuple);
-
-					Type *ft = field->type;
-					ssaValue *fv = ssa_emit_conv(proc, field_expr, ft);
-					ssaValue *gep = ssa_emit_struct_ep(proc, v, index);
-					ssa_emit_store(proc, gep, fv);
-				}
-			}
-		} break;
-		case Type_Array: {
-			if (cl->elems.count > 0) {
-				ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)));
-				for_array(i, cl->elems) {
-					AstNode *elem = cl->elems.e[i];
-					if (ssa_is_elem_const(proc->module, elem, et)) {
-						continue;
-					}
-					ssaValue *field_expr = ssa_build_expr(proc, elem);
-					Type *t = ssa_type(field_expr);
-					GB_ASSERT(t->kind != Type_Tuple);
-					ssaValue *ev = ssa_emit_conv(proc, field_expr, et);
-					ssaValue *gep = ssa_emit_array_epi(proc, v, i);
-					ssa_emit_store(proc, gep, ev);
-				}
-			}
-		} break;
-		case Type_Slice: {
-			if (cl->elems.count > 0) {
-				Type *elem_type = bt->Slice.elem;
-				Type *elem_ptr_type = make_type_pointer(proc->module->allocator, elem_type);
-				Type *elem_ptr_ptr_type = make_type_pointer(proc->module->allocator, elem_ptr_type);
-				ssaValue *slice = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr));
-				GB_ASSERT(slice->kind == ssaValue_ConstantSlice);
-
-				ssaValue *data = ssa_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32);
-
-				for_array(i, cl->elems) {
-					AstNode *elem = cl->elems.e[i];
-					if (ssa_is_elem_const(proc->module, elem, et)) {
-						continue;
-					}
-
-					ssaValue *field_expr = ssa_build_expr(proc, elem);
-					Type *t = ssa_type(field_expr);
-					GB_ASSERT(t->kind != Type_Tuple);
-					ssaValue *ev = ssa_emit_conv(proc, field_expr, elem_type);
-					ssaValue *offset = ssa_emit_ptr_offset(proc, data, ssa_make_const_int(proc->module->allocator, i));
-					ssa_emit_store(proc, offset, ev);
-				}
-
-				ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0);
-				ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1);
-				ssaValue *gep2 = ssa_emit_struct_ep(proc, v, 1);
-
-				ssa_emit_store(proc, gep0, data);
-				ssa_emit_store(proc, gep1, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count));
-				ssa_emit_store(proc, gep2, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count));
-			}
-		} break;
-		}
-
-		return ssa_make_addr(v, expr);
-	case_end;
-
-
-	}
-
-	TokenPos token_pos = ast_node_token(expr).pos;
-	GB_PANIC("Unexpected address expression\n"
-	         "\tAstNode: %.*s @ "
-	         "%.*s(%td:%td)\n",
-	         LIT(ast_node_strings[expr->kind]),
-	         LIT(token_pos.file), token_pos.line, token_pos.column);
-
-
-	return ssa_make_addr(NULL, NULL);
-}
-
-void ssa_build_assign_op(ssaProcedure *proc, ssaAddr lhs, ssaValue *value, TokenKind op) {
-	ssaValue *old_value = ssa_addr_load(proc, lhs);
-	Type *type = ssa_type(old_value);
-
-	ssaValue *change = value;
-	if (is_type_pointer(type) && is_type_integer(ssa_type(value))) {
-		change = ssa_emit_conv(proc, value, default_type(ssa_type(value)));
-	} else {
-		change = ssa_emit_conv(proc, value, type);
-	}
-	ssaValue *new_value = ssa_emit_arith(proc, op, old_value, change, type);
-	ssa_addr_store(proc, lhs, new_value);
-}
-
-void ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block) {
-	switch (cond->kind) {
-	case_ast_node(pe, ParenExpr, cond);
-		ssa_build_cond(proc, pe->expr, true_block, false_block);
-		return;
-	case_end;
-
-	case_ast_node(ue, UnaryExpr, cond);
-		if (ue->op.kind == Token_Not) {
-			ssa_build_cond(proc, ue->expr, false_block, true_block);
-			return;
-		}
-	case_end;
-
-	case_ast_node(be, BinaryExpr, cond);
-		if (be->op.kind == Token_CmpAnd) {
-			ssaBlock *block = ssa_add_block(proc, NULL, "cmp.and");
-			ssa_build_cond(proc, be->left, block, false_block);
-			proc->curr_block = block;
-			ssa_build_cond(proc, be->right, true_block, false_block);
-			return;
-		} else if (be->op.kind == Token_CmpOr) {
-			ssaBlock *block = ssa_add_block(proc, NULL, "cmp.or");
-			ssa_build_cond(proc, be->left, true_block, block);
-			proc->curr_block = block;
-			ssa_build_cond(proc, be->right, true_block, false_block);
-			return;
-		}
-	case_end;
-	}
-
-	ssaValue *expr = ssa_build_expr(proc, cond);
-	expr = ssa_emit_conv(proc, expr, t_bool);
-	ssa_emit_if(proc, expr, true_block, false_block);
-}
-
-
-
-
-void ssa_build_stmt_list(ssaProcedure *proc, AstNodeArray stmts) {
-	for_array(i, stmts) {
-		ssa_build_stmt(proc, stmts.e[i]);
-	}
-}
-
-void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node);
-void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
-	u32 prev_stmt_state_flags = proc->module->stmt_state_flags;
-
-	if (node->stmt_state_flags != 0) {
-		u32 in = node->stmt_state_flags;
-		u32 out = proc->module->stmt_state_flags;
-
-		if (in & StmtStateFlag_bounds_check) {
-			out |= StmtStateFlag_bounds_check;
-			out &= ~StmtStateFlag_no_bounds_check;
-		} else if (in & StmtStateFlag_no_bounds_check) {
-			out |= StmtStateFlag_no_bounds_check;
-			out &= ~StmtStateFlag_bounds_check;
-		}
-
-		proc->module->stmt_state_flags = out;
-	}
-
-	ssa_build_stmt_internal(proc, node);
-
-	proc->module->stmt_state_flags = prev_stmt_state_flags;
-}
-
-void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
-	switch (node->kind) {
-	case_ast_node(bs, EmptyStmt, node);
-	case_end;
-
-	case_ast_node(us, UsingStmt, node);
-		AstNode *decl = unparen_expr(us->node);
-		if (decl->kind == AstNode_VarDecl) {
-			ssa_build_stmt(proc, decl);
-		}
-	case_end;
-
-	case_ast_node(vd, VarDecl, node);
-		ssaModule *m = proc->module;
-		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
-
-		if (vd->values.count == 0) { // declared and zero-initialized
-			for_array(i, vd->names) {
-				AstNode *name = vd->names.e[i];
-				if (!ssa_is_blank_ident(name)) {
-					ssa_add_local_for_identifier(proc, name, true);
-				}
-			}
-		} else { // Tuple(s)
-			Array(ssaAddr) lvals;
-			ssaValueArray  inits;
-			array_init_reserve(&lvals, m->tmp_allocator, vd->names.count);
-			array_init_reserve(&inits, m->tmp_allocator, vd->names.count);
-
-			for_array(i, vd->names) {
-				AstNode *name = vd->names.e[i];
-				ssaAddr lval = ssa_make_addr(NULL, NULL);
-				if (!ssa_is_blank_ident(name)) {
-					ssa_add_local_for_identifier(proc, name, false);
-					lval = ssa_build_addr(proc, name);
-				}
-
-				array_add(&lvals, lval);
-			}
-
-			for_array(i, vd->values) {
-				ssaValue *init = ssa_build_expr(proc, vd->values.e[i]);
-				Type *t = ssa_type(init);
-				if (t->kind == Type_Tuple) {
-					for (isize i = 0; i < t->Tuple.variable_count; i++) {
-						Entity *e = t->Tuple.variables[i];
-						ssaValue *v = ssa_emit_struct_ev(proc, init, i);
-						array_add(&inits, v);
-					}
-				} else {
-					array_add(&inits, init);
-				}
-			}
-
-
-			for_array(i, inits) {
-				if (lvals.e[i].addr == NULL) {
-					continue;
-				}
-				ssaValue *v = ssa_emit_conv(proc, inits.e[i], ssa_addr_type(lvals.e[i]));
-				ssa_addr_store(proc, lvals.e[i], v);
-			}
-		}
-
-		gb_temp_arena_memory_end(tmp);
-	case_end;
-
-	case_ast_node(pd, ProcDecl, node);
-		if (pd->body != NULL) {
-			CheckerInfo *info = proc->module->info;
-
-			Entity **found = map_entity_get(&info->definitions, hash_pointer(pd->name));
-			GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string));
-			Entity *e = *found;
-
-
-			if (map_entity_get(&proc->module->min_dep_map, hash_pointer(e)) == NULL) {
-				// NOTE(bill): Nothing depends upon it so doesn't need to be built
-				break;
-			}
-
-			// NOTE(bill): Generate a new name
-			// parent.name-guid
-			String original_name = pd->name->Ident.string;
-			String pd_name = original_name;
-			if (pd->link_name.len > 0) {
-				pd_name = pd->link_name;
-			}
-
-			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)proc->children.count;
-			name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(pd_name), guid);
-			String name = make_string(name_text, name_len-1);
-
-
-			ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
-			                                           proc->module, e, e->type, pd->type, pd->body, name);
-
-			value->Proc.tags = pd->tags;
-			value->Proc.parent = proc;
-
-			ssa_module_add_value(proc->module, e, value);
-			array_add(&proc->children, &value->Proc);
-			array_add(&proc->module->procs_to_generate, value);
-		} else {
-			CheckerInfo *info = proc->module->info;
-
-			Entity **found = map_entity_get(&info->definitions, hash_pointer(pd->name));
-			GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string));
-			Entity *e = *found;
-
-			// FFI - Foreign function interace
-			String original_name = pd->name->Ident.string;
-			String name = original_name;
-			if (pd->foreign_name.len > 0) {
-				name = pd->foreign_name;
-			}
-
-			ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
-			                                           proc->module, e, e->type, pd->type, pd->body, name);
-
-			value->Proc.tags = pd->tags;
-
-			ssa_module_add_value(proc->module, e, value);
-			ssa_build_proc(value, proc);
-
-			if (value->Proc.tags & ProcTag_foreign) {
-				HashKey key = hash_string(name);
-				ssaValue **prev_value = map_ssa_value_get(&proc->module->members, key);
-				if (prev_value == NULL) {
-					// NOTE(bill): Don't do mutliple declarations in the IR
-					map_ssa_value_set(&proc->module->members, key, value);
-				}
-			} else {
-				array_add(&proc->children, &value->Proc);
-			}
-		}
-	case_end;
-
-	case_ast_node(td, TypeDecl, node);
-
-		// NOTE(bill): Generate a new name
-		// parent_proc.name-guid
-		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)proc->module->members.entries.count;
-		name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(td_name), guid);
-		String name = make_string(name_text, name_len-1);
-
-		Entity **found = map_entity_get(&proc->module->info->definitions, hash_pointer(td->name));
-		GB_ASSERT(found != NULL);
-		Entity *e = *found;
-		ssaValue *value = ssa_make_value_type_name(proc->module->allocator,
-		                                           name, e->type);
-		map_string_set(&proc->module->type_names, hash_pointer(e->type), name);
-		ssa_gen_global_type_name(proc->module, e, name);
-	case_end;
-
-	case_ast_node(ids, IncDecStmt, node);
-		ssa_emit_comment(proc, str_lit("IncDecStmt"));
-		TokenKind op = ids->op.kind;
-		if (op == Token_Increment) {
-			op = Token_Add;
-		} else if (op == Token_Decrement) {
-			op = Token_Sub;
-		}
-		ssaAddr lval = ssa_build_addr(proc, ids->expr);
-		ssaValue *one = ssa_emit_conv(proc, v_one, ssa_addr_type(lval));
-		ssa_build_assign_op(proc, lval, one, op);
-
-	case_end;
-
-	case_ast_node(as, AssignStmt, node);
-		ssa_emit_comment(proc, str_lit("AssignStmt"));
-
-		ssaModule *m = proc->module;
-		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
-
-		switch (as->op.kind) {
-		case Token_Eq: {
-			Array(ssaAddr) lvals;
-			array_init(&lvals, m->tmp_allocator);
-
-			for_array(i, as->lhs) {
-				AstNode *lhs = as->lhs.e[i];
-				ssaAddr lval = {0};
-				if (!ssa_is_blank_ident(lhs)) {
-					lval = ssa_build_addr(proc, lhs);
-				}
-				array_add(&lvals, lval);
-			}
-
-			if (as->lhs.count == as->rhs.count) {
-				if (as->lhs.count == 1) {
-					AstNode *rhs = as->rhs.e[0];
-					ssaValue *init = ssa_build_expr(proc, rhs);
-					ssa_addr_store(proc, lvals.e[0], init);
-				} else {
-					ssaValueArray inits;
-					array_init_reserve(&inits, m->tmp_allocator, lvals.count);
-
-					for_array(i, as->rhs) {
-						ssaValue *init = ssa_build_expr(proc, as->rhs.e[i]);
-						array_add(&inits, init);
-					}
-
-					for_array(i, inits) {
-						ssa_addr_store(proc, lvals.e[i], inits.e[i]);
-					}
-				}
-			} else {
-				ssaValueArray inits;
-				array_init_reserve(&inits, m->tmp_allocator, lvals.count);
-
-				for_array(i, as->rhs) {
-					ssaValue *init = ssa_build_expr(proc, as->rhs.e[i]);
-					Type *t = ssa_type(init);
-					// TODO(bill): refactor for code reuse as this is repeated a bit
-					if (t->kind == Type_Tuple) {
-						for (isize i = 0; i < t->Tuple.variable_count; i++) {
-							Entity *e = t->Tuple.variables[i];
-							ssaValue *v = ssa_emit_struct_ev(proc, init, i);
-							array_add(&inits, v);
-						}
-					} else {
-						array_add(&inits, init);
-					}
-				}
-
-				for_array(i, inits) {
-					ssa_addr_store(proc, lvals.e[i], inits.e[i]);
-				}
-			}
-
-		} break;
-
-		default: {
-			// NOTE(bill): Only 1 += 1 is allowed, no tuples
-			// +=, -=, etc
-			i32 op = cast(i32)as->op.kind;
-			op += Token_Add - Token_AddEq; // Convert += to +
-			ssaAddr lhs = ssa_build_addr(proc, as->lhs.e[0]);
-			ssaValue *value = ssa_build_expr(proc, as->rhs.e[0]);
-			ssa_build_assign_op(proc, lhs, value, cast(TokenKind)op);
-		} break;
-		}
-
-		gb_temp_arena_memory_end(tmp);
-	case_end;
-
-	case_ast_node(es, ExprStmt, node);
-		// NOTE(bill): No need to use return value
-		ssa_build_expr(proc, es->expr);
-	case_end;
-
-	case_ast_node(bs, BlockStmt, node);
-		ssa_open_scope(proc);
-		ssa_build_stmt_list(proc, bs->stmts);
-		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
-	case_end;
-
-	case_ast_node(ds, DeferStmt, node);
-		ssa_emit_comment(proc, str_lit("DeferStmt"));
-		isize scope_index = proc->scope_index;
-		if (ds->stmt->kind == AstNode_BlockStmt) {
-			scope_index--;
-		}
-		ssa_add_defer_node(proc, scope_index, ds->stmt);
-	case_end;
-
-	case_ast_node(rs, ReturnStmt, node);
-		ssa_emit_comment(proc, str_lit("ReturnStmt"));
-		ssaValue *v = NULL;
-		TypeTuple *return_type_tuple  = &proc->type->Proc.results->Tuple;
-		isize return_count = proc->type->Proc.result_count;
-		if (return_count == 0) {
-			// No return values
-		} else if (return_count == 1) {
-			Entity *e = return_type_tuple->variables[0];
-			v = ssa_emit_conv(proc, ssa_build_expr(proc, rs->results.e[0]), e->type);
-		} else {
-			gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
-
-			ssaValueArray results;
-			array_init_reserve(&results, proc->module->tmp_allocator, return_count);
-
-			for_array(res_index, rs->results) {
-				ssaValue *res = ssa_build_expr(proc, rs->results.e[res_index]);
-				Type *t = ssa_type(res);
-				if (t->kind == Type_Tuple) {
-					for (isize i = 0; i < t->Tuple.variable_count; i++) {
-						Entity *e = t->Tuple.variables[i];
-						ssaValue *v = ssa_emit_struct_ev(proc, res, i);
-						array_add(&results, v);
-					}
-				} else {
-					array_add(&results, res);
-				}
-			}
-
-			Type *ret_type = proc->type->Proc.results;
-			v = ssa_add_local_generated(proc, ret_type);
-			for_array(i, results) {
-				Entity *e = return_type_tuple->variables[i];
-				ssaValue *res = ssa_emit_conv(proc, results.e[i], e->type);
-				ssaValue *field = ssa_emit_struct_ep(proc, v, i);
-				ssa_emit_store(proc, field, res);
-			}
-
-			v = ssa_emit_load(proc, v);
-
-			gb_temp_arena_memory_end(tmp);
-		}
-		ssa_emit_return(proc, v);
-
-	case_end;
-
-	case_ast_node(is, IfStmt, node);
-		ssa_emit_comment(proc, str_lit("IfStmt"));
-		if (is->init != NULL) {
-			ssaBlock *init = ssa_add_block(proc, node, "if.init");
-			ssa_emit_jump(proc, init);
-			proc->curr_block = init;
-			ssa_build_stmt(proc, is->init);
-		}
-		ssaBlock *then = ssa_add_block(proc, node, "if.then");
-		ssaBlock *done = ssa_add_block(proc, node, "if.done"); // NOTE(bill): Append later
-		ssaBlock *else_ = done;
-		if (is->else_stmt != NULL) {
-			else_ = ssa_add_block(proc, is->else_stmt, "if.else");
-		}
-
-		ssa_build_cond(proc, is->cond, then, else_);
-		proc->curr_block = then;
-
-		ssa_open_scope(proc);
-		ssa_build_stmt(proc, is->body);
-		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
-
-		ssa_emit_jump(proc, done);
-
-		if (is->else_stmt != NULL) {
-			proc->curr_block = else_;
-
-			ssa_open_scope(proc);
-			ssa_build_stmt(proc, is->else_stmt);
-			ssa_close_scope(proc, ssaDeferExit_Default, NULL);
-
-			ssa_emit_jump(proc, done);
-		}
-		proc->curr_block = done;
-	case_end;
-
-	case_ast_node(fs, ForStmt, node);
-		ssa_emit_comment(proc, str_lit("ForStmt"));
-		if (fs->init != NULL) {
-			ssaBlock *init = ssa_add_block(proc, node, "for.init");
-			ssa_emit_jump(proc, init);
-			proc->curr_block = init;
-			ssa_build_stmt(proc, fs->init);
-		}
-		ssaBlock *body = ssa_add_block(proc, node, "for.body");
-		ssaBlock *done = ssa_add_block(proc, node, "for.done"); // NOTE(bill): Append later
-
-		ssaBlock *loop = body;
-
-		if (fs->cond != NULL) {
-			loop = ssa_add_block(proc, node, "for.loop");
-		}
-		ssaBlock *cont = loop;
-		if (fs->post != NULL) {
-			cont = ssa_add_block(proc, node, "for.post");
-
-		}
-		ssa_emit_jump(proc, loop);
-		proc->curr_block = loop;
-		if (loop != body) {
-			ssa_build_cond(proc, fs->cond, body, done);
-			proc->curr_block = body;
-		}
-
-		ssa_push_target_list(proc, done, cont, NULL);
-
-		ssa_open_scope(proc);
-		ssa_build_stmt(proc, fs->body);
-		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
-
-		ssa_pop_target_list(proc);
-		ssa_emit_jump(proc, cont);
-
-		if (fs->post != NULL) {
-			proc->curr_block = cont;
-			ssa_build_stmt(proc, fs->post);
-			ssa_emit_jump(proc, loop);
-		}
-
-
-		proc->curr_block = done;
-
-	case_end;
-
-	case_ast_node(ms, MatchStmt, node);
-		ssa_emit_comment(proc, str_lit("MatchStmt"));
-		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_add_block(proc, node, "match.done"); // NOTE(bill): Append later
-
-		ast_node(body, BlockStmt, ms->body);
-
-		AstNodeArray default_stmts = {0};
-		ssaBlock *default_fall = NULL;
-		ssaBlock *default_block = NULL;
-
-		ssaBlock *fall = NULL;
-		bool append_fall = false;
-
-		isize case_count = body->stmts.count;
-		for_array(i, body->stmts) {
-			AstNode *clause = body->stmts.e[i];
-			ssaBlock *body = fall;
-
-			ast_node(cc, CaseClause, clause);
-
-			if (body == NULL) {
-				if (cc->list.count == 0) {
-					body = ssa_add_block(proc, clause, "match.dflt.body");
-				} else {
-					body = ssa_add_block(proc, clause, "match.case.body");
-				}
-			}
-			if (append_fall && body == fall) {
-				append_fall = false;
-			}
-
-			fall = done;
-			if (i+1 < case_count) {
-				append_fall = true;
-				fall = ssa_add_block(proc, clause, "match.fall.body");
-			}
-
-			if (cc->list.count == 0) {
-				// default case
-				default_stmts = cc->stmts;
-				default_fall  = fall;
-				default_block = body;
-				continue;
-			}
-
-			ssaBlock *next_cond = NULL;
-			for_array(j, cc->list) {
-				AstNode *expr = cc->list.e[j];
-				next_cond = ssa_add_block(proc, clause, "match.case.next");
-
-				ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, ssa_build_expr(proc, expr));
-				ssa_emit_if(proc, cond, body, next_cond);
-				proc->curr_block = next_cond;
-			}
-			proc->curr_block = body;
-
-			ssa_push_target_list(proc, done, NULL, fall);
-			ssa_open_scope(proc);
-			ssa_build_stmt_list(proc, cc->stmts);
-			ssa_close_scope(proc, ssaDeferExit_Default, body);
-			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);
-			proc->curr_block = default_block;
-
-			ssa_push_target_list(proc, done, NULL, default_fall);
-			ssa_open_scope(proc);
-			ssa_build_stmt_list(proc, default_stmts);
-			ssa_close_scope(proc, ssaDeferExit_Default, default_block);
-			ssa_pop_target_list(proc);
-		}
-
-		ssa_emit_jump(proc, done);
-		proc->curr_block = done;
-	case_end;
-
-
-	case_ast_node(ms, TypeMatchStmt, node);
-		ssa_emit_comment(proc, str_lit("TypeMatchStmt"));
-		gbAllocator allocator = proc->module->allocator;
-
-		ssaValue *parent = ssa_build_expr(proc, ms->tag);
-		bool is_union_ptr = false;
-		bool is_any = false;
-		GB_ASSERT(check_valid_type_match_type(ssa_type(parent), &is_union_ptr, &is_any));
-
-		ssaValue *tag_index = NULL;
-		ssaValue *union_data = NULL;
-		if (is_union_ptr) {
-			ssa_emit_comment(proc, str_lit("get union's tag"));
-			tag_index = ssa_emit_load(proc, ssa_emit_union_tag_ptr(proc, parent));
-			union_data = ssa_emit_conv(proc, parent, t_rawptr);
-		}
-
-		ssaBlock *start_block = ssa_add_block(proc, node, "type-match.case.first");
-		ssa_emit_jump(proc, start_block);
-		proc->curr_block = start_block;
-
-		ssaBlock *done = ssa_add_block(proc, node, "type-match.done"); // NOTE(bill): Append later
-
-		ast_node(body, BlockStmt, ms->body);
-
-		String tag_var_name = ms->var->Ident.string;
-
-		AstNodeArray default_stmts = {0};
-		ssaBlock *default_block = NULL;
-
-
-		isize case_count = body->stmts.count;
-		for_array(i, body->stmts) {
-			AstNode *clause = body->stmts.e[i];
-			ast_node(cc, CaseClause, clause);
-
-			if (cc->list.count == 0) {
-				// default case
-				default_stmts = cc->stmts;
-				default_block = ssa_add_block(proc, clause, "type-match.dflt.body");
-				continue;
-			}
-
-
-			ssaBlock *body = ssa_add_block(proc, clause, "type-match.case.body");
-
-			Scope *scope = *map_scope_get(&proc->module->info->scopes, hash_pointer(clause));
-			Entity *tag_var_entity = current_scope_lookup_entity(scope, tag_var_name);
-			GB_ASSERT_MSG(tag_var_entity != NULL, "%.*s", LIT(tag_var_name));
-
-			ssaBlock *next_cond = NULL;
-			ssaValue *cond = NULL;
-
-			if (is_union_ptr) {
-				Type *bt = type_deref(tag_var_entity->type);
-				ssaValue *index = NULL;
-				Type *ut = base_type(type_deref(ssa_type(parent)));
-				GB_ASSERT(ut->Record.kind == TypeRecord_Union);
-				for (isize field_index = 1; field_index < ut->Record.field_count; field_index++) {
-					Entity *f = ut->Record.fields[field_index];
-					if (are_types_identical(f->type, bt)) {
-						index = ssa_make_const_int(allocator, field_index);
-						break;
-					}
-				}
-				GB_ASSERT(index != NULL);
-
-				ssaValue *tag_var = ssa_add_local(proc, tag_var_entity);
-				ssaValue *data_ptr = ssa_emit_conv(proc, union_data, tag_var_entity->type);
-				ssa_emit_store(proc, tag_var, data_ptr);
-
-				cond = ssa_emit_comp(proc, Token_CmpEq, tag_index, index);
-			} else if (is_any) {
-				Type *type = tag_var_entity->type;
-				ssaValue *any_data = ssa_emit_struct_ev(proc, parent, 1);
-				ssaValue *data = ssa_emit_conv(proc, any_data, make_type_pointer(proc->module->allocator, type));
-				ssa_module_add_value(proc->module, tag_var_entity, data);
-
-				ssaValue *any_ti  = ssa_emit_struct_ev(proc, parent, 0);
-				ssaValue *case_ti = ssa_type_info(proc, type);
-				cond = ssa_emit_comp(proc, Token_CmpEq, any_ti, case_ti);
-			} else {
-				GB_PANIC("Invalid type for type match statement");
-			}
-
-			next_cond = ssa_add_block(proc, clause, "type-match.case.next");
-			ssa_emit_if(proc, cond, body, next_cond);
-			proc->curr_block = next_cond;
-
-			proc->curr_block = body;
-
-			ssa_push_target_list(proc, done, NULL, NULL);
-			ssa_open_scope(proc);
-			ssa_build_stmt_list(proc, cc->stmts);
-			ssa_close_scope(proc, ssaDeferExit_Default, body);
-			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);
-			proc->curr_block = default_block;
-
-			ssa_push_target_list(proc, done, NULL, NULL);
-			ssa_open_scope(proc);
-			ssa_build_stmt_list(proc, default_stmts);
-			ssa_close_scope(proc, ssaDeferExit_Default, default_block);
-			ssa_pop_target_list(proc);
-		}
-
-		ssa_emit_jump(proc, done);
-		proc->curr_block = done;
-	case_end;
-
-	case_ast_node(bs, BranchStmt, node);
-		ssaBlock *block = NULL;
-		switch (bs->token.kind) {
-		case Token_break:
-			for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
-				block = t->break_;
-			}
-			break;
-		case Token_continue:
-			for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
-				block = t->continue_;
-			}
-			break;
-		case Token_fallthrough:
-			for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
-				block = t->fallthrough_;
-			}
-			break;
-		}
-		if (block != NULL) {
-			ssa_emit_defer_stmts(proc, ssaDeferExit_Branch, block);
-		}
-		switch (bs->token.kind) {
-		case Token_break:       ssa_emit_comment(proc, str_lit("break"));       break;
-		case Token_continue:    ssa_emit_comment(proc, str_lit("continue"));    break;
-		case Token_fallthrough: ssa_emit_comment(proc, str_lit("fallthrough")); break;
-		}
-		ssa_emit_jump(proc, block);
-		ssa_emit_unreachable(proc);
-	case_end;
-
-
-
-	case_ast_node(pa, PushAllocator, node);
-		ssa_emit_comment(proc, str_lit("PushAllocator"));
-		ssa_open_scope(proc);
-
-		ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context);
-		ssaValue *prev_context = ssa_add_local_generated(proc, t_context);
-		ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr));
-
-		ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context)));
-
-		ssaValue *gep = ssa_emit_struct_ep(proc, context_ptr, 1);
-		ssa_emit_store(proc, gep, ssa_build_expr(proc, pa->expr));
-
-		ssa_build_stmt(proc, pa->body);
-
-		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
-	case_end;
-
-
-	case_ast_node(pa, PushContext, node);
-		ssa_emit_comment(proc, str_lit("PushContext"));
-		ssa_open_scope(proc);
-
-		ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context);
-		ssaValue *prev_context = ssa_add_local_generated(proc, t_context);
-		ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr));
-
-		ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context)));
-
-		ssa_emit_store(proc, context_ptr, ssa_build_expr(proc, pa->expr));
-
-		ssa_build_stmt(proc, pa->body);
-
-		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
-	case_end;
-
-
-	}
-}
-
-
-
-
-
-
-
-////////////////////////////////////////////////////////////////
-//
-// @Procedure
-//
-////////////////////////////////////////////////////////////////
-
-void ssa_number_proc_registers(ssaProcedure *proc) {
-	i32 reg_index = 0;
-	for_array(i, proc->blocks) {
-		ssaBlock *b = proc->blocks.e[i];
-		b->index = i;
-		for_array(j, b->instrs) {
-			ssaValue *value = b->instrs.e[j];
-			GB_ASSERT(value->kind == ssaValue_Instr);
-			ssaInstr *instr = &value->Instr;
-			if (ssa_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions
-				continue;
-			}
-			value->index = reg_index;
-			reg_index++;
-		}
-	}
-}
-
-void ssa_begin_procedure_body(ssaProcedure *proc) {
-	array_add(&proc->module->procs, proc);
-
-	array_init(&proc->blocks,      heap_allocator());
-	array_init(&proc->defer_stmts, heap_allocator());
-	array_init(&proc->children,    heap_allocator());
-
-	proc->decl_block  = ssa_add_block(proc, proc->type_expr, "decls");
-	proc->entry_block = ssa_add_block(proc, proc->type_expr, "entry");
-	proc->curr_block  = proc->entry_block;
-
-	if (proc->type->Proc.params != NULL) {
-		TypeTuple *params = &proc->type->Proc.params->Tuple;
-		for (isize i = 0; i < params->variable_count; i++) {
-			Entity *e = params->variables[i];
-			ssaValue *param = ssa_add_param(proc, e);
-			array_add(&proc->params, param);
-		}
-	}
-}
-
-
-void ssa_end_procedure_body(ssaProcedure *proc) {
-	if (proc->type->Proc.result_count == 0) {
-		ssa_emit_return(proc, NULL);
-	}
-
-	if (proc->curr_block->instrs.count == 0) {
-		ssa_emit_unreachable(proc);
-	}
-
-	proc->curr_block = proc->decl_block;
-	ssa_emit_jump(proc, proc->entry_block);
-
-	ssa_number_proc_registers(proc);
-}
-
-
-void ssa_insert_code_before_proc(ssaProcedure* proc, ssaProcedure *parent) {
-	if (parent == NULL) {
-		if (str_eq(proc->name, str_lit("main"))) {
-			ssa_emit_startup_runtime(proc);
-		}
-	}
-}
-
-void ssa_build_proc(ssaValue *value, ssaProcedure *parent) {
-	ssaProcedure *proc = &value->Proc;
-
-	proc->parent = parent;
-
-	if (proc->entity != NULL) {
-		ssaModule *m = proc->module;
-		CheckerInfo *info = m->info;
-		Entity *e = proc->entity;
-		String filename = e->token.pos.file;
-		AstFile **found = map_ast_file_get(&info->files, hash_string(filename));
-		GB_ASSERT(found != NULL);
-		AstFile *f = *found;
-		ssaDebugInfo *di_file = NULL;
-
-		ssaDebugInfo **di_file_found = map_ssa_debug_info_get(&m->debug_info, hash_pointer(f));
-		if (di_file_found) {
-			di_file = *di_file_found;
-			GB_ASSERT(di_file->kind == ssaDebugInfo_File);
-		} else {
-			di_file = ssa_add_debug_info_file(proc, f);
-		}
-
-		ssa_add_debug_info_proc(proc, e, proc->name, di_file);
-	}
-
-	if (proc->body != NULL) {
-		u32 prev_stmt_state_flags = proc->module->stmt_state_flags;
-
-		if (proc->tags != 0) {
-			u32 in = proc->tags;
-			u32 out = proc->module->stmt_state_flags;
-			if (in & ProcTag_bounds_check) {
-				out |= StmtStateFlag_bounds_check;
-				out &= ~StmtStateFlag_no_bounds_check;
-			} else if (in & ProcTag_no_bounds_check) {
-				out |= StmtStateFlag_no_bounds_check;
-				out &= ~StmtStateFlag_bounds_check;
-			}
-			proc->module->stmt_state_flags = out;
-		}
-
-
-		ssa_begin_procedure_body(proc);
-		ssa_insert_code_before_proc(proc, parent);
-		ssa_build_stmt(proc, proc->body);
-		ssa_end_procedure_body(proc);
-
-		proc->module->stmt_state_flags = prev_stmt_state_flags;
-	}
-}
-
-
-
-
-
-
-
-////////////////////////////////////////////////////////////////
-//
-// @Module
-//
-////////////////////////////////////////////////////////////////
-
-
-
-void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v) {
-	map_ssa_value_set(&m->values, hash_pointer(e), v);
-}
-
-void ssa_init_module(ssaModule *m, Checker *c) {
-	// TODO(bill): Determine a decent size for the arena
-	isize token_count = c->parser->total_token_count;
-	isize arena_size = 4 * token_count * gb_size_of(ssaValue);
-	gb_arena_init_from_allocator(&m->arena, heap_allocator(), arena_size);
-	gb_arena_init_from_allocator(&m->tmp_arena, heap_allocator(), arena_size);
-	m->allocator     = gb_arena_allocator(&m->arena);
-	m->tmp_allocator = gb_arena_allocator(&m->tmp_arena);
-	m->info = &c->info;
-	m->sizes = c->sizes;
-
-	map_ssa_value_init(&m->values,  heap_allocator());
-	map_ssa_value_init(&m->members, heap_allocator());
-	map_ssa_debug_info_init(&m->debug_info, heap_allocator());
-	map_string_init(&m->type_names, heap_allocator());
-	array_init(&m->procs,    heap_allocator());
-	array_init(&m->procs_to_generate, heap_allocator());
-
-	// Default states
-	m->stmt_state_flags = 0;
-	m->stmt_state_flags |= StmtStateFlag_bounds_check;
-
-	{
-		// Add type info data
-		{
-			String name = str_lit(SSA_TYPE_INFO_DATA_NAME);
-			isize count = c->info.type_info_map.entries.count;
-			Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), make_type_array(m->allocator, t_type_info, count));
-			ssaValue *g = ssa_make_value_global(m->allocator, e, NULL);
-			g->Global.is_private  = true;
-			ssa_module_add_value(m, e, g);
-			map_ssa_value_set(&m->members, hash_string(name), g);
-		}
-
-		// Type info member buffer
-		{
-			// NOTE(bill): Removes need for heap allocation by making it global memory
-			isize count = 0;
-
-			for_array(entry_index, m->info->type_info_map.entries) {
-				MapIsizeEntry *entry = &m->info->type_info_map.entries.e[entry_index];
-				Type *t = cast(Type *)cast(uintptr)entry->key.key;
-
-				switch (t->kind) {
-				case Type_Record:
-					switch (t->Record.kind) {
-					case TypeRecord_Struct:
-					case TypeRecord_RawUnion:
-						count += t->Record.field_count;
-					}
-					break;
-				case Type_Tuple:
-					count += t->Tuple.variable_count;
-					break;
-				}
-			}
-
-			String name = str_lit(SSA_TYPE_INFO_DATA_MEMBER_NAME);
-			Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name),
-			                                 make_type_array(m->allocator, t_type_info_member, count));
-			ssaValue *g = ssa_make_value_global(m->allocator, e, NULL);
-			ssa_module_add_value(m, e, g);
-			map_ssa_value_set(&m->members, hash_string(name), g);
-		}
-	}
-
-	{
-		ssaDebugInfo *di = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_CompileUnit);
-		di->CompileUnit.file = m->info->files.entries.e[0].value; // Zeroth is the init file
-		di->CompileUnit.producer = str_lit("odin");
-
-		map_ssa_debug_info_set(&m->debug_info, hash_pointer(m), di);
-	}
-}
-
-void ssa_destroy_module(ssaModule *m) {
-	map_ssa_value_destroy(&m->values);
-	map_ssa_value_destroy(&m->members);
-	map_string_destroy(&m->type_names);
-	map_ssa_debug_info_destroy(&m->debug_info);
-	array_free(&m->procs_to_generate);
-	gb_arena_free(&m->arena);
-}
-
-
-
-////////////////////////////////////////////////////////////////
-//
-// @Code Generation
-//
-////////////////////////////////////////////////////////////////
-
-
-bool ssa_gen_init(ssaGen *s, Checker *c) {
-	if (global_error_collector.count != 0) {
-		return false;
-	}
-
-	isize tc = c->parser->total_token_count;
-	if (tc < 2) {
-		return false;
-	}
-
-	ssa_init_module(&s->module, c);
-	s->module.generate_debug_info = false;
-
-	// TODO(bill): generate appropriate output name
-	int pos = cast(int)string_extension_position(c->parser->init_fullpath);
-	gbFileError err = gb_file_create(&s->output_file, gb_bprintf("%.*s.ll", pos, c->parser->init_fullpath.text));
-	if (err != gbFileError_None) {
-		return false;
-	}
-
-	return true;
-}
-
-void ssa_gen_destroy(ssaGen *s) {
-	ssa_destroy_module(&s->module);
-	gb_file_close(&s->output_file);
-}
-
-String ssa_mangle_name(ssaGen *s, String path, String name) {
-	// NOTE(bill): prefix names not in the init scope
-	// TODO(bill): make robust and not just rely on the file's name
-
-	ssaModule *m = &s->module;
-	CheckerInfo *info = m->info;
-	gbAllocator a = m->allocator;
-	AstFile *file = *map_ast_file_get(&info->files, hash_string(path));
-
-	char *str = gb_alloc_array(a, char, path.len+1);
-	gb_memmove(str, path.text, path.len);
-	str[path.len] = 0;
-	for (isize i = 0; i < path.len; i++) {
-		if (str[i] == '\\') {
-			str[i] = '/';
-		}
-	}
-
-	char const *base = gb_path_base_name(str);
-	char const *ext = gb_path_extension(base);
-	isize base_len = ext-1-base;
-
-	isize max_len = base_len + 1 + 10 + 1 + name.len;
-	u8 *new_name = gb_alloc_array(a, u8, max_len);
-	isize new_name_len = gb_snprintf(
-		cast(char *)new_name, max_len,
-		"%.*s-%u.%.*s",
-		cast(int)base_len, base,
-		file->id,
-		LIT(name));
-
-	return make_string(new_name, new_name_len-1);
-}
-
-ssaValue *ssa_get_type_info_ptr(ssaProcedure *proc, ssaValue *type_info_data, Type *type) {
-	i32 index = cast(i32)ssa_type_info_index(proc->module->info, type);
-	// gb_printf_err("%d %s\n", index, type_to_string(type));
-	return ssa_emit_array_epi(proc, type_info_data, index);
-}
-
-ssaValue *ssa_type_info_member_offset(ssaProcedure *proc, ssaValue *data, isize count, i32 *index) {
-	ssaValue *offset = ssa_emit_array_epi(proc, data, *index);
-	*index += count;
-	return offset;
-}
-
-void ssa_gen_tree(ssaGen *s) {
-	ssaModule *m = &s->module;
-	CheckerInfo *info = m->info;
-	gbAllocator a = m->allocator;
-
-	if (v_zero == NULL) {
-		v_zero   = ssa_make_const_int (m->allocator, 0);
-		v_one    = ssa_make_const_int (m->allocator, 1);
-		v_zero32 = ssa_make_const_i32 (m->allocator, 0);
-		v_one32  = ssa_make_const_i32 (m->allocator, 1);
-		v_two32  = ssa_make_const_i32 (m->allocator, 2);
-		v_false  = ssa_make_const_bool(m->allocator, false);
-		v_true   = ssa_make_const_bool(m->allocator, true);
-	}
-
-	isize global_variable_max_count = 0;
-	Entity *entry_point = NULL;
-
-	for_array(i, info->entities.entries) {
-		MapDeclInfoEntry *entry = &info->entities.entries.e[i];
-		Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
-		String name = e->token.string;
-		if (e->kind == Entity_Variable) {
-			global_variable_max_count++;
-		} else if (e->kind == Entity_Procedure) {
-			if (e->scope->is_init && str_eq(name, str_lit("main"))) {
-				entry_point = e;
-			}
-		}
-	}
-
-	typedef struct ssaGlobalVariable {
-		ssaValue *var, *init;
-		DeclInfo *decl;
-	} ssaGlobalVariable;
-	Array(ssaGlobalVariable) global_variables;
-	array_init_reserve(&global_variables, m->tmp_allocator, global_variable_max_count);
-
-	m->min_dep_map = generate_minimum_dependency_map(info, entry_point);
-
-	for_array(i, info->entities.entries) {
-		MapDeclInfoEntry *entry = &info->entities.entries.e[i];
-		Entity *e = cast(Entity *)entry->key.ptr;
-		String name = e->token.string;
-		DeclInfo *decl = entry->value;
-		Scope *scope = e->scope;
-
-		if (!scope->is_file) {
-			continue;
-		}
-
-		if (map_entity_get(&m->min_dep_map, hash_pointer(e)) == NULL) {
-			// NOTE(bill): Nothing depends upon it so doesn't need to be built
-			continue;
-		}
-
-		if (!scope->is_global && !scope->is_init) {
-			name = ssa_mangle_name(s, e->token.pos.file, name);
-		}
-
-
-		switch (e->kind) {
-		case Entity_TypeName:
-			GB_ASSERT(e->type->kind == Type_Named);
-			map_string_set(&m->type_names, hash_pointer(e->type), name);
-			ssa_gen_global_type_name(m, e, name);
-			break;
-
-		case Entity_Variable: {
-			ssaValue *g = ssa_make_value_global(a, e, NULL);
-			if (decl->var_decl_tags & VarDeclTag_thread_local) {
-				g->Global.is_thread_local = true;
-			}
-			ssaGlobalVariable var = {0};
-			var.var = g;
-			var.decl = decl;
-
-			if (decl->init_expr != NULL) {
-				TypeAndValue *tav = map_tav_get(&info->types, hash_pointer(decl->init_expr));
-				if (tav != NULL) {
-					if (tav->value.kind != ExactValue_Invalid) {
-						ExactValue v = tav->value;
-						// if (v.kind != ExactValue_String) {
-							g->Global.value = ssa_add_module_constant(m, tav->type, v);
-						// }
-					}
-				}
-			}
-
-			if (g->Global.value == NULL) {
-				array_add(&global_variables, var);
-			}
-
-			map_ssa_value_set(&m->values,  hash_pointer(e), g);
-			map_ssa_value_set(&m->members, hash_string(name), g);
-		} break;
-
-		case Entity_Procedure: {
-			AstNodeProcDecl *pd = &decl->proc_decl->ProcDecl;
-			String original_name = name;
-			AstNode *body = pd->body;
-			if (pd->tags & ProcTag_foreign) {
-				name = pd->name->Ident.string;
-			}
-			if (pd->foreign_name.len > 0) {
-				name = pd->foreign_name;
-			} else if (pd->link_name.len > 0) {
-				name = pd->link_name;
-			}
-
-			ssaValue *p = ssa_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name);
-			p->Proc.tags = pd->tags;
-
-			map_ssa_value_set(&m->values, hash_pointer(e), p);
-			HashKey hash_name = hash_string(name);
-			if (map_ssa_value_get(&m->members, hash_name) == NULL) {
-				map_ssa_value_set(&m->members, hash_name, p);
-			}
-		} break;
-		}
-	}
-
-	for_array(i, m->members.entries) {
-		MapSsaValueEntry *entry = &m->members.entries.e[i];
-		ssaValue *v = entry->value;
-		if (v->kind == ssaValue_Proc)
-			ssa_build_proc(v, NULL);
-	}
-
-	ssaDebugInfo *compile_unit = m->debug_info.entries.e[0].value;
-	GB_ASSERT(compile_unit->kind == ssaDebugInfo_CompileUnit);
-	ssaDebugInfo *all_procs = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_AllProcs);
-
-	isize all_proc_max_count = 0;
-	for_array(i, m->debug_info.entries) {
-		MapSsaDebugInfoEntry *entry = &m->debug_info.entries.e[i];
-		ssaDebugInfo *di = entry->value;
-		di->id = i;
-		if (di->kind == ssaDebugInfo_Proc) {
-			all_proc_max_count++;
-		}
-	}
-
-	array_init_reserve(&all_procs->AllProcs.procs, m->allocator, all_proc_max_count);
-	map_ssa_debug_info_set(&m->debug_info, hash_pointer(all_procs), all_procs); // NOTE(bill): This doesn't need to be mapped
-	compile_unit->CompileUnit.all_procs = all_procs;
-
-
-	for_array(i, m->debug_info.entries) {
-		MapSsaDebugInfoEntry *entry = &m->debug_info.entries.e[i];
-		ssaDebugInfo *di = entry->value;
-		di->id = i;
-		if (di->kind == ssaDebugInfo_Proc) {
-			array_add(&all_procs->AllProcs.procs, di);
-		}
-	}
-
-
-	{ // Startup Runtime
-		// Cleanup(bill): probably better way of doing code insertion
-		String name = str_lit(SSA_STARTUP_RUNTIME_PROC_NAME);
-		Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope),
-		                                 NULL, 0,
-		                                 NULL, 0, false);
-		AstNode *body = gb_alloc_item(a, AstNode);
-		ssaValue *p = ssa_make_value_procedure(a, m, NULL, proc_type, NULL, body, name);
-		Token token = {0};
-		token.string = name;
-		Entity *e = make_entity_procedure(a, NULL, token, proc_type);
-
-		map_ssa_value_set(&m->values, hash_pointer(e), p);
-		map_ssa_value_set(&m->members, hash_string(name), p);
-
-		ssaProcedure *proc = &p->Proc;
-		proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea?
-
-		ssa_begin_procedure_body(proc);
-
-		// TODO(bill): Should do a dependency graph do check which order to initialize them in?
-		for_array(i, global_variables) {
-			ssaGlobalVariable *var = &global_variables.e[i];
-			if (var->decl->init_expr != NULL) {
-				var->init = ssa_build_expr(proc, var->decl->init_expr);
-			}
-		}
-
-		// NOTE(bill): Initialize constants first
-		for_array(i, global_variables) {
-			ssaGlobalVariable *var = &global_variables.e[i];
-			if (var->init != NULL) {
-				if (var->init->kind == ssaValue_Constant) {
-					ssa_emit_store(proc, var->var, var->init);
-				}
-			}
-		}
-
-		for_array(i, global_variables) {
-			ssaGlobalVariable *var = &global_variables.e[i];
-			if (var->init != NULL) {
-				if (var->init->kind != ssaValue_Constant) {
-					ssa_emit_store(proc, var->var, var->init);
-				}
-			}
-		}
-
-		{ // NOTE(bill): Setup type_info data
-			// TODO(bill): Try and make a lot of this constant aggregate literals in LLVM IR
-			ssaValue *type_info_data = NULL;
-			ssaValue *type_info_member_data = NULL;
-
-			ssaValue **found = NULL;
-			found = map_ssa_value_get(&proc->module->members, hash_string(str_lit(SSA_TYPE_INFO_DATA_NAME)));
-			GB_ASSERT(found != NULL);
-			type_info_data = *found;
-
-			found = map_ssa_value_get(&proc->module->members, hash_string(str_lit(SSA_TYPE_INFO_DATA_MEMBER_NAME)));
-			GB_ASSERT(found != NULL);
-			type_info_member_data = *found;
-
-			CheckerInfo *info = proc->module->info;
-
-			// Useful types
-			Type *t_i64_slice_ptr    = make_type_pointer(a, make_type_slice(a, t_i64));
-			Type *t_string_slice_ptr = make_type_pointer(a, make_type_slice(a, t_string));
-
-			i32 type_info_member_index = 0;
-
-			for_array(type_info_map_index, info->type_info_map.entries) {
-				MapIsizeEntry *entry = &info->type_info_map.entries.e[type_info_map_index];
-				Type *t = cast(Type *)cast(uintptr)entry->key.key;
-				t = default_type(t);
-				isize entry_index = entry->value;
-
-				ssaValue *tag = NULL;
-
-				switch (t->kind) {
-				case Type_Named: {
-					tag = ssa_add_local_generated(proc, t_type_info_named);
-
-					// TODO(bill): Which is better? The mangled name or actual name?
-					ssaValue *name = ssa_make_const_string(a, t->Named.type_name->token.string);
-					ssaValue *gtip = ssa_get_type_info_ptr(proc, type_info_data, t->Named.base);
-
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), name);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), gtip);
-				} break;
-
-				case Type_Basic:
-					switch (t->Basic.kind) {
-					case Basic_bool:
-						tag = ssa_add_local_generated(proc, t_type_info_boolean);
-						break;
-					case Basic_i8:
-					case Basic_u8:
-					case Basic_i16:
-					case Basic_u16:
-					case Basic_i32:
-					case Basic_u32:
-					case Basic_i64:
-					case Basic_u64:
-					case Basic_i128:
-					case Basic_u128:
-					case Basic_int:
-					case Basic_uint: {
-						tag = ssa_add_local_generated(proc, t_type_info_integer);
-						bool is_unsigned = (t->Basic.flags & BasicFlag_Unsigned) != 0;
-						ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
-						ssaValue *is_signed = ssa_make_const_bool(a, !is_unsigned);
-						ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits);
-						ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), is_signed);
-					} break;
-
-					// case Basic_f16:
-					case Basic_f32:
-					case Basic_f64:
-					// case Basic_f128:
-					{
-						tag = ssa_add_local_generated(proc, t_type_info_float);
-						ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
-						ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits);
-					} break;
-
-					case Basic_rawptr:
-						tag = ssa_add_local_generated(proc, t_type_info_pointer);
-						break;
-
-					case Basic_string:
-						tag = ssa_add_local_generated(proc, t_type_info_string);
-						break;
-
-					case Basic_any:
-						tag = ssa_add_local_generated(proc, t_type_info_any);
-						break;
-					}
-					break;
-
-				case Type_Pointer: {
-					tag = ssa_add_local_generated(proc, t_type_info_pointer);
-					ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Pointer.elem);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
-				} break;
-				case Type_Maybe: {
-					tag = ssa_add_local_generated(proc, t_type_info_maybe);
-					ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Maybe.elem);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
-				} break;
-				case Type_Array: {
-					tag = ssa_add_local_generated(proc, t_type_info_array);
-					ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Array.elem);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
-
-					isize ez = type_size_of(m->sizes, a, t->Array.elem);
-					ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1);
-					ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez));
-
-					ssaValue *count = ssa_emit_struct_ep(proc, tag, 2);
-					ssa_emit_store(proc, count, ssa_make_const_int(a, t->Array.count));
-
-				} break;
-				case Type_Slice: {
-					tag = ssa_add_local_generated(proc, t_type_info_slice);
-					ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Slice.elem);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
-
-					isize ez = type_size_of(m->sizes, a, t->Slice.elem);
-					ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1);
-					ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez));
-
-				} break;
-				case Type_Vector: {
-					tag = ssa_add_local_generated(proc, t_type_info_vector);
-					ssaValue *gep = ssa_get_type_info_ptr(proc, type_info_data, t->Vector.elem);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
-
-					isize ez = type_size_of(m->sizes, a, t->Vector.elem);
-					ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1);
-					ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez));
-
-					ssaValue *count = ssa_emit_struct_ep(proc, tag, 2);
-					ssa_emit_store(proc, count, ssa_make_const_int(a, t->Vector.count));
-
-					ssaValue *align = ssa_emit_struct_ep(proc, tag, 3);
-					ssa_emit_store(proc, count, ssa_make_const_int(a, type_align_of(m->sizes, a, t)));
-
-				} break;
-				case Type_Record: {
-					switch (t->Record.kind) {
-					case TypeRecord_Struct: {
-						tag = ssa_add_local_generated(proc, t_type_info_struct);
-
-						{
-							ssaValue *packed  = ssa_make_const_bool(a, t->Record.struct_is_packed);
-							ssaValue *ordered = ssa_make_const_bool(a, t->Record.struct_is_ordered);
-							ssaValue *size    = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
-							ssaValue *align   = ssa_make_const_int(a, type_align_of(m->sizes, a, t));
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 3), packed);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 4), ordered);
-						}
-
-						ssaValue *memory = ssa_type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index);
-
-						type_set_offsets(m->sizes, a, t); // NOTE(bill): Just incase the offsets have not been set yet
-						for (isize source_index = 0; source_index < t->Record.field_count; source_index++) {
-							// TODO(bill): Order fields in source order not layout order
-							Entity *f = t->Record.fields_in_src_order[source_index];
-							ssaValue *tip = ssa_get_type_info_ptr(proc, type_info_data, f->type);
-							i64 foffset = t->Record.struct_offsets[f->Variable.field_index];
-							GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field);
-
-							ssaValue *field     = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, source_index));
-							ssaValue *name      = ssa_emit_struct_ep(proc, field, 0);
-							ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1);
-							ssaValue *offset    = ssa_emit_struct_ep(proc, field, 2);
-
-							if (f->token.string.len > 0) {
-								ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string));
-							}
-							ssa_emit_store(proc, type_info, tip);
-							ssa_emit_store(proc, offset, ssa_make_const_int(a, foffset));
-						}
-
-						Type *slice_type = make_type_slice(a, t_type_info_member);
-						Type *slice_type_ptr = make_type_pointer(a, slice_type);
-						ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0);
-						ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count);
-
-						ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0);
-						ssaValue *len  = ssa_emit_struct_ep(proc, slice, 1);
-						ssaValue *cap  = ssa_emit_struct_ep(proc, slice, 2);
-
-						ssa_emit_store(proc, elem, memory);
-						ssa_emit_store(proc, len, field_count);
-						ssa_emit_store(proc, cap, field_count);
-					} break;
-					case TypeRecord_Union:
-						tag = ssa_add_local_generated(proc, t_type_info_union);
-						{
-							ssaValue *size    = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
-							ssaValue *align   = ssa_make_const_int(a, type_align_of(m->sizes, a, t));
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1),  size);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2),  align);
-						}
-						break;
-					case TypeRecord_RawUnion: {
-						tag = ssa_add_local_generated(proc, t_type_info_raw_union);
-						{
-							ssaValue *size    = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
-							ssaValue *align   = ssa_make_const_int(a, type_align_of(m->sizes, a, t));
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1),  size);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2),  align);
-						}
-
-						ssaValue *memory = ssa_type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index);
-
-						for (isize i = 0; i < t->Record.field_count; i++) {
-							ssaValue *field     = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i));
-							ssaValue *name      = ssa_emit_struct_ep(proc, field, 0);
-							ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1);
-							ssaValue *offset    = ssa_emit_struct_ep(proc, field, 2);
-
-							Entity *f = t->Record.fields[i];
-							ssaValue *tip = ssa_get_type_info_ptr(proc, type_info_data, f->type);
-
-							if (f->token.string.len > 0) {
-								ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string));
-							}
-							ssa_emit_store(proc, type_info, tip);
-							ssa_emit_store(proc, offset, ssa_make_const_int(a, 0));
-						}
-
-						Type *slice_type = make_type_slice(a, t_type_info_member);
-						Type *slice_type_ptr = make_type_pointer(a, slice_type);
-						ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0);
-						ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count);
-
-						ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0);
-						ssaValue *len  = ssa_emit_struct_ep(proc, slice, 1);
-						ssaValue *cap  = ssa_emit_struct_ep(proc, slice, 2);
-
-						ssa_emit_store(proc, elem, memory);
-						ssa_emit_store(proc, len, field_count);
-						ssa_emit_store(proc, cap, field_count);
-					} break;
-					case TypeRecord_Enum: {
-						tag = ssa_add_local_generated(proc, t_type_info_enum);
-						Type *enum_base = t->Record.enum_base;
-						if (enum_base == NULL) {
-							enum_base = t_int;
-						}
-						ssaValue *base = ssa_emit_struct_ep(proc, tag, 0);
-						ssa_emit_store(proc, base, ssa_get_type_info_ptr(proc, type_info_data, enum_base));
-
-						if (t->Record.other_field_count > 0) {
-							Entity **fields = t->Record.other_fields;
-							isize count = t->Record.other_field_count;
-							ssaValue *value_array = NULL;
-							ssaValue *name_array = NULL;
-
-
-							{
-								Token token = {Token_Identifier};
-								i32 id = cast(i32)entry_index;
-								char name_base[] = "__$enum_values";
-								isize name_len = gb_size_of(name_base) + 10;
-								token.string.text = gb_alloc_array(a, u8, name_len);
-								token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
-								                               "%s-%d", name_base, id)-1;
-								Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_i64, count));
-								value_array = ssa_make_value_global(a, e, NULL);
-								value_array->Global.is_private = true;
-								ssa_module_add_value(m, e, value_array);
-								map_ssa_value_set(&m->members, hash_string(token.string), value_array);
-							}
-							{
-								Token token = {Token_Identifier};
-								i32 id = cast(i32)entry_index;
-								char name_base[] = "__$enum_names";
-								isize name_len = gb_size_of(name_base) + 10;
-								token.string.text = gb_alloc_array(a, u8, name_len);
-								token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
-								                               "%s-%d", name_base, id)-1;
-								Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_string, count));
-								name_array = ssa_make_value_global(a, e, NULL);
-								name_array->Global.is_private = true;
-								ssa_module_add_value(m, e, name_array);
-								map_ssa_value_set(&m->members, hash_string(token.string), name_array);
-							}
-
-							for (isize i = 0; i < count; i++) {
-								ssaValue *value_gep = ssa_emit_array_epi(proc, value_array, i);
-								ssaValue *name_gep  = ssa_emit_array_epi(proc, name_array, i);
-
-								ssa_emit_store(proc, value_gep, ssa_make_const_i64(a, fields[i]->Constant.value.value_integer));
-								ssa_emit_store(proc, name_gep,  ssa_make_const_string(a, fields[i]->token.string));
-							}
-
-							ssaValue *v_count = ssa_make_const_int(a, count);
-
-
-							ssaValue *values = ssa_emit_struct_ep(proc, tag, 1);
-							ssaValue *names  = ssa_emit_struct_ep(proc, tag, 2);
-							ssaValue *value_slice = ssa_add_local_generated(proc, type_deref(t_i64_slice_ptr));
-							ssaValue *name_slice  = ssa_add_local_generated(proc, type_deref(t_string_slice_ptr));
-
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 0), ssa_array_elem(proc, value_array));
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 1), v_count);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 2), v_count);
-
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 0), ssa_array_elem(proc, name_array));
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 1), v_count);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 2), v_count);
-
-							ssa_emit_store(proc, values, ssa_emit_load(proc, value_slice));
-							ssa_emit_store(proc, names,  ssa_emit_load(proc, name_slice));
-						}
-					} break;
-					}
-				} break;
-
-				case Type_Tuple: {
-					tag = ssa_add_local_generated(proc, t_type_info_tuple);
-
-					{
-						ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t));
-						ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align);
-					}
-
-					ssaValue *memory = ssa_type_info_member_offset(proc, type_info_member_data, t->Tuple.variable_count, &type_info_member_index);
-
-					for (isize i = 0; i < t->Tuple.variable_count; i++) {
-						ssaValue *field     = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i));
-						ssaValue *name      = ssa_emit_struct_ep(proc, field, 0);
-						ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1);
-						// NOTE(bill): offset is not used for tuples
-
-						Entity *f = t->Tuple.variables[i];
-						ssaValue *tip = ssa_get_type_info_ptr(proc, type_info_data, f->type);
-
-						if (f->token.string.len > 0) {
-							ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string));
-						}
-						ssa_emit_store(proc, type_info, tip);
-					}
-
-					Type *slice_type = make_type_slice(a, t_type_info_member);
-					Type *slice_type_ptr = make_type_pointer(a, slice_type);
-					ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0);
-					ssaValue *variable_count = ssa_make_const_int(a, t->Tuple.variable_count);
-
-					ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0);
-					ssaValue *len  = ssa_emit_struct_ep(proc, slice, 1);
-					ssaValue *cap  = ssa_emit_struct_ep(proc, slice, 2);
-
-					ssa_emit_store(proc, elem, memory);
-					ssa_emit_store(proc, len, variable_count);
-					ssa_emit_store(proc, cap, variable_count);
-				} break;
-
-				case Type_Proc: {
-					tag = ssa_add_local_generated(proc, t_type_info_procedure);
-
-					ssaValue *params   = ssa_emit_struct_ep(proc, tag, 0);
-					ssaValue *results  = ssa_emit_struct_ep(proc, tag, 1);
-					ssaValue *variadic = ssa_emit_struct_ep(proc, tag, 2);
-
-					if (t->Proc.params) {
-						ssa_emit_store(proc, params, ssa_get_type_info_ptr(proc, type_info_data, t->Proc.params));
-					}
-					if (t->Proc.results) {
-						ssa_emit_store(proc, results, ssa_get_type_info_ptr(proc, type_info_data, t->Proc.results));
-					}
-					ssa_emit_store(proc, variadic, ssa_make_const_bool(a, t->Proc.variadic));
-
-					// TODO(bill): Type_Info for procedures
-				} break;
-				}
-
-				if (tag != NULL) {
-					ssaValue *gep = ssa_emit_array_epi(proc, type_info_data, entry_index);
-					ssaValue *val = ssa_emit_conv(proc, ssa_emit_load(proc, tag), t_type_info);
-					ssa_emit_store(proc, gep, val);
-				}
-			}
-		}
-
-		ssa_end_procedure_body(proc);
-	}
-
-	for_array(i, m->procs_to_generate) {
-		ssa_build_proc(m->procs_to_generate.e[i], m->procs_to_generate.e[i]->Proc.parent);
-	}
-
-	// {
-	// 	DWORD old_protect = 0;
-	// 	DWORD new_protect = PAGE_READONLY;
-	// 	BOOL ok = VirtualProtect(m->arena.physical_start, m->arena.total_size, new_protect, &old_protect);
-	// }
-
-
-
-	// m->layout = str_lit("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64");
-}
-

+ 0 - 493
src/ssa_opt.cpp

@@ -1,493 +0,0 @@
-// Optimizations for the SSA code
-
-void ssa_opt_add_operands(ssaValueArray *ops, ssaInstr *i) {
-	switch (i->kind) {
-	case ssaInstr_Comment:
-		break;
-	case ssaInstr_Local:
-		break;
-	case ssaInstr_ZeroInit:
-		array_add(ops, i->ZeroInit.address);
-		break;
-	case ssaInstr_Store:
-		array_add(ops, i->Store.address);
-		array_add(ops, i->Store.value);
-		break;
-	case ssaInstr_Load:
-		array_add(ops, i->Load.address);
-		break;
-	case ssaInstr_ArrayElementPtr:
-		array_add(ops, i->ArrayElementPtr.address);
-		array_add(ops, i->ArrayElementPtr.elem_index);
-		break;
-	case ssaInstr_StructElementPtr:
-		array_add(ops, i->StructElementPtr.address);
-		break;
-	case ssaInstr_PtrOffset:
-		array_add(ops, i->PtrOffset.address);
-		array_add(ops, i->PtrOffset.offset);
-		break;
-	case ssaInstr_ArrayExtractValue:
-		array_add(ops, i->ArrayExtractValue.address);
-		break;
-	case ssaInstr_StructExtractValue:
-		array_add(ops, i->StructExtractValue.address);
-		break;
-	case ssaInstr_Conv:
-		array_add(ops, i->Conv.value);
-		break;
-	case ssaInstr_Jump:
-		break;
-	case ssaInstr_If:
-		array_add(ops, i->If.cond);
-		break;
-	case ssaInstr_Return:
-		if (i->Return.value != NULL) {
-			array_add(ops, i->Return.value);
-		}
-		break;
-	case ssaInstr_Select:
-		array_add(ops, i->Select.cond);
-		break;
-	case ssaInstr_Phi:
-		for_array(j, i->Phi.edges) {
-			array_add(ops, i->Phi.edges.e[j]);
-		}
-		break;
-	case ssaInstr_Unreachable: break;
-	case ssaInstr_BinaryOp:
-		array_add(ops, i->BinaryOp.left);
-		array_add(ops, i->BinaryOp.right);
-		break;
-	case ssaInstr_Call:
-		array_add(ops, i->Call.value);
-		for (isize j = 0; j < i->Call.arg_count; j++) {
-			array_add(ops, i->Call.args[j]);
-		}
-		break;
-	case ssaInstr_VectorExtractElement:
-		array_add(ops, i->VectorExtractElement.vector);
-		array_add(ops, i->VectorExtractElement.index);
-		break;
-	case ssaInstr_VectorInsertElement:
-		array_add(ops, i->VectorInsertElement.vector);
-		array_add(ops, i->VectorInsertElement.elem);
-		array_add(ops, i->VectorInsertElement.index);
-		break;
-	case ssaInstr_VectorShuffle:
-		array_add(ops, i->VectorShuffle.vector);
-		break;
-	case ssaInstr_StartupRuntime:
-		break;
-	case ssaInstr_BoundsCheck:
-		array_add(ops, i->BoundsCheck.index);
-		array_add(ops, i->BoundsCheck.len);
-		break;
-	case ssaInstr_SliceBoundsCheck:
-		array_add(ops, i->SliceBoundsCheck.low);
-		array_add(ops, i->SliceBoundsCheck.high);
-		array_add(ops, i->SliceBoundsCheck.max);
-		break;
-
-
-	}
-}
-
-
-
-
-
-void ssa_opt_block_replace_pred(ssaBlock *b, ssaBlock *from, ssaBlock *to) {
-	for_array(i, b->preds) {
-		ssaBlock *pred = b->preds.e[i];
-		if (pred == from) {
-			b->preds.e[i] = to;
-		}
-	}
-}
-
-void ssa_opt_block_replace_succ(ssaBlock *b, ssaBlock *from, ssaBlock *to) {
-	for_array(i, b->succs) {
-		ssaBlock *succ = b->succs.e[i];
-		if (succ == from) {
-			b->succs.e[i] = to;
-		}
-	}
-}
-
-bool ssa_opt_block_has_phi(ssaBlock *b) {
-	return b->instrs.e[0]->Instr.kind == ssaInstr_Phi;
-}
-
-
-
-
-
-
-
-
-
-
-ssaValueArray ssa_get_block_phi_nodes(ssaBlock *b) {
-	ssaValueArray phis = {0};
-	for_array(i, b->instrs) {
-		ssaInstr *instr = &b->instrs.e[i]->Instr;
-		if (instr->kind != ssaInstr_Phi) {
-			phis = b->instrs;
-			phis.count = i;
-			return phis;
-		}
-	}
-	return phis;
-}
-
-void ssa_remove_pred(ssaBlock *b, ssaBlock *p) {
-	ssaValueArray phis = ssa_get_block_phi_nodes(b);
-	isize i = 0;
-	for_array(j, b->preds) {
-		ssaBlock *pred = b->preds.e[j];
-		if (pred != p) {
-			b->preds.e[i] = b->preds.e[j];
-			for_array(k, phis) {
-				ssaInstrPhi *phi = &phis.e[k]->Instr.Phi;
-				phi->edges.e[i] = phi->edges.e[j];
-			}
-			i++;
-		}
-	}
-	b->preds.count = i;
-	for_array(k, phis) {
-		ssaInstrPhi *phi = &phis.e[k]->Instr.Phi;
-		phi->edges.count = i;
-	}
-
-}
-
-void ssa_remove_dead_blocks(ssaProcedure *proc) {
-	isize j = 0;
-	for_array(i, proc->blocks) {
-		ssaBlock *b = proc->blocks.e[i];
-		if (b == NULL) {
-			continue;
-		}
-		// NOTE(bill): Swap order
-		b->index = j;
-		proc->blocks.e[j++] = b;
-	}
-	proc->blocks.count = j;
-}
-
-void ssa_mark_reachable(ssaBlock *b) {
-	isize const WHITE =  0;
-	isize const BLACK = -1;
-	b->index = BLACK;
-	for_array(i, b->succs) {
-		ssaBlock *succ = b->succs.e[i];
-		if (succ->index == WHITE) {
-			ssa_mark_reachable(succ);
-		}
-	}
-}
-
-void ssa_remove_unreachable_blocks(ssaProcedure *proc) {
-	isize const WHITE =  0;
-	isize const BLACK = -1;
-	for_array(i, proc->blocks) {
-		proc->blocks.e[i]->index = WHITE;
-	}
-
-	ssa_mark_reachable(proc->blocks.e[0]);
-
-	for_array(i, proc->blocks) {
-		ssaBlock *b = proc->blocks.e[i];
-		if (b->index == WHITE) {
-			for_array(j, b->succs) {
-				ssaBlock *c = b->succs.e[j];
-				if (c->index == BLACK) {
-					ssa_remove_pred(c, b);
-				}
-			}
-			// NOTE(bill): Mark as empty but don't actually free it
-			// As it's been allocated with an arena
-			proc->blocks.e[i] = NULL;
-		}
-	}
-	ssa_remove_dead_blocks(proc);
-}
-
-bool ssa_opt_block_fusion(ssaProcedure *proc, ssaBlock *a) {
-	if (a->succs.count != 1) {
-		return false;
-	}
-	ssaBlock *b = a->succs.e[0];
-	if (b->preds.count != 1) {
-		return false;
-	}
-
-	if (ssa_opt_block_has_phi(b)) {
-		return false;
-	}
-
-	array_pop(&a->instrs); // Remove branch at end
-	for_array(i, b->instrs) {
-		array_add(&a->instrs, b->instrs.e[i]);
-		ssa_set_instr_parent(b->instrs.e[i], a);
-	}
-
-	array_clear(&a->succs);
-	for_array(i, b->succs) {
-		array_add(&a->succs, b->succs.e[i]);
-	}
-
-	// Fix preds links
-	for_array(i, b->succs) {
-		ssa_opt_block_replace_pred(b->succs.e[i], b, a);
-	}
-
-	proc->blocks.e[b->index] = NULL;
-	return true;
-}
-
-void ssa_opt_blocks(ssaProcedure *proc) {
-	ssa_remove_unreachable_blocks(proc);
-
-#if 1
-	bool changed = true;
-	while (changed) {
-		changed = false;
-		for_array(i, proc->blocks) {
-			ssaBlock *b = proc->blocks.e[i];
-			if (b == NULL) {
-				continue;
-			}
-			GB_ASSERT(b->index == i);
-
-			if (ssa_opt_block_fusion(proc, b)) {
-				changed = true;
-			}
-			// TODO(bill): other simple block optimizations
-		}
-	}
-#endif
-
-	ssa_remove_dead_blocks(proc);
-}
-void ssa_opt_build_referrers(ssaProcedure *proc) {
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
-
-	ssaValueArray ops = {0}; // NOTE(bill): Act as a buffer
-	array_init_reserve(&ops, proc->module->tmp_allocator, 64); // HACK(bill): This _could_ overflow the temp arena
-	for_array(i, proc->blocks) {
-		ssaBlock *b = proc->blocks.e[i];
-		for_array(j, b->instrs) {
-			ssaValue *instr = b->instrs.e[j];
-			array_clear(&ops);
-			ssa_opt_add_operands(&ops, &instr->Instr);
-			for_array(k, ops) {
-				ssaValue *op = ops.e[k];
-				if (op == NULL) {
-					continue;
-				}
-				ssaValueArray *refs = ssa_value_referrers(op);
-				if (refs != NULL) {
-					array_add(refs, instr);
-				}
-			}
-		}
-	}
-
-	gb_temp_arena_memory_end(tmp);
-}
-
-
-
-
-
-
-
-// State of Lengauer-Tarjan algorithm
-// Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
-typedef struct ssaLTState {
-	isize count;
-	// NOTE(bill): These are arrays
-	ssaBlock **sdom;     // Semidominator
-	ssaBlock **parent;   // Parent in DFS traversal of CFG
-	ssaBlock **ancestor;
-} ssaLTState;
-
-// §2.2 - bottom of page
-void ssa_lt_link(ssaLTState *lt, ssaBlock *p, ssaBlock *q) {
-	lt->ancestor[q->index] = p;
-}
-
-i32 ssa_lt_depth_first_search(ssaLTState *lt, ssaBlock *p, i32 i, ssaBlock **preorder) {
-	preorder[i] = p;
-	p->dom.pre = i++;
-	lt->sdom[p->index] = p;
-	ssa_lt_link(lt, NULL, p);
-	for_array(index, p->succs) {
-		ssaBlock *q = p->succs.e[index];
-		if (lt->sdom[q->index] == NULL) {
-			lt->parent[q->index] = p;
-			i = ssa_lt_depth_first_search(lt, q, i, preorder);
-		}
-	}
-	return i;
-}
-
-ssaBlock *ssa_lt_eval(ssaLTState *lt, ssaBlock *v) {
-	ssaBlock *u = v;
-	for (;
-	     lt->ancestor[v->index] != NULL;
-	     v = lt->ancestor[v->index]) {
-		if (lt->sdom[v->index]->dom.pre < lt->sdom[u->index]->dom.pre) {
-			u = v;
-		}
-	}
-	return u;
-}
-
-typedef struct ssaDomPrePost {
-	i32 pre, post;
-} ssaDomPrePost;
-
-ssaDomPrePost ssa_opt_number_dom_tree(ssaBlock *v, i32 pre, i32 post) {
-	ssaDomPrePost result = {pre, post};
-
-	v->dom.pre = pre++;
-	for_array(i, v->dom.children) {
-		result = ssa_opt_number_dom_tree(v->dom.children.e[i], result.pre, result.post);
-	}
-	v->dom.post = post++;
-
-	result.pre  = pre;
-	result.post = post;
-	return result;
-}
-
-
-// NOTE(bill): Requires `ssa_opt_blocks` to be called before this
-void ssa_opt_build_dom_tree(ssaProcedure *proc) {
-	// Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
-
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
-
-	isize n = proc->blocks.count;
-	ssaBlock **buf = gb_alloc_array(proc->module->tmp_allocator, ssaBlock *, 5*n);
-
-	ssaLTState lt = {0};
-	lt.count    = n;
-	lt.sdom     = &buf[0*n];
-	lt.parent   = &buf[1*n];
-	lt.ancestor = &buf[2*n];
-
-	ssaBlock **preorder = &buf[3*n];
-	ssaBlock **buckets  = &buf[4*n];
-	ssaBlock *root = proc->blocks.e[0];
-
-	// Step 1 - number vertices
-	i32 pre_num = ssa_lt_depth_first_search(&lt, root, 0, preorder);
-	gb_memmove(buckets, preorder, n*gb_size_of(preorder[0]));
-
-	for (i32 i = n-1; i > 0; i--) {
-		ssaBlock *w = preorder[i];
-
-		// Step 3 - Implicitly define idom for nodes
-		for (ssaBlock *v = buckets[i]; v != w; v = buckets[v->dom.pre]) {
-			ssaBlock *u = ssa_lt_eval(&lt, v);
-			if (lt.sdom[u->index]->dom.pre < i) {
-				v->dom.idom = u;
-			} else {
-				v->dom.idom = w;
-			}
-		}
-
-		// Step 2 - Compute all sdoms
-		lt.sdom[w->index] = lt.parent[w->index];
-		for_array(pred_index, w->preds) {
-			ssaBlock *v = w->preds.e[pred_index];
-			ssaBlock *u = ssa_lt_eval(&lt, v);
-			if (lt.sdom[u->index]->dom.pre < lt.sdom[w->index]->dom.pre) {
-				lt.sdom[w->index] = lt.sdom[u->index];
-			}
-		}
-
-		ssa_lt_link(&lt, lt.parent[w->index], w);
-
-		if (lt.parent[w->index] == lt.sdom[w->index]) {
-			w->dom.idom = lt.parent[w->index];
-		} else {
-			buckets[i] = buckets[lt.sdom[w->index]->dom.pre];
-			buckets[lt.sdom[w->index]->dom.pre] = w;
-		}
-	}
-
-	// The rest of Step 3
-	for (ssaBlock *v = buckets[0]; v != root; v = buckets[v->dom.pre]) {
-		v->dom.idom = root;
-	}
-
-	// Step 4 - Explicitly define idom for nodes (in preorder)
-	for (isize i = 1; i < n; i++) {
-		ssaBlock *w = preorder[i];
-		if (w == root) {
-			w->dom.idom = NULL;
-		} else {
-			// Weird tree relationships here!
-
-			if (w->dom.idom != lt.sdom[w->index]) {
-				w->dom.idom = w->dom.idom->dom.idom;
-			}
-
-			// Calculate children relation as inverse of idom
-			if (w->dom.idom->dom.children.e == NULL) {
-				// TODO(bill): Is this good enough for memory allocations?
-				array_init(&w->dom.idom->dom.children, heap_allocator());
-			}
-			array_add(&w->dom.idom->dom.children, w);
-		}
-	}
-
-	ssa_opt_number_dom_tree(root, 0, 0);
-
-	gb_temp_arena_memory_end(tmp);
-}
-
-void ssa_opt_mem2reg(ssaProcedure *proc) {
-	// TODO(bill): ssa_opt_mem2reg
-}
-
-
-
-void ssa_opt_tree(ssaGen *s) {
-	s->opt_called = true;
-
-	for_array(member_index, s->module.procs) {
-		ssaProcedure *proc = s->module.procs.e[member_index];
-		if (proc->blocks.count == 0) { // Prototype/external procedure
-			continue;
-		}
-
-		ssa_opt_blocks(proc);
-	#if 1
-		ssa_opt_build_referrers(proc);
-		ssa_opt_build_dom_tree(proc);
-
-		// TODO(bill): ssa optimization
-		// [ ] cse (common-subexpression) elim
-		// [ ] copy elim
-		// [ ] dead code elim
-		// [ ] dead store/load elim
-		// [ ] phi elim
-		// [ ] short circuit elim
-		// [ ] bounds check elim
-		// [ ] lift/mem2reg
-		// [ ] lift/mem2reg
-
-		ssa_opt_mem2reg(proc);
-	#endif
-
-		GB_ASSERT(proc->blocks.count > 0);
-		ssa_number_proc_registers(proc);
-	}
-}

+ 0 - 1439
src/ssa_print.cpp

@@ -1,1439 +0,0 @@
-typedef struct ssaFileBuffer {
-	gbVirtualMemory vm;
-	isize           offset;
-	gbFile *        output;
-} ssaFileBuffer;
-
-void ssa_file_buffer_init(ssaFileBuffer *f, gbFile *output) {
-	isize size = 8*gb_virtual_memory_page_size(NULL);
-	f->vm = gb_vm_alloc(NULL, size);
-	f->offset = 0;
-	f->output = output;
-}
-
-void ssa_file_buffer_destroy(ssaFileBuffer *f) {
-	if (f->offset > 0) {
-		// NOTE(bill): finish writing buffered data
-		gb_file_write(f->output, f->vm.data, f->offset);
-	}
-
-	gb_vm_free(f->vm);
-}
-
-void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) {
-	if (len > f->vm.size) {
-		gb_file_write(f->output, data, len);
-		return;
-	}
-
-	if ((f->vm.size - f->offset) < len) {
-		gb_file_write(f->output, f->vm.data, f->offset);
-		f->offset = 0;
-	}
-	u8 *cursor = cast(u8 *)f->vm.data + f->offset;
-	gb_memmove(cursor, data, len);
-	f->offset += len;
-}
-
-
-void ssa_fprintf(ssaFileBuffer *f, char *fmt, ...) {
-	va_list va;
-	va_start(va, fmt);
-	char buf[4096] = {0};
-	isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
-	ssa_file_buffer_write(f, buf, len-1);
-	va_end(va);
-}
-
-
-void ssa_file_write(ssaFileBuffer *f, void *data, isize len) {
-	ssa_file_buffer_write(f, data, len);
-}
-
-
-bool ssa_valid_char(u8 c) {
-	if (c >= 0x80) {
-		return false;
-	}
-
-	if (gb_char_is_alphanumeric(c)) {
-		return true;
-	}
-
-	switch (c) {
-	case '$':
-	case '-':
-	case '.':
-	case '_':
-		return true;
-	}
-
-	return false;
-}
-
-void ssa_print_escape_string(ssaFileBuffer *f, String name, bool print_quotes) {
-	isize extra = 0;
-	for (isize i = 0; i < name.len; i++) {
-		u8 c = name.text[i];
-		if (!ssa_valid_char(c)) {
-			extra += 2;
-		}
-	}
-
-	if (extra == 0) {
-		ssa_fprintf(f, "%.*s", LIT(name));
-		return;
-	}
-
-
-	char hex_table[] = "0123456789ABCDEF";
-	isize buf_len = name.len + extra + 2;
-
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
-
-	u8 *buf = gb_alloc_array(string_buffer_allocator, u8, buf_len);
-
-	isize j = 0;
-
-	if (print_quotes) {
-		buf[j++] = '"';
-	}
-
-	for (isize i = 0; i < name.len; i++) {
-		u8 c = name.text[i];
-		if (ssa_valid_char(c)) {
-			buf[j++] = c;
-		} else {
-			buf[j] = '\\';
-			buf[j+1] = hex_table[c >> 4];
-			buf[j+2] = hex_table[c & 0x0f];
-			j += 3;
-		}
-	}
-
-	if (print_quotes) {
-		buf[j++] = '"';
-	}
-
-	ssa_file_write(f, buf, j);
-
-	gb_temp_arena_memory_end(tmp);
-}
-
-
-
-void ssa_print_encoded_local(ssaFileBuffer *f, String name) {
-	ssa_fprintf(f, "%%");
-	ssa_print_escape_string(f, name, true);
-}
-
-void ssa_print_encoded_global(ssaFileBuffer *f, String name, bool global_scope) {
-	ssa_fprintf(f, "@");
-	if (!global_scope && str_ne(name, str_lit("main"))) {
-		ssa_fprintf(f, ".");
-	}
-	ssa_print_escape_string(f, name, true);
-}
-
-
-void ssa_print_type(ssaFileBuffer *f, ssaModule *m, Type *t) {
-	BaseTypeSizes s = m->sizes;
-	i64 word_bits = 8*s.word_size;
-	GB_ASSERT_NOT_NULL(t);
-	t = default_type(t);
-
-	switch (t->kind) {
-	case Type_Basic:
-		switch (t->Basic.kind) {
-		case Basic_bool:   ssa_fprintf(f, "i1");                      break;
-		case Basic_i8:     ssa_fprintf(f, "i8");                      break;
-		case Basic_u8:     ssa_fprintf(f, "i8");                      break;
-		case Basic_i16:    ssa_fprintf(f, "i16");                     break;
-		case Basic_u16:    ssa_fprintf(f, "i16");                     break;
-		case Basic_i32:    ssa_fprintf(f, "i32");                     break;
-		case Basic_u32:    ssa_fprintf(f, "i32");                     break;
-		case Basic_i64:    ssa_fprintf(f, "i64");                     break;
-		case Basic_u64:    ssa_fprintf(f, "i64");                     break;
-		case Basic_i128:   ssa_fprintf(f, "i128");                    break;
-		case Basic_u128:   ssa_fprintf(f, "i128");                    break;
-		// case Basic_f16:    ssa_fprintf(f, "half");                    break;
-		case Basic_f32:    ssa_fprintf(f, "float");                   break;
-		case Basic_f64:    ssa_fprintf(f, "double");                  break;
-		// case Basic_f128:   ssa_fprintf(f, "fp128");                   break;
-		case Basic_rawptr: ssa_fprintf(f, "%%..rawptr");              break;
-		case Basic_string: ssa_fprintf(f, "%%..string");              break;
-		case Basic_uint:   ssa_fprintf(f, "i%lld", word_bits);        break;
-		case Basic_int:    ssa_fprintf(f, "i%lld", word_bits);        break;
-		case Basic_any:    ssa_fprintf(f, "%%..any");                 break;
-		}
-		break;
-	case Type_Pointer:
-		ssa_print_type(f, m, t->Pointer.elem);
-		ssa_fprintf(f, "*");
-		break;
-	case Type_Maybe:
-		ssa_fprintf(f, "{");
-		ssa_print_type(f, m, t->Maybe.elem);
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, t_bool);
-		ssa_fprintf(f, "}");
-		break;
-	case Type_Array:
-		ssa_fprintf(f, "[%lld x ", t->Array.count);
-		ssa_print_type(f, m, t->Array.elem);
-		ssa_fprintf(f, "]");
-		break;
-	case Type_Vector:
-		ssa_fprintf(f, "<%lld x ", t->Vector.count);
-		ssa_print_type(f, m, t->Vector.elem);
-		ssa_fprintf(f, ">");
-		break;
-	case Type_Slice:
-		ssa_fprintf(f, "{");
-		ssa_print_type(f, m, t->Slice.elem);
-		ssa_fprintf(f, "*, i%lld, i%lld}", word_bits, word_bits);
-		break;
-	case Type_Record: {
-		switch (t->Record.kind) {
-		case TypeRecord_Struct:
-			if (t->Record.struct_is_packed) {
-				ssa_fprintf(f, "<");
-			}
-			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 = base_type(ft);
-				if (!is_type_struct(bft)) {
-					ft = bft;
-				}
-				ssa_print_type(f, m, ft);
-			}
-			ssa_fprintf(f, "}");
-			if (t->Record.struct_is_packed) {
-				ssa_fprintf(f, ">");
-			}
-			break;
-		case TypeRecord_Union: {
-			// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
-			// LLVM takes the first element's alignment as the entire alignment (like C)
-			i64 size_of_union  = type_size_of(s, heap_allocator(), t) - s.word_size;
-			i64 align_of_union = type_align_of(s, heap_allocator(), t);
-			ssa_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8], i%lld}", align_of_union, size_of_union, word_bits);
-		} break;
-		case TypeRecord_RawUnion: {
-			// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
-			// LLVM takes the first element's alignment as the entire alignment (like C)
-			i64 size_of_union  = type_size_of(s, heap_allocator(), t);
-			i64 align_of_union = type_align_of(s, heap_allocator(), t);
-			ssa_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align_of_union, size_of_union);
-		} break;
-		case TypeRecord_Enum:
-			ssa_print_type(f, m, t->Record.enum_base);
-			break;
-		}
-	} break;
-
-
-	case Type_Named:
-		if (is_type_struct(t) || is_type_union(t)) {
-			String *name = map_string_get(&m->type_names, hash_pointer(t));
-			GB_ASSERT_MSG(name != NULL, "%.*s", LIT(t->Named.name));
-			ssa_print_encoded_local(f, *name);
-			// ssa_print_encoded_local(f, t->Named.name);
-		} else {
-			ssa_print_type(f, m, base_type(t));
-		}
-		break;
-	case Type_Tuple:
-		if (t->Tuple.variable_count == 1) {
-			ssa_print_type(f, m, t->Tuple.variables[0]->type);
-		} else {
-			ssa_fprintf(f, "{");
-			for (isize i = 0; i < t->Tuple.variable_count; i++) {
-				if (i > 0) {
-					ssa_fprintf(f, ", ");
-				}
-				ssa_print_type(f, m, t->Tuple.variables[i]->type);
-			}
-			ssa_fprintf(f, "}");
-		}
-		break;
-	case Type_Proc: {
-		if (t->Proc.result_count == 0) {
-			ssa_fprintf(f, "void");
-		} else {
-			ssa_print_type(f, m, t->Proc.results);
-		}
-		ssa_fprintf(f, " (");
-		TypeTuple *params = &t->Proc.params->Tuple;
-		for (isize i = 0; i < t->Proc.param_count; i++) {
-			if (i > 0) {
-				ssa_fprintf(f, ", ");
-			}
-			ssa_print_type(f, m, params->variables[i]->type);
-		}
-		ssa_fprintf(f, ")*");
-	} break;
-	}
-}
-
-void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Type *type);
-
-void ssa_print_compound_element(ssaFileBuffer *f, ssaModule *m, ExactValue v, Type *elem_type) {
-	ssa_print_type(f, m, elem_type);
-	ssa_fprintf(f, " ");
-
-	if (v.kind != ExactValue_Invalid && is_type_maybe(elem_type)) {
-		Type *t = base_type(elem_type)->Maybe.elem;
-		ssa_fprintf(f, "{");
-		ssa_print_type(f, m, t);
-		ssa_fprintf(f, " ");
-	}
-
-	if (v.kind == ExactValue_Invalid || base_type(elem_type) == t_any) {
-		ssa_fprintf(f, "zeroinitializer");
-	} else {
-		ssa_print_exact_value(f, m, v, elem_type);
-	}
-
-	if (v.kind != ExactValue_Invalid && is_type_maybe(elem_type)) {
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, t_bool);
-		ssa_fprintf(f, " ");
-		ssa_fprintf(f, "true}");
-	}
-}
-
-void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Type *type) {
-	type = base_type(type);
-	if (is_type_float(type)) {
-		value = exact_value_to_float(value);
-	} else if (is_type_integer(type)) {
-		value = exact_value_to_integer(value);
-	} else if (is_type_pointer(type)) {
-		value = exact_value_to_integer(value);
-	}
-
-	switch (value.kind) {
-	case ExactValue_Bool:
-		ssa_fprintf(f, "%s", (value.value_bool ? "true" : "false"));
-		break;
-	case ExactValue_String: {
-		String str = value.value_string;
-		if (str.len == 0) {
-			ssa_fprintf(f, "zeroinitializer");
-			break;
-		}
-		if (!is_type_string(type)) {
-			GB_ASSERT(is_type_array(type));
-			ssa_fprintf(f, "c\"");
-			ssa_print_escape_string(f, str, false);
-			ssa_fprintf(f, "\"");
-		} else {
-			// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
-			// of the .ll file
-			ssaValue *str_array = ssa_add_global_string_array(m, str);
-
-			ssa_fprintf(f, "{i8* getelementptr inbounds (");
-			ssa_print_type(f, m, str_array->Global.entity->type);
-			ssa_fprintf(f, ", ");
-			ssa_print_type(f, m, str_array->Global.entity->type);
-			ssa_fprintf(f, "* ");
-			ssa_print_encoded_global(f, str_array->Global.entity->token.string, false);
-			ssa_fprintf(f, ", ");
-			ssa_print_type(f, m, t_int);
-			ssa_fprintf(f, " 0, i32 0), ");
-			ssa_print_type(f, m, t_int);
-			ssa_fprintf(f, " %lld}", cast(i64)str.len);
-		}
-	} break;
-	case ExactValue_Integer: {
-		if (is_type_pointer(type)) {
-			if (value.value_integer == 0) {
-				ssa_fprintf(f, "null");
-			} else {
-				ssa_fprintf(f, "inttoptr (");
-				ssa_print_type(f, m, t_int);
-				ssa_fprintf(f, " %llu to ", value.value_integer);
-				ssa_print_type(f, m, t_rawptr);
-				ssa_fprintf(f, ")");
-			}
-		} else {
-			ssa_fprintf(f, "%lld", value.value_integer);
-		}
-	} break;
-	case ExactValue_Float: {
-		GB_ASSERT(is_type_float(type));
-		type = base_type(type);
-		u64 u = *cast(u64*)&value.value_float;
-		switch (type->Basic.kind) {
-		case Basic_f32:
-			// IMPORTANT NOTE(bill): LLVM requires all floating point constants to be
-			// a 64 bit number if bits_of(float type) <= 64.
-			// https://groups.google.com/forum/#!topic/llvm-dev/IlqV3TbSk6M
-			// 64 bit mantissa: 52 bits
-			// 32 bit mantissa: 23 bits
-			// 29 == 52-23
-			u >>= 29;
-			u <<= 29;
-			break;
-		}
-
-		switch (type->Basic.kind) {
-		case 0: break;
-#if 0
-		case Basic_f16:
-			ssa_fprintf(f, "bitcast (");
-			ssa_print_type(f, m, t_u16);
-			ssa_fprintf(f, " %u to ", cast(u16)f32_to_f16(cast(f32)value.value_float));
-			ssa_print_type(f, m, t_f16);
-			ssa_fprintf(f, ")");
-			break;
-		case Basic_f128:
-			ssa_fprintf(f, "bitcast (");
-			ssa_fprintf(f, "i128");
-			// TODO(bill): Actually support f128
-			ssa_fprintf(f, " %llu to ", u);
-			ssa_print_type(f, m, t_f128);
-			ssa_fprintf(f, ")");
-			break;
-#endif
-		default:
-			ssa_fprintf(f, "0x%016llx", u);
-			break;
-		}
-	} break;
-	case ExactValue_Pointer:
-		if (value.value_pointer == 0) {
-			ssa_fprintf(f, "null");
-		} else {
-			ssa_fprintf(f, "inttoptr (");
-			ssa_print_type(f, m, t_int);
-			ssa_fprintf(f, " %llu to ", cast(u64)cast(uintptr)value.value_pointer);
-			ssa_print_type(f, m, t_rawptr);
-			ssa_fprintf(f, ")");
-		}
-		break;
-
-	case ExactValue_Compound: {
-		type = base_type(type);
-		if (is_type_array(type)) {
-			ast_node(cl, CompoundLit, value.value_compound);
-			isize elem_count = cl->elems.count;
-			if (elem_count == 0) {
-				ssa_fprintf(f, "zeroinitializer");
-				break;
-			}
-
-			ssa_fprintf(f, "[");
-			Type *elem_type = type->Array.elem;
-
-			for (isize i = 0; i < elem_count; i++) {
-				if (i > 0) {
-					ssa_fprintf(f, ", ");
-				}
-				TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]);
-				GB_ASSERT(tav != NULL);
-				ssa_print_compound_element(f, m, tav->value, elem_type);
-			}
-			for (isize i = elem_count; i < type->Array.count; i++) {
-				if (i >= elem_count) {
-					ssa_fprintf(f, ", ");
-				}
-				ssa_print_type(f, m, elem_type);
-				ssa_fprintf(f, " zeroinitializer");
-			}
-
-			ssa_fprintf(f, "]");
-		} else if (is_type_vector(type)) {
-			ast_node(cl, CompoundLit, value.value_compound);
-			isize elem_count = cl->elems.count;
-			if (elem_count == 0) {
-				ssa_fprintf(f, "zeroinitializer");
-				break;
-			}
-
-			ssa_fprintf(f, "<");
-			Type *elem_type = type->Vector.elem;
-
-			if (elem_count == 1 && type->Vector.count > 1) {
-				TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[0]);
-				GB_ASSERT(tav != NULL);
-
-				for (isize i = 0; i < type->Vector.count; i++) {
-					if (i > 0) {
-						ssa_fprintf(f, ", ");
-					}
-					ssa_print_compound_element(f, m, tav->value, elem_type);
-				}
-			} else {
-				for (isize i = 0; i < elem_count; i++) {
-					if (i > 0) {
-						ssa_fprintf(f, ", ");
-					}
-					TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]);
-					GB_ASSERT(tav != NULL);
-					ssa_print_compound_element(f, m, tav->value, elem_type);
-				}
-			}
-
-			ssa_fprintf(f, ">");
-		} else if (is_type_struct(type)) {
-			gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
-
-			ast_node(cl, CompoundLit, value.value_compound);
-
-			if (cl->elems.count == 0) {
-				ssa_fprintf(f, "zeroinitializer");
-				break;
-			}
-
-
-			isize value_count = type->Record.field_count;
-			ExactValue *values = gb_alloc_array(m->tmp_allocator, ExactValue, value_count);
-
-
-			if (cl->elems.e[0]->kind == AstNode_FieldValue) {
-				isize elem_count = cl->elems.count;
-				for (isize i = 0; i < elem_count; i++) {
-					ast_node(fv, FieldValue, cl->elems.e[i]);
-					String name = fv->field->Ident.string;
-
-					TypeAndValue *tav = type_and_value_of_expression(m->info, fv->value);
-					GB_ASSERT(tav != NULL);
-
-					Selection sel = lookup_field(m->allocator, type, name, false);
-					Entity *f = type->Record.fields[sel.index.e[0]];
-
-					values[f->Variable.field_index] = tav->value;
-				}
-			} else {
-				for (isize i = 0; i < value_count; i++) {
-					TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]);
-					GB_ASSERT(tav != NULL);
-
-					Entity *f = type->Record.fields_in_src_order[i];
-
-					values[f->Variable.field_index] = tav->value;
-				}
-			}
-
-
-
-			if (type->Record.struct_is_packed) {
-				ssa_fprintf(f, "<");
-			}
-			ssa_fprintf(f, "{");
-
-
-			for (isize i = 0; i < value_count; i++) {
-				if (i > 0) {
-					ssa_fprintf(f, ", ");
-				}
-				Type *elem_type = type->Record.fields[i]->type;
-
-				ssa_print_compound_element(f, m, values[i], elem_type);
-			}
-
-
-			ssa_fprintf(f, "}");
-			if (type->Record.struct_is_packed) {
-				ssa_fprintf(f, ">");
-			}
-
-			gb_temp_arena_memory_end(tmp);
-		} else {
-			ssa_fprintf(f, "zeroinitializer");
-		}
-
-	} break;
-
-	default:
-		ssa_fprintf(f, "zeroinitializer");
-		// GB_PANIC("Invalid ExactValue: %d", value.kind);
-		break;
-	}
-}
-
-void ssa_print_block_name(ssaFileBuffer *f, ssaBlock *b) {
-	if (b != NULL) {
-		ssa_print_escape_string(f, b->label, false);
-		ssa_fprintf(f, "-%td", b->index);
-	} else {
-		ssa_fprintf(f, "<INVALID-BLOCK>");
-	}
-}
-
-void ssa_print_value(ssaFileBuffer *f, ssaModule *m, ssaValue *value, Type *type_hint) {
-	if (value == NULL) {
-		ssa_fprintf(f, "!!!NULL_VALUE");
-		return;
-	}
-	switch (value->kind) {
-	default: GB_PANIC("Unknown ssaValue kind"); break;
-
-	case ssaValue_Constant:
-		ssa_print_exact_value(f, m, value->Constant.value, type_hint);
-		break;
-
-	case ssaValue_ConstantSlice: {
-		ssaValueConstantSlice *cs = &value->ConstantSlice;
-		if (cs->backing_array == NULL || cs->count == 0) {
-			ssa_fprintf(f, "zeroinitializer");
-		} else {
-			Type *at = base_type(type_deref(ssa_type(cs->backing_array)));
-			Type *et = at->Array.elem;
-			ssa_fprintf(f, "{");
-			ssa_print_type(f, m, et);
-			ssa_fprintf(f, "* getelementptr inbounds (");
-			ssa_print_type(f, m, at);
-			ssa_fprintf(f, ", ");
-			ssa_print_type(f, m, at);
-			ssa_fprintf(f, "* ");
-			ssa_print_value(f, m, cs->backing_array, at);
-			ssa_fprintf(f, ", ");
-			ssa_print_type(f, m, t_int);
-			ssa_fprintf(f, " 0, i32 0), ");
-			ssa_print_type(f, m, t_int);
-			ssa_fprintf(f, " %lld, ", cs->count);
-			ssa_print_type(f, m, t_int);
-			ssa_fprintf(f, " %lld}", cs->count);
-		}
-	} break;
-
-	case ssaValue_Nil:
-		ssa_fprintf(f, "zeroinitializer");
-		break;
-
-	case ssaValue_TypeName:
-		ssa_print_encoded_local(f, value->TypeName.name);
-		break;
-	case ssaValue_Global: {
-		Scope *scope = value->Global.entity->scope;
-		bool in_global_scope = false;
-		if (scope != NULL) {
-			in_global_scope = scope->is_global || scope->is_init;
-		}
-		ssa_print_encoded_global(f, value->Global.entity->token.string, in_global_scope);
-	} break;
-	case ssaValue_Param:
-		ssa_print_encoded_local(f, value->Param.entity->token.string);
-		break;
-	case ssaValue_Proc:
-		ssa_print_encoded_global(f, value->Proc.name, (value->Proc.tags & (ProcTag_foreign|ProcTag_link_name)) != 0);
-		break;
-	case ssaValue_Instr:
-		ssa_fprintf(f, "%%%d", value->index);
-		break;
-	}
-}
-
-void ssa_print_instr(ssaFileBuffer *f, ssaModule *m, ssaValue *value) {
-	GB_ASSERT(value->kind == ssaValue_Instr);
-	ssaInstr *instr = &value->Instr;
-
-	ssa_fprintf(f, "\t");
-
-	switch (instr->kind) {
-	case ssaInstr_StartupRuntime: {
-		ssa_fprintf(f, "call void ");
-		ssa_print_encoded_global(f, str_lit(SSA_STARTUP_RUNTIME_PROC_NAME), false);
-		ssa_fprintf(f, "()\n");
-	} break;
-
-	case ssaInstr_Comment:
-		ssa_fprintf(f, "; %.*s\n", LIT(instr->Comment.text));
-		break;
-
-	case ssaInstr_Local: {
-		Type *type = instr->Local.entity->type;
-		ssa_fprintf(f, "%%%d = alloca ", value->index);
-		ssa_print_type(f, m, type);
-		ssa_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type));
-	} break;
-
-	case ssaInstr_ZeroInit: {
-		Type *type = type_deref(ssa_type(instr->ZeroInit.address));
-		ssa_fprintf(f, "store ");
-		ssa_print_type(f, m, type);
-		ssa_fprintf(f, " zeroinitializer, ");
-		ssa_print_type(f, m, type);
-		ssa_fprintf(f, "* %%%d\n", instr->ZeroInit.address->index);
-	} break;
-
-	case ssaInstr_Store: {
-		Type *type = ssa_type(instr->Store.value);
-		ssa_fprintf(f, "store ");
-		ssa_print_type(f, m, type);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->Store.value, type);
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, type);
-		ssa_fprintf(f, "* ");
-		ssa_print_value(f, m, instr->Store.address, type);
-		ssa_fprintf(f, "\n");
-	} break;
-
-	case ssaInstr_Load: {
-		Type *type = instr->Load.type;
-		ssa_fprintf(f, "%%%d = load ", value->index);
-		ssa_print_type(f, m, type);
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, type);
-		ssa_fprintf(f, "* ");
-		ssa_print_value(f, m, instr->Load.address, type);
-		ssa_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type));
-	} break;
-
-	case ssaInstr_ArrayElementPtr: {
-		Type *et = ssa_type(instr->ArrayElementPtr.address);
-		ssa_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
-
-		ssa_print_type(f, m, type_deref(et));
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, et);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->ArrayElementPtr.address, et);
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, t_int);
-		ssa_fprintf(f, " 0, ");
-
-		ssaValue *index =instr->ArrayElementPtr.elem_index;
-		Type *t = ssa_type(index);
-		ssa_print_type(f, m, t);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, index, t);
-		ssa_fprintf(f, "\n");
-	} break;
-
-	case ssaInstr_StructElementPtr: {
-		Type *et = ssa_type(instr->StructElementPtr.address);
-		ssa_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
-
-		ssa_print_type(f, m, type_deref(et));
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, et);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->StructElementPtr.address, et);
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, t_int);
-		ssa_fprintf(f, " 0, ");
-		ssa_print_type(f, m, t_i32);
-		ssa_fprintf(f, " %d", instr->StructElementPtr.elem_index);
-		ssa_fprintf(f, "\n");
-	} break;
-
-	case ssaInstr_PtrOffset: {
-		Type *pt = ssa_type(instr->PtrOffset.address);
-		ssa_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
-		ssa_print_type(f, m, type_deref(pt));
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, pt);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->PtrOffset.address, pt);
-
-		ssaValue *offset = instr->PtrOffset.offset;
-		Type *t = ssa_type(offset);
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, t);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, offset, t);
-		ssa_fprintf(f, "\n");
-	} break;
-
-	case ssaInstr_Phi: {
-		ssa_fprintf(f, "%%%d = phi ", value->index);
-		ssa_print_type(f, m, instr->Phi.type);
-		ssa_fprintf(f, " ", value->index);
-
-		for (isize i = 0; i < instr->Phi.edges.count; i++) {
-			if (i > 0) {
-				ssa_fprintf(f, ", ");
-			}
-
-			ssaValue *edge = instr->Phi.edges.e[i];
-			ssaBlock *block = NULL;
-			if (instr->parent != NULL &&
-			    i < instr->parent->preds.count) {
-				block = instr->parent->preds.e[i];
-			}
-
-			ssa_fprintf(f, "[ ");
-			ssa_print_value(f, m, edge, instr->Phi.type);
-			ssa_fprintf(f, ", %%");
-			ssa_print_block_name(f, block);
-			ssa_fprintf(f, " ]");
-		}
-		ssa_fprintf(f, "\n");
-	} break;
-
-	case ssaInstr_ArrayExtractValue: {
-		Type *et = ssa_type(instr->ArrayExtractValue.address);
-		ssa_fprintf(f, "%%%d = extractvalue ", value->index);
-
-		ssa_print_type(f, m, et);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->ArrayExtractValue.address, et);
-		ssa_fprintf(f, ", %d\n", instr->ArrayExtractValue.index);
-	} break;
-
-	case ssaInstr_StructExtractValue: {
-		Type *et = ssa_type(instr->StructExtractValue.address);
-		ssa_fprintf(f, "%%%d = extractvalue ", value->index);
-
-		ssa_print_type(f, m, et);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->StructExtractValue.address, et);
-		ssa_fprintf(f, ", %d\n", instr->StructExtractValue.index);
-	} break;
-
-	case ssaInstr_UnionTagPtr: {
-		Type *et = ssa_type(instr->UnionTagPtr.address);
-		ssa_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
-
-		ssa_print_type(f, m, type_deref(et));
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, et);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->UnionTagPtr.address, et);
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, t_int);
-		ssa_fprintf(f, " 0, ");
-		ssa_print_type(f, m, t_i32);
-		ssa_fprintf(f, " %d", 2);
-		ssa_fprintf(f, "\n");
-	} break;
-
-	case ssaInstr_UnionTagValue: {
-		Type *et = ssa_type(instr->UnionTagValue.address);
-		ssa_fprintf(f, "%%%d = extractvalue ", value->index);
-
-		ssa_print_type(f, m, et);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->UnionTagValue.address, et);
-		ssa_fprintf(f, ", %d\n", 2);
-	} break;
-
-	case ssaInstr_Jump: {;
-		ssa_fprintf(f, "br label %%");
-		ssa_print_block_name(f, instr->Jump.block);
-		ssa_fprintf(f, "\n");
-	} break;
-
-	case ssaInstr_If: {;
-		ssa_fprintf(f, "br ");
-		ssa_print_type(f, m, t_bool);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->If.cond, t_bool);
-		ssa_fprintf(f, ", ", instr->If.cond->index);
-		ssa_fprintf(f, "label %%");   ssa_print_block_name(f, instr->If.true_block);
-		ssa_fprintf(f, ", label %%"); ssa_print_block_name(f, instr->If.false_block);
-		ssa_fprintf(f, "\n");
-	} break;
-
-	case ssaInstr_Return: {
-		ssaInstrReturn *ret = &instr->Return;
-		ssa_fprintf(f, "ret ");
-		if (ret->value == NULL) {
-			ssa_fprintf(f, "void");
-		} else {
-			Type *t = ssa_type(ret->value);
-			ssa_print_type(f, m, t);
-			ssa_fprintf(f, " ");
-			ssa_print_value(f, m, ret->value, t);
-		}
-
-		ssa_fprintf(f, "\n");
-
-	} break;
-
-	case ssaInstr_Conv: {
-		ssaInstrConv *c = &instr->Conv;
-		ssa_fprintf(f, "%%%d = %.*s ", value->index, LIT(ssa_conv_strings[c->kind]));
-		ssa_print_type(f, m, c->from);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, c->value, c->from);
-		ssa_fprintf(f, " to ");
-		ssa_print_type(f, m, c->to);
-		ssa_fprintf(f, "\n");
-
-	} break;
-
-	case ssaInstr_Unreachable: {
-		ssa_fprintf(f, "unreachable\n");
-	} break;
-
-	case ssaInstr_BinaryOp: {
-		ssaInstrBinaryOp *bo = &value->Instr.BinaryOp;
-		Type *type = base_type(ssa_type(bo->left));
-		Type *elem_type = type;
-		while (elem_type->kind == Type_Vector) {
-			elem_type = base_type(elem_type->Vector.elem);
-		}
-
-		ssa_fprintf(f, "%%%d = ", value->index);
-
-		if (gb_is_between(bo->op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) {
-			if (is_type_string(elem_type)) {
-				ssa_fprintf(f, "call ");
-				ssa_print_type(f, m, t_bool);
-				char *runtime_proc = "";
-				switch (bo->op) {
-				case Token_CmpEq: runtime_proc = "__string_eq"; break;
-				case Token_NotEq: runtime_proc = "__string_ne"; break;
-				case Token_Lt:    runtime_proc = "__string_lt"; break;
-				case Token_Gt:    runtime_proc = "__string_gt"; break;
-				case Token_LtEq:  runtime_proc = "__string_le"; break;
-				case Token_GtEq:  runtime_proc = "__string_gt"; break;
-				}
-
-				ssa_fprintf(f, " ");
-				ssa_print_encoded_global(f, make_string_c(runtime_proc), false);
-				ssa_fprintf(f, "(");
-				ssa_print_type(f, m, type);
-				ssa_fprintf(f, " ");
-				ssa_print_value(f, m, bo->left, type);
-				ssa_fprintf(f, ", ");
-				ssa_print_type(f, m, type);
-				ssa_fprintf(f, " ");
-				ssa_print_value(f, m, bo->right, type);
-				ssa_fprintf(f, ")\n");
-				return;
-
-			} else if (is_type_float(elem_type)) {
-				ssa_fprintf(f, "fcmp ");
-				switch (bo->op) {
-				case Token_CmpEq: ssa_fprintf(f, "oeq"); break;
-				case Token_NotEq: ssa_fprintf(f, "one"); break;
-				case Token_Lt:    ssa_fprintf(f, "olt"); break;
-				case Token_Gt:    ssa_fprintf(f, "ogt"); break;
-				case Token_LtEq:  ssa_fprintf(f, "ole"); break;
-				case Token_GtEq:  ssa_fprintf(f, "oge"); break;
-				}
-			} else {
-				ssa_fprintf(f, "icmp ");
-				if (bo->op != Token_CmpEq &&
-				    bo->op != Token_NotEq) {
-					if (is_type_unsigned(elem_type)) {
-						ssa_fprintf(f, "u");
-					} else {
-						ssa_fprintf(f, "s");
-					}
-				}
-				switch (bo->op) {
-				case Token_CmpEq: ssa_fprintf(f, "eq"); break;
-				case Token_NotEq: ssa_fprintf(f, "ne"); break;
-				case Token_Lt:    ssa_fprintf(f, "lt"); break;
-				case Token_Gt:    ssa_fprintf(f, "gt"); break;
-				case Token_LtEq:  ssa_fprintf(f, "le"); break;
-				case Token_GtEq:  ssa_fprintf(f, "ge"); break;
-				}
-			}
-		} else {
-			if (is_type_float(elem_type)) {
-				ssa_fprintf(f, "f");
-			}
-
-			switch (bo->op) {
-			case Token_Add:    ssa_fprintf(f, "add");  break;
-			case Token_Sub:    ssa_fprintf(f, "sub");  break;
-			case Token_And:    ssa_fprintf(f, "and");  break;
-			case Token_Or:     ssa_fprintf(f, "or");   break;
-			case Token_Xor:    ssa_fprintf(f, "xor");  break;
-			case Token_Shl:    ssa_fprintf(f, "shl");  break;
-			case Token_Shr:    ssa_fprintf(f, "lshr"); break;
-			case Token_Mul:    ssa_fprintf(f, "mul");  break;
-			case Token_Not:    ssa_fprintf(f, "xor");  break;
-
-			case Token_AndNot: GB_PANIC("Token_AndNot Should never be called");
-
-			default: {
-				if (!is_type_float(elem_type)) {
-					if (is_type_unsigned(elem_type)) ssa_fprintf(f, "u");
-					else                             ssa_fprintf(f, "s");
-				}
-
-				switch (bo->op) {
-				case Token_Quo: ssa_fprintf(f, "div"); break;
-				case Token_Mod: ssa_fprintf(f, "rem"); break;
-				}
-			} break;
-			}
-		}
-
-		ssa_fprintf(f, " ");
-		ssa_print_type(f, m, type);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, bo->left, type);
-		ssa_fprintf(f, ", ");
-		ssa_print_value(f, m, bo->right, type);
-		ssa_fprintf(f, "\n");
-
-	} break;
-
-	case ssaInstr_Call: {
-		ssaInstrCall *call = &instr->Call;
-		Type *result_type = call->type;
-		if (result_type) {
-			ssa_fprintf(f, "%%%d = ", value->index);
-		}
-		ssa_fprintf(f, "call ");
-		if (result_type) {
-			ssa_print_type(f, m, result_type);
-		} else {
-			ssa_fprintf(f, "void");
-		}
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, call->value, call->type);
-
-
-		ssa_fprintf(f, "(");
-		if (call->arg_count > 0) {
-			Type *proc_type = base_type(ssa_type(call->value));
-			GB_ASSERT(proc_type->kind == Type_Proc);
-			TypeTuple *params = &proc_type->Proc.params->Tuple;
-			for (isize i = 0; i < call->arg_count; i++) {
-				Entity *e = params->variables[i];
-				GB_ASSERT(e != NULL);
-				Type *t = e->type;
-				if (i > 0) {
-					ssa_fprintf(f, ", ");
-				}
-				ssa_print_type(f, m, t);
-				ssa_fprintf(f, " ");
-				ssaValue *arg = call->args[i];
-				ssa_print_value(f, m, arg, t);
-			}
-		}
-		ssa_fprintf(f, ")\n");
-
-	} break;
-
-	case ssaInstr_Select: {
-		ssa_fprintf(f, "%%%d = select i1 ", value->index);
-		ssa_print_value(f, m, instr->Select.cond, t_bool);
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, ssa_type(instr->Select.true_value));
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->Select.true_value, ssa_type(instr->Select.true_value));
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, ssa_type(instr->Select.false_value));
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->Select.false_value, ssa_type(instr->Select.false_value));
-		ssa_fprintf(f, "\n");
-	} break;
-
-	case ssaInstr_VectorExtractElement: {
-		Type *vt = ssa_type(instr->VectorExtractElement.vector);
-		Type *it = ssa_type(instr->VectorExtractElement.index);
-		ssa_fprintf(f, "%%%d = extractelement ", value->index);
-
-		ssa_print_type(f, m, vt);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->VectorExtractElement.vector, vt);
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m, it);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->VectorExtractElement.index, it);
-		ssa_fprintf(f, "\n");
-	} break;
-
-	case ssaInstr_VectorInsertElement: {
-		ssaInstrVectorInsertElement *ie = &instr->VectorInsertElement;
-		Type *vt = ssa_type(ie->vector);
-		ssa_fprintf(f, "%%%d = insertelement ", value->index);
-
-		ssa_print_type(f, m, vt);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, ie->vector, vt);
-		ssa_fprintf(f, ", ");
-
-		ssa_print_type(f, m, ssa_type(ie->elem));
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, ie->elem, ssa_type(ie->elem));
-		ssa_fprintf(f, ", ");
-
-		ssa_print_type(f, m, ssa_type(ie->index));
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, ie->index, ssa_type(ie->index));
-
-		ssa_fprintf(f, "\n");
-	} break;
-
-	case ssaInstr_VectorShuffle: {
-		ssaInstrVectorShuffle *sv = &instr->VectorShuffle;
-		Type *vt = ssa_type(sv->vector);
-		ssa_fprintf(f, "%%%d = shufflevector ", value->index);
-
-		ssa_print_type(f, m, vt);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, sv->vector, vt);
-		ssa_fprintf(f, ", ");
-
-		ssa_print_type(f, m, vt);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, sv->vector, vt);
-		ssa_fprintf(f, ", ");
-
-		ssa_fprintf(f, "<%td x i32> <", sv->index_count);
-		for (isize i = 0; i < sv->index_count; i++) {
-			if (i > 0) {
-				ssa_fprintf(f, ", ");
-			}
-			ssa_fprintf(f, "i32 %d", sv->indices[i]);
-		}
-		ssa_fprintf(f, ">");
-		ssa_fprintf(f, "\n");
-	} break;
-
-	case ssaInstr_BoundsCheck: {
-		ssaInstrBoundsCheck *bc = &instr->BoundsCheck;
-		ssa_fprintf(f, "call void ");
-		ssa_print_encoded_global(f, str_lit("__bounds_check_error"), false);
-		ssa_fprintf(f, "(");
-		ssa_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string);
-		ssa_fprintf(f, ", ");
-
-		ssa_print_type(f, m, t_int);
-		ssa_fprintf(f, " ");
-		ssa_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int);
-		ssa_fprintf(f, ", ");
-
-		ssa_print_type(f, m, t_int);
-		ssa_fprintf(f, " ");
-		ssa_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int);
-		ssa_fprintf(f, ", ");
-
-		ssa_print_type(f, m, t_int);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, bc->index, t_int);
-		ssa_fprintf(f, ", ");
-
-		ssa_print_type(f, m, t_int);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, bc->len, t_int);
-
-		ssa_fprintf(f, ")\n");
-	} break;
-
-	case ssaInstr_SliceBoundsCheck: {
-		ssaInstrSliceBoundsCheck *bc = &instr->SliceBoundsCheck;
-		ssa_fprintf(f, "call void ");
-		if (bc->is_substring) {
-			ssa_print_encoded_global(f, str_lit("__substring_expr_error"), false);
-		} else {
-			ssa_print_encoded_global(f, str_lit("__slice_expr_error"), false);
-		}
-
-		ssa_fprintf(f, "(");
-		ssa_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string);
-		ssa_fprintf(f, ", ");
-
-		ssa_print_type(f, m, t_int);
-		ssa_fprintf(f, " ");
-		ssa_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int);
-		ssa_fprintf(f, ", ");
-
-		ssa_print_type(f, m, t_int);
-		ssa_fprintf(f, " ");
-		ssa_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int);
-		ssa_fprintf(f, ", ");
-
-		ssa_print_type(f, m, t_int);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, bc->low, t_int);
-		ssa_fprintf(f, ", ");
-
-		ssa_print_type(f, m, t_int);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, bc->high, t_int);
-
-		if (!bc->is_substring) {
-			ssa_fprintf(f, ", ");
-			ssa_print_type(f, m, t_int);
-			ssa_fprintf(f, " ");
-			ssa_print_value(f, m, bc->max, t_int);
-		}
-
-		ssa_fprintf(f, ")\n");
-	} break;
-
-
-	default: {
-		GB_PANIC("<unknown instr> %d\n", instr->kind);
-		ssa_fprintf(f, "; <unknown instr> %d\n", instr->kind);
-	} break;
-	}
-}
-
-void ssa_print_proc(ssaFileBuffer *f, ssaModule *m, ssaProcedure *proc) {
-	if (proc->body == NULL) {
-		ssa_fprintf(f, "declare ");
-		if (proc->tags & ProcTag_dll_import) {
-			ssa_fprintf(f, "dllimport ");
-		}
-		if (proc->tags & ProcTag_dll_export) {
-			ssa_fprintf(f, "dllexport ");
-		}
-	} else {
-		ssa_fprintf(f, "\ndefine ");
-	}
-
-	if (proc->tags & ProcTag_stdcall) {
-		ssa_fprintf(f, "cc 64 ");
-	} else if (proc->tags & ProcTag_fastcall) {
-		ssa_fprintf(f, "cc 65 ");
-	}
-
-	TypeProc *proc_type = &proc->type->Proc;
-
-	if (proc_type->result_count == 0) {
-		ssa_fprintf(f, "void");
-	} else {
-		ssa_print_type(f, m, proc_type->results);
-	}
-
-	ssa_fprintf(f, " ");
-	ssa_print_encoded_global(f, proc->name, (proc->tags & (ProcTag_foreign|ProcTag_link_name)) != 0);
-	ssa_fprintf(f, "(");
-
-	if (proc_type->param_count > 0) {
-		TypeTuple *params = &proc_type->params->Tuple;
-		for (isize i = 0; i < params->variable_count; i++) {
-			Entity *e = params->variables[i];
-			if (i > 0) {
-				ssa_fprintf(f, ", ");
-			}
-			ssa_print_type(f, m, e->type);
-			if (proc->body != NULL) {
-				ssa_fprintf(f, " %%%.*s", LIT(e->token.string));
-			}
-		}
-	}
-
-	ssa_fprintf(f, ") ");
-
-	if (proc->tags & ProcTag_inline) {
-		ssa_fprintf(f, "alwaysinline ");
-	}
-	if (proc->tags & ProcTag_no_inline) {
-		ssa_fprintf(f, "noinline ");
-	}
-
-
-	if (proc->module->generate_debug_info && proc->entity != NULL) {
-		if (proc->body != NULL) {
-			ssaDebugInfo *di = *map_ssa_debug_info_get(&proc->module->debug_info, hash_pointer(proc->entity));
-			GB_ASSERT(di->kind == ssaDebugInfo_Proc);
-			ssa_fprintf(f, "!dbg !%d ", di->id);
-		}
-	}
-
-
-	if (proc->body != NULL) {
-		// ssa_fprintf(f, "nounwind uwtable {\n");
-
-		ssa_fprintf(f, "{\n");
-		for_array(i, proc->blocks) {
-			ssaBlock *block = proc->blocks.e[i];
-
-			if (i > 0) ssa_fprintf(f, "\n");
-			ssa_print_block_name(f, block);
-			ssa_fprintf(f, ":\n");
-
-			for_array(j, block->instrs) {
-				ssaValue *value = block->instrs.e[j];
-				ssa_print_instr(f, m, value);
-			}
-		}
-		ssa_fprintf(f, "}\n");
-	} else {
-		ssa_fprintf(f, "\n");
-	}
-
-	for_array(i, proc->children) {
-		ssa_print_proc(f, m, proc->children.e[i]);
-	}
-}
-
-void ssa_print_type_name(ssaFileBuffer *f, ssaModule *m, ssaValue *v) {
-	GB_ASSERT(v->kind == ssaValue_TypeName);
-	Type *bt = base_type(ssa_type(v));
-	if (!is_type_struct(bt) && !is_type_union(bt)) {
-		return;
-	}
-	ssa_print_encoded_local(f, v->TypeName.name);
-	ssa_fprintf(f, " = type ");
-	ssa_print_type(f, m, base_type(v->TypeName.type));
-	ssa_fprintf(f, "\n");
-}
-
-void ssa_print_llvm_ir(ssaGen *ssa) {
-	ssaModule *m = &ssa->module;
-	ssaFileBuffer buf = {0}, *f = &buf;
-	ssa_file_buffer_init(f, &ssa->output_file);
-
-	if (m->layout.len > 0) {
-		ssa_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout));
-	}
-
-	ssa_print_encoded_local(f, str_lit("..string"));
-	ssa_fprintf(f, " = type {i8*, ");
-	ssa_print_type(f, m, t_int);
-	ssa_fprintf(f, "} ; Basic_string\n");
-	ssa_print_encoded_local(f, str_lit("..rawptr"));
-	ssa_fprintf(f, " = type i8* ; Basic_rawptr\n");
-
-	ssa_print_encoded_local(f, str_lit("..any"));
-	ssa_fprintf(f, " = type {");
-	ssa_print_type(f, m, t_type_info_ptr);
-	ssa_fprintf(f, ", ");
-	ssa_print_type(f, m, t_rawptr);
-	ssa_fprintf(f, "} ; Basic_any\n");
-
-
-	for_array(member_index, m->members.entries) {
-		MapSsaValueEntry *entry = &m->members.entries.e[member_index];
-		ssaValue *v = entry->value;
-		if (v->kind != ssaValue_TypeName) {
-			continue;
-		}
-		ssa_print_type_name(f, m, v);
-	}
-
-	ssa_fprintf(f, "\n");
-
-	for_array(member_index, m->members.entries) {
-		MapSsaValueEntry *entry = &m->members.entries.e[member_index];
-		ssaValue *v = entry->value;
-		if (v->kind != ssaValue_Proc) {
-			continue;
-		}
-		if (v->Proc.body == NULL) {
-			ssa_print_proc(f, m, &v->Proc);
-		}
-	}
-
-	for_array(member_index, m->members.entries) {
-		MapSsaValueEntry *entry = &m->members.entries.e[member_index];
-		ssaValue *v = entry->value;
-		if (v->kind != ssaValue_Proc) {
-			continue;
-		}
-		if (v->Proc.body != NULL) {
-			ssa_print_proc(f, m, &v->Proc);
-		}
-	}
-
-
-	for_array(member_index, m->members.entries) {
-		MapSsaValueEntry *entry = &m->members.entries.e[member_index];
-		ssaValue *v = entry->value;
-		if (v->kind != ssaValue_Global) {
-			continue;
-		}
-		ssaValueGlobal *g = &v->Global;
-		Scope *scope = g->entity->scope;
-		bool in_global_scope = false;
-		if (scope != NULL) {
-			in_global_scope = scope->is_global || scope->is_init;
-		}
-		ssa_print_encoded_global(f, g->entity->token.string, in_global_scope);
-		ssa_fprintf(f, " = ");
-		if (g->is_thread_local) {
-			ssa_fprintf(f, "thread_local ");
-		}
-
-		if (g->is_private) {
-			ssa_fprintf(f, "private ");
-		}
-		if (g->is_constant) {
-			if (g->is_unnamed_addr) {
-				ssa_fprintf(f, "unnamed_addr ");
-			}
-			ssa_fprintf(f, "constant ");
-		} else {
-			ssa_fprintf(f, "global ");
-		}
-
-
-		ssa_print_type(f, m, g->entity->type);
-		ssa_fprintf(f, " ");
-		if (g->value != NULL) {
-			ssa_print_value(f, m, g->value, g->entity->type);
-		} else {
-			ssa_fprintf(f, "zeroinitializer");
-		}
-		ssa_fprintf(f, "\n");
-	}
-
-
-#if 0
-	if (m->generate_debug_info) {
-		ssa_fprintf(f, "\n");
-		ssa_fprintf(f, "!llvm.dbg.cu = !{!0}\n");
-
-		for_array(di_index, m->debug_info.entries) {
-			MapSsaDebugInfoEntry *entry = &m->debug_info.entries.e[di_index];
-			ssaDebugInfo *di = entry->value;
-			ssa_fprintf(f, "!%d = ", di->id);
-
-			switch (di->kind) {
-			case ssaDebugInfo_CompileUnit: {
-				auto *cu = &di->CompileUnit;
-				ssaDebugInfo *file = *map_ssa_debug_info_get(&m->debug_info, hash_pointer(cu->file));
-				ssa_fprintf(f,
-				            "distinct !DICompileUnit("
-				            "language: DW_LANG_Go, " // Is this good enough?
-				            "file: !%d, "
-				            "producer: \"%.*s\", "
-				            "flags: \"\", "
-				            "runtimeVersion: 0, "
-				            "isOptimized: false, "
-				            "emissionKind: FullDebug"
-				            ")",
-				            file->id, LIT(cu->producer));
-
-			} break;
-			case ssaDebugInfo_File:
-				ssa_fprintf(f, "!DIFile(filename: \"");
-				ssa_print_escape_string(f, di->File.filename, false);
-				ssa_fprintf(f, "\", directory: \"");
-				ssa_print_escape_string(f, di->File.directory, false);
-				ssa_fprintf(f, "\")");
-				break;
-			case ssaDebugInfo_Proc:
-				ssa_fprintf(f, "distinct !DISubprogram("
-				            "name: \"%.*s\", "
-				            // "linkageName: \"\", "
-				            "file: !%d, "
-				            "line: %td, "
-				            "isDefinition: true, "
-				            "isLocal: false, "
-				            "unit: !0"
-				            ")",
-				            LIT(di->Proc.name),
-				            di->Proc.file->id,
-				            di->Proc.pos.line);
-				break;
-
-			case ssaDebugInfo_AllProcs:
-				ssa_fprintf(f, "!{");
-				for_array(proc_index, di->AllProcs.procs) {
-					ssaDebugInfo *p = di->AllProcs.procs.e[proc_index];
-					if (proc_index > 0) {ssa_fprintf(f, ",");}
-					ssa_fprintf(f, "!%d", p->id);
-				}
-				ssa_fprintf(f, "}");
-				break;
-			}
-
-			ssa_fprintf(f, "\n");
-		}
-	}
-#endif
-	ssa_file_buffer_destroy(f);
-}

+ 0 - 422
src/string.cpp

@@ -1,422 +0,0 @@
-gb_global gbArena string_buffer_arena = {0};
-gb_global gbAllocator string_buffer_allocator = {0};
-
-void init_string_buffer_memory(void) {
-	// NOTE(bill): This should be enough memory for file systems
-	gb_arena_init_from_allocator(&string_buffer_arena, heap_allocator(), gb_megabytes(1));
-	string_buffer_allocator = gb_arena_allocator(&string_buffer_arena);
-}
-
-
-// NOTE(bill): Used for UTF-8 strings
-typedef struct String {
-	u8 *  text;
-	isize len;
-} String;
-// NOTE(bill): used for printf style arguments
-#define LIT(x) ((int)(x).len), (x).text
-
-
-typedef struct String16 {
-	wchar_t *text;
-	isize    len;
-} String16;
-
-
-gb_inline String make_string(u8 *text, isize len) {
-	String s;
-	s.text = text;
-	if (len < 0) {
-		len = gb_strlen(cast(char *)text);
-	}
-	s.len = len;
-	return s;
-}
-
-
-gb_inline String16 make_string16(wchar_t *text, isize len) {
-	String16 s;
-	s.text = text;
-	s.len = len;
-	return s;
-}
-
-
-gb_inline String make_string_c(char *text) {
-	return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
-}
-
-#define str_lit(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1)
-
-
-gb_inline bool are_strings_equal(String a, String b) {
-	if (a.len == b.len) {
-		return gb_memcompare(a.text, b.text, a.len) == 0;
-	}
-	return false;
-}
-
-gb_inline bool str_eq_ignore_case(String a, String b) {
-	if (a.len == b.len) {
-		for (isize i = 0; i < a.len; i++) {
-			char x = cast(char)a.text[i];
-			char y = cast(char)b.text[i];
-			if (gb_char_to_lower(x) != gb_char_to_lower(y))
-				return false;
-		}
-		return true;
-	}
-	return false;
-}
-
-int string_compare(String x, String y) {
-	if (x.len == y.len &&
-	    x.text == y.text) {
-		return 0;
-	}
-
-	isize n = gb_min(x.len, y.len);
-
-	isize fast = n/gb_size_of(isize) + 1;
-	isize offset = (fast-1)*gb_size_of(isize);
-	isize curr_block = 0;
-	if (n <= gb_size_of(isize)) {
-		fast = 0;
-	}
-
-	isize *la = cast(isize *)x.text;
-	isize *lb = cast(isize *)y.text;
-
-	for (; curr_block < fast; curr_block++) {
-		if (la[curr_block] ^ lb[curr_block]) {
-			for (isize pos = curr_block*gb_size_of(isize); pos < n; pos++) {
-				if (x.text[pos] ^ y.text[pos]) {
-					return cast(int)x.text[pos] - cast(int)y.text[pos];
-				}
-			}
-		}
-	}
-
-	for (; offset < n; offset++) {
-		if (x.text[offset] ^ y.text[offset]) {
-			return cast(int)x.text[offset] - cast(int)y.text[offset];
-		}
-	}
-
-	return 0;
-}
-
-GB_COMPARE_PROC(string_cmp_proc) {
-	String x = *(String *)a;
-	String y = *(String *)b;
-	return string_compare(x, y);
-}
-
-
-// gb_inline bool operator ==(String a, String b) { return are_strings_equal(a, b) != 0; }
-// gb_inline bool operator !=(String a, String b) { return !operator==(a, b); }
-// gb_inline bool operator < (String a, String b) { return string_compare(a, b) < 0; }
-// gb_inline bool operator > (String a, String b) { return string_compare(a, b) > 0; }
-// gb_inline bool operator <=(String a, String b) { return string_compare(a, b) <= 0; }
-// gb_inline bool operator >=(String a, String b) { return string_compare(a, b) >= 0; }
-
-// template <size_t N> gb_inline bool operator ==(String a, char const (&b)[N]) { return a == make_string(cast(u8 *)b, N-1); }
-// template <size_t N> gb_inline bool operator !=(String a, char const (&b)[N]) { return a != make_string(cast(u8 *)b, N-1); }
-// template <size_t N> gb_inline bool operator ==(char const (&a)[N], String b) { return make_string(cast(u8 *)a, N-1) == b; }
-// template <size_t N> gb_inline bool operator !=(char const (&a)[N], String b) { return make_string(cast(u8 *)a, N-1) != b; }
-
-gb_inline bool str_eq(String a, String b) { return are_strings_equal(a, b) != 0; }
-gb_inline bool str_ne(String a, String b) { return !str_eq(a, b);                }
-gb_inline bool str_lt(String a, String b) { return string_compare(a, b) < 0;     }
-gb_inline bool str_gt(String a, String b) { return string_compare(a, b) > 0;     }
-gb_inline bool str_le(String a, String b) { return string_compare(a, b) <= 0;    }
-gb_inline bool str_ge(String a, String b) { return string_compare(a, b) >= 0;    }
-
-
-
-gb_inline isize string_extension_position(String str) {
-	isize dot_pos = -1;
-	isize i = str.len;
-	bool seen_dot = false;
-	while (i --> 0) {
-		if (str.text[i] == GB_PATH_SEPARATOR)
-			break;
-		if (str.text[i] == '.') {
-			dot_pos = i;
-			break;
-		}
-	}
-
-	return dot_pos;
-}
-
-gb_inline bool string_has_extension(String str, String ext) {
-	if (str.len > ext.len+1) {
-		u8 *s = str.text+str.len - ext.len-1;
-		if (s[0] == '.') {
-			s++;
-			return gb_memcompare(s, ext.text, ext.len) == 0;
-		}
-		return false;
-	}
-	return false;
-}
-
-bool string_contains_char(String s, u8 c) {
-	for (isize i = 0; i < s.len; i++) {
-		if (s.text[i] == c)
-			return true;
-	}
-	return false;
-}
-
-// TODO(bill): Make this non-windows specific
-String16 string_to_string16(gbAllocator a, String s) {
-	if (s.len < 1) {
-		return make_string16(NULL, 0);
-	}
-
-	int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
-	                              cast(char *)s.text, s.len, NULL, 0);
-	if (len == 0) {
-		return make_string16(NULL, 0);
-	}
-
-	wchar_t *text = gb_alloc_array(a, wchar_t, len+1);
-
-	int len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
-	                               cast(char *)s.text, s.len, text, len);
-	if (len1 == 0) {
-		gb_free(a, text);
-		return make_string16(NULL, 0);
-	}
-	text[len] = 0;
-
-	return make_string16(text, len-1);
-}
-
-String string16_to_string(gbAllocator a, String16 s) {
-	if (s.len < 1) {
-		return make_string(NULL, 0);
-	}
-
-	int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
-	                              s.text, s.len, NULL, 0,
-	                              NULL, NULL);
-	if (len == 0) {
-		return make_string(NULL, 0);
-	}
-
-	u8 *text = gb_alloc_array(a, u8, len+1);
-
-	int len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
-	                               s.text, s.len, cast(char *)text, len,
-	                               NULL, NULL);
-	if (len1 == 0) {
-		gb_free(a, text);
-		return make_string(NULL, 0);
-	}
-	text[len] = 0;
-
-	return make_string(text, len-1);
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *tail_string) {
-	if (s.text[0] == quote &&
-	    (quote == '$' || quote == '"')) {
-		return false;
-	} else if (s.text[0] >= 0x80) {
-		Rune r = -1;
-		isize size = gb_utf8_decode(s.text, s.len, &r);
-		*rune = r;
-		*multiple_bytes = true;
-		*tail_string = make_string(s.text+size, s.len-size);
-		return true;
-	} else if (s.text[0] != '\\') {
-		*rune = s.text[0];
-		*tail_string = make_string(s.text+1, s.len-1);
-		return true;
-	}
-
-	if (s.len <= 1) {
-		return false;
-	}
-	u8 c = s.text[1];
-	s = make_string(s.text+2, s.len-2);
-
-	switch (c) {
-	default: return false;
-
-	case 'a':  *rune = '\a'; break;
-	case 'b':  *rune = '\b'; break;
-	case 'f':  *rune = '\f'; break;
-	case 'n':  *rune = '\n'; break;
-	case 'r':  *rune = '\r'; break;
-	case 't':  *rune = '\t'; break;
-	case 'v':  *rune = '\v'; break;
-	case '\\': *rune = '\\'; break;
-
-
-	case '$':
-	case '"':
-		if (c != quote) {
-			return false;
-		}
-		*rune = c;
-		break;
-
-	case '0':
-	case '1':
-	case '2':
-	case '3':
-	case '4':
-	case '5':
-	case '6':
-	case '7': {
-		i32 r = gb_digit_to_int(c);
-		if (s.len < 2) {
-			return false;
-		}
-		for (isize i = 0; i < 2; i++) {
-			i32 d = gb_digit_to_int(s.text[i]);
-			if (d < 0 || d > 7) {
-				return false;
-			}
-			r = (r<<3) | d;
-		}
-		s = make_string(s.text+2, s.len-2);
-		if (r > 0xff) {
-			return false;
-		}
-		*rune = r;
-	} break;
-
-	case 'x':
-	case 'u':
-	case 'U': {
-		isize count = 0;
-		switch (c) {
-		case 'x': count = 2; break;
-		case 'u': count = 4; break;
-		case 'U': count = 8; break;
-		}
-
-		Rune r = 0;
-		if (s.len < count) {
-			return false;
-		}
-		for (isize i = 0; i < count; i++) {
-			i32 d = gb_hex_digit_to_int(s.text[i]);
-			if (d < 0) {
-				return false;
-			}
-			r = (r<<4) | d;
-		}
-		s = make_string(s.text+count, s.len-count);
-		if (c == 'x') {
-			*rune = r;
-			break;
-		}
-		if (r > GB_RUNE_MAX) {
-			return false;
-		}
-		*rune = r;
-		*multiple_bytes = true;
-	} break;
-	}
-	*tail_string = s;
-	return true;
-}
-
-
-// 0 == failure
-// 1 == original memory
-// 2 == new allocation
-i32 unquote_string(gbAllocator a, String *s_) {
-	GB_ASSERT(s_ != NULL);
-	String s = *s_;
-	isize n = s.len;
-	if (n < 2)
-		return 0;
-	u8 quote = s.text[0];
-	if (quote != s.text[n-1])
-		return 0;
-	s.text += 1;
-	s.len -= 2;
-
-	if (quote == '`') {
-		if (string_contains_char(s, '`')) {
-			return 0;
-		}
-		*s_ = s;
-		return 1;
-	}
-	if (quote != '"' && quote != '$')
-		return 0;
-
-	if (string_contains_char(s, '\n'))
-		return 0;
-
-	if (!string_contains_char(s, '\\') && !string_contains_char(s, quote)) {
-		if (quote == '"') {
-			*s_ = s;
-			return 1;
-		} else if (quote == '$') {
-			Rune r = GB_RUNE_INVALID;
-			isize size = gb_utf8_decode(s.text, s.len, &r);
-			if ((size == s.len) && (r != -1 || size != 1)) {
-				*s_ = s;
-				return 1;
-			}
-		}
-	}
-
-
-	u8 rune_temp[4] = {0};
-	isize buf_len = 3*s.len / 2;
-	u8 *buf = gb_alloc_array(a, u8, buf_len);
-	isize offset = 0;
-	while (s.len > 0) {
-		String tail_string = {0};
-		Rune r = 0;
-		bool multiple_bytes = false;
-		bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
-		if (!success) {
-			gb_free(a, buf);
-			return 0;
-		}
-		s = tail_string;
-
-		if (r < 0x80 || !multiple_bytes) {
-			buf[offset++] = cast(u8)r;
-		} else {
-			isize size = gb_utf8_encode_rune(rune_temp, r);
-			gb_memmove(buf+offset, rune_temp, size);
-			offset += size;
-		}
-
-		if (quote == '$' && s.len != 0) {
-			gb_free(a, buf);
-			return 0;
-		}
-	}
-	*s_ = make_string(buf, offset);
-	return 2;
-}

+ 0 - 105
src/timings.cpp

@@ -1,105 +0,0 @@
-typedef struct TimeStamp {
-	u64    start;
-	u64    finish;
-	String label;
-} TimeStamp;
-
-typedef struct Timings {
-	TimeStamp        total;
-	Array(TimeStamp) sections;
-	u64              freq;
-} Timings;
-
-
-u64 win32_time_stamp_time_now(void) {
-	LARGE_INTEGER counter;
-	QueryPerformanceCounter(&counter);
-	return counter.QuadPart;
-}
-
-u64 win32_time_stamp__freq(void) {
-	gb_local_persist LARGE_INTEGER win32_perf_count_freq = {0};
-	if (!win32_perf_count_freq.QuadPart) {
-		QueryPerformanceFrequency(&win32_perf_count_freq);
-		GB_ASSERT(win32_perf_count_freq.QuadPart != 0);
-	}
-
-	return win32_perf_count_freq.QuadPart;
-}
-
-u64 time_stamp_time_now(void) {
-#if defined(GB_SYSTEM_WINDOWS)
-	return win32_time_stamp_time_now();
-#else
-#error time_stamp_time_now
-#endif
-}
-
-u64 time_stamp__freq(void) {
-#if defined(GB_SYSTEM_WINDOWS)
-	return win32_time_stamp__freq();
-#else
-#error time_stamp__freq
-#endif
-}
-
-TimeStamp make_time_stamp(String label) {
-	TimeStamp ts = {0};
-	ts.start = time_stamp_time_now();
-	ts.label = label;
-	return ts;
-}
-
-void timings_init(Timings *t, String label, isize buffer_size) {
-	array_init_reserve(&t->sections, heap_allocator(), buffer_size);
-	t->total = make_time_stamp(label);
-	t->freq  = time_stamp__freq();
-}
-
-void timings_destroy(Timings *t) {
-	array_free(&t->sections);
-}
-
-void timings__stop_current_section(Timings *t) {
-	if (t->sections.count > 0) {
-		t->sections.e[t->sections.count-1].finish = time_stamp_time_now();
-	}
-}
-
-void timings_start_section(Timings *t, String label) {
-	timings__stop_current_section(t);
-	array_add(&t->sections, make_time_stamp(label));
-}
-
-f64 time_stamp_as_ms(TimeStamp ts, u64 freq) {
-	GB_ASSERT_MSG(ts.finish >= ts.start, "time_stamp_as_ms - %.*s", LIT(ts.label));
-	return 1000.0 * cast(f64)(ts.finish - ts.start) / cast(f64)freq;
-}
-
-void timings_print_all(Timings *t) {
-	timings__stop_current_section(t);
-	t->total.finish = time_stamp_time_now();
-
-	char const SPACES[] = "                                                                ";
-
-	isize max_len = t->total.label.len;
-	for_array(i, t->sections) {
-		TimeStamp ts = t->sections.e[i];
-		max_len = gb_max(max_len, ts.label.len);
-	}
-
-	GB_ASSERT(max_len <= gb_size_of(SPACES)-1);
-
-	gb_printf("%.*s%.*s - %.3f ms\n",
-	          LIT(t->total.label),
-	          cast(int)(max_len-t->total.label.len), SPACES,
-	          time_stamp_as_ms(t->total, t->freq));
-
-	for_array(i, t->sections) {
-		TimeStamp ts = t->sections.e[i];
-		gb_printf("%.*s%.*s - %.3f ms\n",
-		          LIT(ts.label),
-	              cast(int)(max_len-ts.label.len), SPACES,
-		          time_stamp_as_ms(ts, t->freq));
-	}
-}

+ 0 - 816
src/tokenizer.cpp

@@ -1,816 +0,0 @@
-#define TOKEN_KINDS \
-	TOKEN_KIND(Token_Invalid, "Invalid"), \
-	TOKEN_KIND(Token_EOF, "EOF"), \
-	TOKEN_KIND(Token_Comment, "Comment"), \
-\
-TOKEN_KIND(Token__LiteralBegin, "_LiteralBegin"), \
-	TOKEN_KIND(Token_Identifier, "Identifier"), \
-	TOKEN_KIND(Token_Integer, "Integer"), \
-	TOKEN_KIND(Token_Float, "Float"), \
-	TOKEN_KIND(Token_Rune, "Rune"), \
-	TOKEN_KIND(Token_String, "String"), \
-TOKEN_KIND(Token__LiteralEnd, "_LiteralEnd"), \
-\
-TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \
-	TOKEN_KIND(Token_Eq, "="), \
-	TOKEN_KIND(Token_Not, "!"), \
-	TOKEN_KIND(Token_Hash, "#"), \
-	TOKEN_KIND(Token_At, "@"), \
-	TOKEN_KIND(Token_Pointer, "^"), \
-	TOKEN_KIND(Token_Maybe, "?"), \
-	TOKEN_KIND(Token_Add, "+"), \
-	TOKEN_KIND(Token_Sub, "-"), \
-	TOKEN_KIND(Token_Mul, "*"), \
-	TOKEN_KIND(Token_Quo, "/"), \
-	TOKEN_KIND(Token_Mod, "%"), \
-	TOKEN_KIND(Token_And, "&"), \
-	TOKEN_KIND(Token_Or, "|"), \
-	TOKEN_KIND(Token_Xor, "~"), \
-	TOKEN_KIND(Token_AndNot, "&~"), \
-	TOKEN_KIND(Token_Shl, "<<"), \
-	TOKEN_KIND(Token_Shr, ">>"), \
-\
-	TOKEN_KIND(Token_as,         "as"), \
-	TOKEN_KIND(Token_transmute,  "transmute"), \
-	TOKEN_KIND(Token_down_cast,  "down_cast"), \
-	TOKEN_KIND(Token_union_cast, "union_cast"), \
-\
-	TOKEN_KIND(Token_Prime, "'"), \
-	TOKEN_KIND(Token_DoublePrime, "''"), \
-\
-	TOKEN_KIND(Token_CmpAnd, "&&"), \
-	TOKEN_KIND(Token_CmpOr, "||"), \
-\
-TOKEN_KIND(Token__AssignOpBegin, "_AssignOpBegin"), \
-	TOKEN_KIND(Token_AddEq, "+="), \
-	TOKEN_KIND(Token_SubEq, "-="), \
-	TOKEN_KIND(Token_MulEq, "*="), \
-	TOKEN_KIND(Token_QuoEq, "/="), \
-	TOKEN_KIND(Token_ModEq, "%="), \
-	TOKEN_KIND(Token_AndEq, "&="), \
-	TOKEN_KIND(Token_OrEq, "|="), \
-	TOKEN_KIND(Token_XorEq, "~="), \
-	TOKEN_KIND(Token_AndNotEq, "&~="), \
-	TOKEN_KIND(Token_ShlEq, "<<="), \
-	TOKEN_KIND(Token_ShrEq, ">>="), \
-	TOKEN_KIND(Token_CmpAndEq, "&&="), \
-	TOKEN_KIND(Token_CmpOrEq, "||="), \
-TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \
-	TOKEN_KIND(Token_Increment, "++"), \
-	TOKEN_KIND(Token_Decrement, "--"), \
-	TOKEN_KIND(Token_ArrowRight, "->"), \
-	TOKEN_KIND(Token_ArrowLeft, "<-"), \
-\
-TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
-	TOKEN_KIND(Token_CmpEq, "=="), \
-	TOKEN_KIND(Token_NotEq, "!="), \
-	TOKEN_KIND(Token_Lt, "<"), \
-	TOKEN_KIND(Token_Gt, ">"), \
-	TOKEN_KIND(Token_LtEq, "<="), \
-	TOKEN_KIND(Token_GtEq, ">="), \
-TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \
-\
-	TOKEN_KIND(Token_OpenParen, "("), \
-	TOKEN_KIND(Token_CloseParen, ")"), \
-	TOKEN_KIND(Token_OpenBracket, "["), \
-	TOKEN_KIND(Token_CloseBracket, "]"), \
-	TOKEN_KIND(Token_OpenBrace, "{"), \
-	TOKEN_KIND(Token_CloseBrace, "}"), \
-	TOKEN_KIND(Token_Colon, ":"), \
-	TOKEN_KIND(Token_Semicolon, ";"), \
-	TOKEN_KIND(Token_Period, "."), \
-	TOKEN_KIND(Token_Comma, ","), \
-	TOKEN_KIND(Token_Ellipsis, ".."), \
-	TOKEN_KIND(Token_RangeExclusive, "..<"), \
-TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
-\
-TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
-	TOKEN_KIND(Token_type,           "type"), \
-	TOKEN_KIND(Token_proc,           "proc"), \
-	TOKEN_KIND(Token_match,          "match"), \
-	TOKEN_KIND(Token_break,          "break"), \
-	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"), \
-	TOKEN_KIND(Token_for,            "for"), \
-	TOKEN_KIND(Token_range,          "range"), \
-	TOKEN_KIND(Token_defer,          "defer"), \
-	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_asm,            "asm"), \
-	TOKEN_KIND(Token_volatile,       "volatile"), \
-	TOKEN_KIND(Token_atomic,         "atomic"), \
-	TOKEN_KIND(Token_push_allocator, "push_allocator"), \
-	TOKEN_KIND(Token_push_context,   "push_context"), \
-TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
-	TOKEN_KIND(Token_Count, "")
-
-typedef enum TokenKind {
-#define TOKEN_KIND(e, s) e
-	TOKEN_KINDS
-#undef TOKEN_KIND
-} TokenKind;
-
-String const token_strings[] = {
-#define TOKEN_KIND(e, s) {cast(u8 *)s, gb_size_of(s)-1}
-	TOKEN_KINDS
-#undef TOKEN_KIND
-};
-
-
-typedef struct TokenPos {
-	String file;
-	isize  line;
-	isize  column;
-} TokenPos;
-
-i32 token_pos_cmp(TokenPos a, TokenPos b) {
-	if (a.line == b.line) {
-		if (a.column == b.column) {
-			isize min_len = gb_min(a.file.len, b.file.len);
-			return gb_memcompare(a.file.text, b.file.text, min_len);
-		}
-		return (a.column < b.column) ? -1 : +1;
-	}
-
-	return (a.line < b.line) ? -1 : +1;
-}
-
-bool token_pos_are_equal(TokenPos a, TokenPos b) {
-	return token_pos_cmp(a, b) == 0;
-}
-
-// NOTE(bill): Text is UTF-8, thus why u8 and not char
-typedef struct Token {
-	TokenKind kind;
-	String string;
-	TokenPos pos;
-} Token;
-
-Token empty_token = {Token_Invalid};
-Token blank_token = {Token_Identifier, {cast(u8 *)"_", 1}};
-
-Token make_token_ident(String s) {
-	Token t = {Token_Identifier, s};
-	return t;
-}
-
-
-typedef struct ErrorCollector {
-	TokenPos prev;
-	i64 count;
-	i64 warning_count;
-	gbMutex mutex;
-} ErrorCollector;
-
-gb_global ErrorCollector global_error_collector;
-
-void init_global_error_collector(void) {
-	gb_mutex_init(&global_error_collector.mutex);
-}
-
-
-void warning(Token token, char *fmt, ...) {
-	gb_mutex_lock(&global_error_collector.mutex);
-
-	global_error_collector.warning_count++;
-	// NOTE(bill): Duplicate error, skip it
-	if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
-		va_list va;
-
-		global_error_collector.prev = token.pos;
-
-		va_start(va, fmt);
-		gb_printf_err("%.*s(%td:%td) Warning: %s\n",
-		              LIT(token.pos.file), token.pos.line, token.pos.column,
-		              gb_bprintf_va(fmt, va));
-		va_end(va);
-	}
-
-	gb_mutex_unlock(&global_error_collector.mutex);
-}
-
-void error(Token token, char *fmt, ...) {
-	gb_mutex_lock(&global_error_collector.mutex);
-
-	global_error_collector.count++;
-	// NOTE(bill): Duplicate error, skip it
-	if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
-		va_list va;
-
-		global_error_collector.prev = token.pos;
-
-		va_start(va, fmt);
-		gb_printf_err("%.*s(%td:%td) %s\n",
-		              LIT(token.pos.file), token.pos.line, token.pos.column,
-		              gb_bprintf_va(fmt, va));
-		va_end(va);
-	}
-
-	gb_mutex_unlock(&global_error_collector.mutex);
-}
-
-void syntax_error(Token token, char *fmt, ...) {
-	gb_mutex_lock(&global_error_collector.mutex);
-
-	global_error_collector.count++;
-	// NOTE(bill): Duplicate error, skip it
-	if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
-		va_list va;
-
-		global_error_collector.prev = token.pos;
-
-		va_start(va, fmt);
-		gb_printf_err("%.*s(%td:%td) Syntax Error: %s\n",
-		              LIT(token.pos.file), token.pos.line, token.pos.column,
-		              gb_bprintf_va(fmt, va));
-		va_end(va);
-	}
-
-	gb_mutex_unlock(&global_error_collector.mutex);
-}
-
-
-void compiler_error(char *fmt, ...) {
-	va_list va;
-
-	va_start(va, fmt);
-	gb_printf_err("Internal Compiler Error: %s\n",
-	              gb_bprintf_va(fmt, va));
-	va_end(va);
-	gb_exit(1);
-}
-
-
-
-
-
-gb_inline bool token_is_literal(Token t) {
-	return gb_is_between(t.kind, Token__LiteralBegin+1, Token__LiteralEnd-1);
-}
-gb_inline bool token_is_operator(Token t) {
-	return gb_is_between(t.kind, Token__OperatorBegin+1, Token__OperatorEnd-1);
-}
-gb_inline bool token_is_keyword(Token t) {
-	return gb_is_between(t.kind, Token__KeywordBegin+1, Token__KeywordEnd-1);
-}
-gb_inline bool token_is_comparison(Token t) {
-	return gb_is_between(t.kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1);
-}
-gb_inline bool token_is_shift(Token t) {
-	return t.kind == Token_Shl || t.kind == Token_Shr;
-}
-
-gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); }
-
-
-typedef enum TokenizerInitError {
-	TokenizerInit_None,
-
-	TokenizerInit_Invalid,
-	TokenizerInit_NotExists,
-	TokenizerInit_Permission,
-	TokenizerInit_Empty,
-
-	TokenizerInit_Count,
-} TokenizerInitError;
-
-
-typedef struct Tokenizer {
-	String fullpath;
-	u8 *start;
-	u8 *end;
-
-	Rune  curr_rune;   // current character
-	u8 *  curr;        // character pos
-	u8 *  read_curr;   // pos from start
-	u8 *  line;        // current line pos
-	isize line_count;
-
-	isize error_count;
-	Array(String) allocated_strings;
-} Tokenizer;
-
-
-void tokenizer_err(Tokenizer *t, char *msg, ...) {
-	va_list va;
-	isize column = t->read_curr - t->line+1;
-	if (column < 1)
-		column = 1;
-
-	gb_printf_err("%.*s(%td:%td) Syntax error: ", LIT(t->fullpath), t->line_count, column);
-
-	va_start(va, msg);
-	gb_printf_err_va(msg, va);
-	va_end(va);
-
-	gb_printf_err("\n");
-
-	t->error_count++;
-}
-
-void advance_to_next_rune(Tokenizer *t) {
-	if (t->read_curr < t->end) {
-		Rune rune;
-		isize width = 1;
-
-		t->curr = t->read_curr;
-		if (t->curr_rune == '\n') {
-			t->line = t->curr;
-			t->line_count++;
-		}
-		rune = *t->read_curr;
-		if (rune == 0) {
-			tokenizer_err(t, "Illegal character NUL");
-		} else if (rune >= 0x80) { // not ASCII
-			width = gb_utf8_decode(t->read_curr, t->end-t->read_curr, &rune);
-			if (rune == GB_RUNE_INVALID && width == 1)
-				tokenizer_err(t, "Illegal UTF-8 encoding");
-			else if (rune == GB_RUNE_BOM && t->curr-t->start > 0)
-				tokenizer_err(t, "Illegal byte order mark");
-		}
-		t->read_curr += width;
-		t->curr_rune = rune;
-	} else {
-		t->curr = t->end;
-		if (t->curr_rune == '\n') {
-			t->line = t->curr;
-			t->line_count++;
-		}
-		t->curr_rune = GB_RUNE_EOF;
-	}
-}
-
-TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
-	TokenizerInitError err = TokenizerInit_None;
-
-	char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1);
-	memcpy(c_str, fullpath.text, fullpath.len);
-	c_str[fullpath.len] = '\0';
-
-	// TODO(bill): Memory map rather than copy contents
-	gbFileContents fc = gb_file_read_contents(heap_allocator(), true, c_str);
-	gb_zero_item(t);
-	if (fc.data != NULL) {
-		t->start = cast(u8 *)fc.data;
-		t->line = t->read_curr = t->curr = t->start;
-		t->end = t->start + fc.size;
-		t->fullpath = fullpath;
-		t->line_count = 1;
-
-		advance_to_next_rune(t);
-		if (t->curr_rune == GB_RUNE_BOM) {
-			advance_to_next_rune(t); // Ignore BOM at file beginning
-		}
-
-		array_init(&t->allocated_strings, heap_allocator());
-	} else {
-		gbFile f = {0};
-		gbFileError file_err = gb_file_open(&f, c_str);
-
-		switch (file_err) {
-		case gbFileError_Invalid:    err = TokenizerInit_Invalid;    break;
-		case gbFileError_NotExists:  err = TokenizerInit_NotExists;  break;
-		case gbFileError_Permission: err = TokenizerInit_Permission; break;
-		}
-
-		if (err == TokenizerInit_None && gb_file_size(&f) == 0) {
-			err = TokenizerInit_Empty;
-		}
-
-		gb_file_close(&f);
-	}
-
-	gb_free(heap_allocator(), c_str);
-	return err;
-}
-
-gb_inline void destroy_tokenizer(Tokenizer *t) {
-	if (t->start != NULL) {
-		gb_free(heap_allocator(), t->start);
-	}
-	for_array(i, t->allocated_strings) {
-		gb_free(heap_allocator(), t->allocated_strings.e[i].text);
-	}
-	array_free(&t->allocated_strings);
-}
-
-void tokenizer_skip_whitespace(Tokenizer *t) {
-	while (rune_is_whitespace(t->curr_rune)) {
-		advance_to_next_rune(t);
-	}
-}
-
-gb_inline i32 digit_value(Rune r) {
-	if (gb_char_is_digit(cast(char)r)) {
-		return r - '0';
-	} else if (gb_is_between(cast(char)r, 'a', 'f')) {
-		return r - 'a' + 10;
-	} else if (gb_is_between(cast(char)r, 'A', 'F')) {
-		return r - 'A' + 10;
-	}
-	return 16; // NOTE(bill): Larger than highest possible
-}
-
-gb_inline void scan_mantissa(Tokenizer *t, i32 base) {
-	// TODO(bill): Allow for underscores in numbers as a number separator
-	// TODO(bill): Is this a good idea?
-	// while (digit_value(t->curr_rune) < base || t->curr_rune == '_')
-	while (digit_value(t->curr_rune) < base) {
-		advance_to_next_rune(t);
-	}
-}
-
-
-Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
-	Token token = {0};
-	token.kind = Token_Integer;
-	token.string = make_string(t->curr, 1);
-	token.pos.file = t->fullpath;
-	token.pos.line = t->line_count;
-	token.pos.column = t->curr-t->line+1;
-
-	if (seen_decimal_point) {
-		token.kind = Token_Float;
-		scan_mantissa(t, 10);
-		goto exponent;
-	}
-
-	if (t->curr_rune == '0') {
-		u8 *prev = t->curr;
-		advance_to_next_rune(t);
-		if (t->curr_rune == 'b') { // Binary
-			advance_to_next_rune(t);
-			scan_mantissa(t, 2);
-			if (t->curr - prev <= 2)
-				token.kind = Token_Invalid;
-		} else if (t->curr_rune == 'o') { // Octal
-			advance_to_next_rune(t);
-			scan_mantissa(t, 8);
-			if (t->curr - prev <= 2)
-				token.kind = Token_Invalid;
-		} else if (t->curr_rune == 'd') { // Decimal
-			advance_to_next_rune(t);
-			scan_mantissa(t, 10);
-			if (t->curr - prev <= 2)
-				token.kind = Token_Invalid;
-		} else if (t->curr_rune == 'x') { // Hexadecimal
-			advance_to_next_rune(t);
-			scan_mantissa(t, 16);
-			if (t->curr - prev <= 2)
-				token.kind = Token_Invalid;
-		} else {
-			seen_decimal_point = false;
-			scan_mantissa(t, 10);
-
-			if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') {
-				seen_decimal_point = true;
-				goto fraction;
-			}
-		}
-
-		token.string.len = t->curr - token.string.text;
-		return token;
-	}
-
-	scan_mantissa(t, 10);
-
-fraction:
-	if (t->curr_rune == '.') {
-		token.kind = Token_Float;
-		advance_to_next_rune(t);
-		scan_mantissa(t, 10);
-	}
-
-exponent:
-	if (t->curr_rune == 'e' || t->curr_rune == 'E') {
-		token.kind = Token_Float;
-		advance_to_next_rune(t);
-		if (t->curr_rune == '-' || t->curr_rune == '+') {
-			advance_to_next_rune(t);
-		}
-		scan_mantissa(t, 10);
-	}
-
-	token.string.len = t->curr - token.string.text;
-	return token;
-}
-
-// Quote == " for string
-bool scan_escape(Tokenizer *t, Rune quote) {
-	isize len = 0;
-	u32 base = 0, max = 0, x = 0;
-
-	Rune r = t->curr_rune;
-	if (r == 'a'  ||
-	    r == 'b'  ||
-	    r == 'f'  ||
-	    r == 'n'  ||
-	    r == 'r'  ||
-	    r == 't'  ||
-	    r == 'v'  ||
-	    r == '\\' ||
-	    r == quote) {
-		advance_to_next_rune(t);
-		return true;
-	} else if (gb_is_between(r, '0', '7')) {
-		len = 3; base = 8; max = 255;
-	} else if (r == 'x') {
-		advance_to_next_rune(t);
-		len = 2; base = 16; max = 255;
-	} else if (r == 'u') {
-		advance_to_next_rune(t);
-		len = 4; base = 16; max = GB_RUNE_MAX;
-	} else if (r == 'U') {
-		advance_to_next_rune(t);
-		len = 8; base = 16; max = GB_RUNE_MAX;
-	} else {
-		if (t->curr_rune < 0)
-			tokenizer_err(t, "Escape sequence was not terminated");
-		else
-			tokenizer_err(t, "Unknown escape sequence");
-		return false;
-	}
-
-	while (len --> 0) {
-		u32 d = cast(u32)digit_value(t->curr_rune);
-		if (d >= base) {
-			if (t->curr_rune < 0)
-				tokenizer_err(t, "Escape sequence was not terminated");
-			else
-				tokenizer_err(t, "Illegal character %d in escape sequence", t->curr_rune);
-			return false;
-		}
-
-		x = x*base + d;
-		advance_to_next_rune(t);
-	}
-
-	return true;
-}
-
-gb_inline TokenKind token_kind_variant2(Tokenizer *t, TokenKind a, TokenKind b) {
-	if (t->curr_rune == '=') {
-		advance_to_next_rune(t);
-		return b;
-	}
-	return a;
-}
-
-
-gb_inline TokenKind token_kind_variant3(Tokenizer *t, TokenKind a, TokenKind b, Rune ch_c, TokenKind c) {
-	if (t->curr_rune == '=') {
-		advance_to_next_rune(t);
-		return b;
-	}
-	if (t->curr_rune == ch_c) {
-		advance_to_next_rune(t);
-		return c;
-	}
-	return a;
-}
-
-gb_inline TokenKind token_kind_variant4(Tokenizer *t, TokenKind a, TokenKind b, Rune ch_c, TokenKind c, Rune ch_d, TokenKind d) {
-	if (t->curr_rune == '=') {
-		advance_to_next_rune(t);
-		return b;
-	} else if (t->curr_rune == ch_c) {
-		advance_to_next_rune(t);
-		return c;
-	} else if (t->curr_rune == ch_d) {
-		advance_to_next_rune(t);
-		return d;
-	}
-	return a;
-}
-
-
-gb_inline TokenKind token_kind_dub_eq(Tokenizer *t, Rune sing_rune, TokenKind sing, TokenKind sing_eq, TokenKind dub, TokenKind dub_eq) {
-	if (t->curr_rune == '=') {
-		advance_to_next_rune(t);
-		return sing_eq;
-	} else if (t->curr_rune == sing_rune) {
-		advance_to_next_rune(t);
-		if (t->curr_rune == '=') {
-			advance_to_next_rune(t);
-			return dub_eq;
-		}
-		return dub;
-	}
-	return sing;
-}
-
-Token tokenizer_get_token(Tokenizer *t) {
-	Token token = {0};
-	Rune curr_rune;
-
-	tokenizer_skip_whitespace(t);
-	token.string = make_string(t->curr, 1);
-	token.pos.file = t->fullpath;
-	token.pos.line = t->line_count;
-	token.pos.column = t->curr - t->line + 1;
-
-	curr_rune = t->curr_rune;
-	if (rune_is_letter(curr_rune)) {
-		token.kind = Token_Identifier;
-		while (rune_is_letter(t->curr_rune) || rune_is_digit(t->curr_rune)) {
-			advance_to_next_rune(t);
-		}
-
-		token.string.len = t->curr - token.string.text;
-
-		// NOTE(bill): All keywords are > 1
-		if (token.string.len > 1) {
-			if (str_eq(token.string, token_strings[Token_as])) {
-				token.kind = Token_as;
-			} else if (str_eq(token.string, token_strings[Token_transmute])) {
-				token.kind = Token_transmute;
-			} else if (str_eq(token.string, token_strings[Token_down_cast])) {
-				token.kind = Token_down_cast;
-			} else if (str_eq(token.string, token_strings[Token_union_cast])) {
-				token.kind = Token_union_cast;
-			} else {
-				for (i32 k = Token__KeywordBegin+1; k < Token__KeywordEnd; k++) {
-					if (str_eq(token.string, token_strings[k])) {
-						token.kind = cast(TokenKind)k;
-						break;
-					}
-				}
-			}
-		}
-
-	} else if (gb_is_between(curr_rune, '0', '9')) {
-		token = scan_number_to_token(t, false);
-	} else {
-		advance_to_next_rune(t);
-		switch (curr_rune) {
-		case GB_RUNE_EOF:
-			token.kind = Token_EOF;
-			break;
-
-		case '\'':
-			token.kind = Token_Prime;
-			if (t->curr_rune == '\'') {
-				advance_to_next_rune(t);
-				token.kind = Token_DoublePrime;
-			}
-			break;
-
-		case '`': // Raw String Literal
-		case '"': // String Literal
-		{
-			Rune quote = curr_rune;
-			token.kind = Token_String;
-			if (curr_rune == '"') {
-				for (;;) {
-					Rune r = t->curr_rune;
-					if (r == '\n' || r < 0) {
-						tokenizer_err(t, "String literal not terminated");
-						break;
-					}
-					advance_to_next_rune(t);
-					if (r == quote)
-						break;
-					if (r == '\\')
-						scan_escape(t, '"');
-				}
-			} else {
-				for (;;) {
-					Rune r = t->curr_rune;
-					if (r < 0) {
-						tokenizer_err(t, "String literal not terminated");
-						break;
-					}
-					advance_to_next_rune(t);
-					if (r == quote)
-						break;
-				}
-			}
-			token.string.len = t->curr - token.string.text;
-			i32 success = unquote_string(heap_allocator(), &token.string);
-			if (success > 0) {
-				if (success == 2) {
-					array_add(&t->allocated_strings, token.string);
-				}
-				return token;
-			} else {
-				tokenizer_err(t, "Invalid string literal");
-			}
-		} break;
-
-		case '.':
-			token.kind = Token_Period; // Default
-			if (gb_is_between(t->curr_rune, '0', '9')) { // Might be a number
-				token = scan_number_to_token(t, true);
-			} else if (t->curr_rune == '.') { // Could be an ellipsis
-				advance_to_next_rune(t);
-				token.kind = Token_Ellipsis;
-				if (t->curr_rune == '<') {
-					advance_to_next_rune(t);
-					token.kind = Token_RangeExclusive;
-				}
-			}
-			break;
-
-		case '#': token.kind = Token_Hash;         break;
-		case '@': token.kind = Token_At;           break;
-		case '^': token.kind = Token_Pointer;      break;
-		case '?': token.kind = Token_Maybe;        break;
-		case ';': token.kind = Token_Semicolon;    break;
-		case ',': token.kind = Token_Comma;        break;
-		case '(': token.kind = Token_OpenParen;    break;
-		case ')': token.kind = Token_CloseParen;   break;
-		case '[': token.kind = Token_OpenBracket;  break;
-		case ']': token.kind = Token_CloseBracket; break;
-		case '{': token.kind = Token_OpenBrace;    break;
-		case '}': token.kind = Token_CloseBrace;   break;
-		case ':': token.kind = Token_Colon;        break;
-
-		case '*': token.kind = token_kind_variant2(t, Token_Mul,   Token_MulEq);     break;
-		case '%': token.kind = token_kind_variant2(t, Token_Mod,   Token_ModEq);     break;
-		case '=': token.kind = token_kind_variant2(t, Token_Eq,    Token_CmpEq);     break;
-		case '~': token.kind = token_kind_variant2(t, Token_Xor,   Token_XorEq);     break;
-		case '!': token.kind = token_kind_variant2(t, Token_Not,   Token_NotEq);     break;
-		case '+': token.kind = token_kind_variant3(t, Token_Add,   Token_AddEq, '+', Token_Increment); break;
-		case '-': token.kind = token_kind_variant4(t, Token_Sub,   Token_SubEq, '-', Token_Decrement, '>', Token_ArrowRight); break;
-		case '/': {
-			if (t->curr_rune == '/') {
-				while (t->curr_rune != '\n') {
-					advance_to_next_rune(t);
-				}
-				token.kind = Token_Comment;
-			} else if (t->curr_rune == '*') {
-				isize comment_scope = 1;
-				advance_to_next_rune(t);
-				while (comment_scope > 0) {
-					if (t->curr_rune == '/') {
-						advance_to_next_rune(t);
-						if (t->curr_rune == '*') {
-							advance_to_next_rune(t);
-							comment_scope++;
-						}
-					} else if (t->curr_rune == '*') {
-						advance_to_next_rune(t);
-						if (t->curr_rune == '/') {
-							advance_to_next_rune(t);
-							comment_scope--;
-						}
-					} else {
-						advance_to_next_rune(t);
-					}
-				}
-				token.kind = Token_Comment;
-			} else {
-				token.kind = token_kind_variant2(t, Token_Quo, Token_QuoEq);
-			}
-		} break;
-
-		case '<':
-			if (t->curr_rune == '-') {
-				token.kind = Token_ArrowLeft;
-			} else {
-				token.kind = token_kind_dub_eq(t, '<', Token_Lt, Token_LtEq, Token_Shl, Token_ShlEq);
-			}
-			break;
-		case '>':
-			token.kind = token_kind_dub_eq(t, '>', Token_Gt, Token_GtEq, Token_Shr, Token_ShrEq);
-			break;
-
-		case '&':
-			token.kind = Token_And;
-			if (t->curr_rune == '~') {
-				token.kind = Token_AndNot;
-				advance_to_next_rune(t);
-				if (t->curr_rune == '=') {
-					token.kind = Token_AndNotEq;
-					advance_to_next_rune(t);
-				}
-			} else {
-				token.kind = token_kind_dub_eq(t, '&', Token_And, Token_AndEq, Token_CmpAnd, Token_CmpAndEq);
-			}
-			break;
-
-		case '|': token.kind = token_kind_dub_eq(t, '|', Token_Or, Token_OrEq, Token_CmpOr, Token_CmpOrEq); break;
-
-		default:
-			if (curr_rune != GB_RUNE_BOM) {
-				u8 str[4] = {0};
-				int len = cast(int)gb_utf8_encode_rune(str, curr_rune);
-				tokenizer_err(t, "Illegal character: %.*s (%d) ", len, str, curr_rune);
-			}
-			token.kind = Token_Invalid;
-			break;
-		}
-	}
-
-	token.string.len = t->curr - token.string.text;
-	return token;
-}

+ 0 - 66
src/unicode.cpp

@@ -1,66 +0,0 @@
-#pragma warning(push)
-#pragma warning(disable: 4245)
-
-// #include "utf8proc/utf8proc.h"
-#include "utf8proc/utf8proc.c"
-
-#pragma warning(pop)
-
-bool rune_is_letter(Rune r) {
-	if ((r < 0x80 && gb_char_is_alpha(cast(char)r)) ||
-	    r == '_') {
-		return true;
-	}
-	switch (utf8proc_category(r)) {
-	case UTF8PROC_CATEGORY_LU:
-	case UTF8PROC_CATEGORY_LL:
-	case UTF8PROC_CATEGORY_LT:
-	case UTF8PROC_CATEGORY_LM:
-	case UTF8PROC_CATEGORY_LO:
-		return true;
-	}
-	return false;
-}
-
-bool rune_is_digit(Rune r) {
-	if (r < 0x80 && gb_is_between(r, '0', '9')) {
-		return true;
-	}
-	return utf8proc_category(r) == UTF8PROC_CATEGORY_ND;
-}
-
-bool rune_is_whitespace(Rune r) {
-	switch (r) {
-	case ' ':
-	case '\t':
-	case '\n':
-	case '\r':
-		return true;
-	}
-	return false;
-}
-
-
-bool is_string_an_identifier(String s) {
-	if (s.len < 1) {
-		return false;
-	}
-	isize offset = 0;
-	while (offset < s.len) {
-		bool ok = false;
-		Rune r = -1;
-		isize size = gb_utf8_decode(s.text+offset, s.len-offset, &r);
-		if (offset == 0) {
-			ok = rune_is_letter(r);
-		} else {
-			ok = rune_is_letter(r) || rune_is_digit(r);
-		}
-
-		if (!ok) {
-			return false;
-		}
-		offset += size;
-	}
-
-	return offset == s.len;
-}