Jelajahi Sumber

Minimal Dependency Map: Only build what is needed

Ginger Bill 9 tahun lalu
induk
melakukan
b593332942

+ 8 - 4
build.bat

@@ -30,10 +30,8 @@ set linker_flags= -incremental:no -opt:ref -subsystem:console
 
 if %release_mode% EQU 0 ( rem Debug
 	set linker_flags=%linker_flags% -debug
-	set libs=%libs% src\utf8proc\utf8proc_debug.lib
 ) else ( rem Release
-	set linker_flags=%linker_flags% -debug
-	set libs=%libs% src\utf8proc\utf8proc.lib
+	set linker_flags=%linker_flags%
 )
 
 set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
@@ -48,7 +46,13 @@ rem pushd %build_dir%
 
 	cl %compiler_settings% "src\main.cpp" ^
 		/link %linker_settings% -OUT:%exe_name% ^
-	&& odin run code/wills_game/willsgame.odin
+	&& odin run code/demo.odin
+	rem clang++ src\main.cpp -o %exe_name% ^
+	rem 	-Wno-deprecated-declarations ^
+	rem 	-Wno-unused-value ^
+	rem 	-Wno-switch ^
+	rem 	-Wno-writable-strings
+	rem && odin run code/demo.odin
 	rem odin run code/demo.odin
 
 

+ 3 - 1
code/demo.odin

@@ -1,3 +1,5 @@
-main :: proc() {
+#import "fmt.odin"
 
+main :: proc() {
+	fmt.println("Hello")
 }

+ 112 - 49
src/checker/checker.cpp

@@ -104,6 +104,7 @@ struct ProcedureInfo {
 	DeclInfo *decl;
 	Type *    type; // Type_Procedure
 	AstNode * body; // AstNode_BlockStatement
+	u32       tags;
 };
 
 struct Scope {
@@ -213,6 +214,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
 struct CheckerContext {
 	Scope *scope;
 	DeclInfo *decl;
+	u32 stmt_state_flags;
 };
 
 // NOTE(bill): Symbol tables
@@ -321,6 +323,7 @@ void check_open_scope(Checker *c, AstNode *node) {
 		scope->is_proc = true;
 	}
 	c->context.scope = scope;
+	c->context.stmt_state_flags |= StmtStateFlag_bounds_check;
 }
 
 void check_close_scope(Checker *c) {
@@ -528,7 +531,6 @@ void destroy_checker_info(CheckerInfo *i) {
 	map_destroy(&i->foreign_procs);
 	map_destroy(&i->type_info_map);
 	map_destroy(&i->files);
-
 }
 
 
@@ -574,14 +576,16 @@ TypeAndValue *type_and_value_of_expression(CheckerInfo *i, AstNode *expression)
 
 
 Entity *entity_of_ident(CheckerInfo *i, AstNode *identifier) {
-	GB_ASSERT(identifier->kind == AstNode_Ident);
-	Entity **found = map_get(&i->definitions, hash_pointer(identifier));
-	if (found)
-		return *found;
-
-	found = map_get(&i->uses, hash_pointer(identifier));
-	if (found)
-		return *found;
+	if (identifier->kind == AstNode_Ident) {
+		Entity **found = map_get(&i->definitions, hash_pointer(identifier));
+		if (found) {
+			return *found;
+		}
+		found = map_get(&i->uses, hash_pointer(identifier));
+		if (found) {
+			return *found;
+		}
+	}
 	return NULL;
 }
 
@@ -611,8 +615,9 @@ void add_type_and_value(CheckerInfo *i, AstNode *expression, AddressingMode mode
 
 	if (mode == Addressing_Constant) {
 		GB_ASSERT(value.kind != ExactValue_Invalid);
-		GB_ASSERT_MSG(type != t_invalid || is_type_constant_type(type),
-		              "type: %s", type_to_string(type));
+		if (!(type != t_invalid || is_type_constant_type(type))) {
+			compiler_error("add_type_and_value - invalid type: %s", type_to_string(type));
+		}
 	}
 
 	TypeAndValue tv = {};
@@ -666,21 +671,19 @@ b32 add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
 	return true;
 }
 
-void add_entity_use(CheckerInfo *i, AstNode *identifier, Entity *entity) {
+void add_entity_use(Checker *c, AstNode *identifier, Entity *entity) {
 	GB_ASSERT(identifier != NULL);
-	GB_ASSERT(identifier->kind == AstNode_Ident);
-	HashKey key = hash_pointer(identifier);
-	map_set(&i->uses, key, entity);
-
-	if (entity != NULL && entity->kind == Entity_ImportName) {
-		entity->ImportName.used = true;
+	if (identifier->kind != AstNode_Ident) {
+		return;
 	}
+	map_set(&c->info.uses, hash_pointer(identifier), entity);
+	add_declaration_dependency(c, entity); // TODO(bill): Should this be here?
 }
 
 
-void add_file_entity(Checker *c, Scope *file_scope, AstNode *identifier, Entity *e, DeclInfo *d) {
+void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d) {
 	GB_ASSERT(identifier->Ident.string == e->token.string);
-	add_entity(c, file_scope, identifier, e);
+	add_entity(c, e->scope, identifier, e);
 	map_set(&c->info.entities, hash_pointer(e), d);
 }
 
@@ -721,7 +724,7 @@ void add_type_info_type(Checker *c, Type *t) {
 		return;
 	}
 
-	Type *bt = get_base_type(t);
+	Type *bt = base_type(t);
 	switch (bt->kind) {
 	case Type_Basic: {
 		if (bt->Basic.kind == Basic_string) {
@@ -778,13 +781,14 @@ void add_type_info_type(Checker *c, Type *t) {
 }
 
 
-void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body) {
+void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u32 tags) {
 	ProcedureInfo info = {};
 	info.file = file;
 	info.token = token;
 	info.decl  = decl;
 	info.type  = type;
 	info.body  = body;
+	info.tags  = tags;
 	gb_array_append(c->procs, info);
 }
 
@@ -808,6 +812,50 @@ void add_curr_ast_file(Checker *c, AstFile *file) {
 	TokenPos zero_pos = {};
 	global_error_collector.prev = zero_pos;
 	c->curr_ast_file = file;
+	c->context.decl = file->decl_info;
+}
+
+
+
+
+void add_dependency_to_map(Map<Entity *> *map, CheckerInfo *info, Entity *node) {
+	if (node == NULL) {
+		return;
+	}
+	if (map_get(map, hash_pointer(node)) != NULL) {
+		return;
+	}
+	map_set(map, hash_pointer(node), node);
+
+
+	DeclInfo **found = map_get(&info->entities, hash_pointer(node));
+	if (found == NULL) {
+		return;
+	}
+
+	DeclInfo *decl = *found;
+	gb_for_array(i, decl->deps.entries) {
+		Entity *e = cast(Entity *)cast(uintptr)decl->deps.entries[i].key.key;
+		add_dependency_to_map(map, info, e);
+	}
+}
+
+Map<Entity *> generate_minimum_dependency_map(CheckerInfo *info, Entity *start) {
+	Map<Entity *> map = {}; // Key: Entity *
+	map_init(&map, gb_heap_allocator());
+
+	add_dependency_to_map(&map, info, start);
+
+	gb_for_array(i, info->entities.entries) {
+		auto *entry = &info->entities.entries[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);
+		}
+	}
+
+	return map;
 }
 
 
@@ -853,7 +901,7 @@ void init_runtime_types(Checker *c) {
 		t_type_info = e->type;
 		t_type_info_ptr = make_type_pointer(c->allocator, t_type_info);
 
-		auto *record = &get_base_type(e->type)->Record;
+		auto *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);
@@ -899,7 +947,6 @@ void init_runtime_types(Checker *c) {
 	}
 }
 
-
 void check_parsed_files(Checker *c) {
 
 	gbArray(AstNode *) import_decls;
@@ -929,6 +976,7 @@ void check_parsed_files(Checker *c) {
 		}
 
 		f->scope = scope;
+		f->decl_info = make_declaration_info(c->allocator, f->scope);
 		HashKey key = hash_string(f->tokenizer.fullpath);
 		map_set(&file_scopes, key, scope);
 		map_set(&c->info.files, key, f);
@@ -963,10 +1011,11 @@ void check_parsed_files(Checker *c) {
 					AstNode *value = cd->values[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_file_entity(c, file_scope, name, e, di);
+					add_entity_and_decl_info(c, name, e, di);
 				}
 
 				isize lhs_count = gb_array_count(cd->names);
@@ -999,6 +1048,7 @@ void check_parsed_files(Checker *c) {
 						value = vd->values[i];
 					}
 					Entity *e = make_entity_variable(c->allocator, file_scope, name->Ident, NULL);
+					e->identifier = name;
 					entities[entity_index++] = e;
 
 					DeclInfo *d = di;
@@ -1010,25 +1060,27 @@ void check_parsed_files(Checker *c) {
 						d->var_decl_tags = vd->tags;
 					}
 
-					add_file_entity(c, file_scope, name, e, d);
+					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_file_entity(c, file_scope, td->name, e, d);
+				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_file_entity(c, file_scope, pd->name, e, d);
+				add_entity_and_decl_info(c, pd->name, e, d);
 			case_end;
 
 			default:
@@ -1069,7 +1121,7 @@ void check_parsed_files(Checker *c) {
 				warning(id->token, "Multiple #import of the same file within this scope");
 			}
 
-			if (id->import_name.string == make_string(".")) {
+			if (id->import_name.string == ".") {
 				// NOTE(bill): Add imported entities to this file's scope
 				gb_for_array(elem_index, scope->elements.entries) {
 					Entity *e = scope->elements.entries[elem_index].value;
@@ -1155,7 +1207,7 @@ void check_parsed_files(Checker *c) {
 	check_global_entity(c, Entity_TypeName);
 
 	init_runtime_types(c);
-#if 1
+
 	check_global_entity(c, Entity_Constant);
 	check_global_entity(c, Entity_Procedure);
 	check_global_entity(c, Entity_Variable);
@@ -1164,13 +1216,42 @@ void check_parsed_files(Checker *c) {
 	gb_for_array(i, c->procs) {
 		ProcedureInfo *pi = &c->procs[i];
 		add_curr_ast_file(c, pi->file);
+
+		b32 bounds_check    = (pi->tags & ProcTag_bounds_check)    != 0;
+		b32 no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0;
+
+		auto prev_context = c->context;
+		defer (c->context = prev_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);
 	}
 
+	if (false) {
+		gb_printf("Dependency graph:\n");
+		gb_for_array(i, c->info.entities.entries) {
+			auto *entry = &c->info.entities.entries[i];
+			Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
+			DeclInfo *d = entry->value;
+			if (gb_array_count(d->deps.entries) > 0) {
+				gb_printf("\t%.*s depends on\n", LIT(e->token.string));
+				gb_for_array(j, d->deps.entries) {
+					Entity *e = cast(Entity *)cast(uintptr)d->deps.entries[j].key.key;
+					gb_printf("\t\t%.*s\n", LIT(e->token.string));
+				}
+			}
+		}
+	}
 
 	// Add untyped expression values
 	gb_for_array(i, c->info.untyped.entries) {
-		auto *entry = c->info.untyped.entries + i;
+		auto *entry = &c->info.untyped.entries[i];
 		HashKey key = entry->key;
 		AstNode *expr = cast(AstNode *)cast(uintptr)key.key;
 		ExpressionInfo *info = &entry->value;
@@ -1181,24 +1262,6 @@ void check_parsed_files(Checker *c) {
 			add_type_and_value(&c->info, expr, info->mode, info->type, info->value);
 		}
 	}
-#endif
-
-#if 0
-	gb_for_array(i, c->parser->files) {
-		AstFile *f = &c->parser->files[i];
-		Scope *scope = f->scope;
-		gb_for_array(j, scope->elements.entries) {
-			Entity *e = scope->elements.entries[j].value;
-			switch (e->kind) {
-			case Entity_ImportName: {
-				if (!e->ImportName.used) {
-					warning(e->token, "Unused import name: %.*s", LIT(e->ImportName.name));
-				}
-			} break;
-			}
-		}
-	}
-#endif
 }
 
 

+ 3 - 0
src/checker/entity.cpp

@@ -34,6 +34,8 @@ struct Entity {
 	Scope *    scope;
 	Token      token;
 	Type *     type;
+	AstNode *  identifier; // Can be NULL
+
 	Entity *   using_parent;
 	AstNode *  using_expr;
 
@@ -51,6 +53,7 @@ struct Entity {
 			b8  is_field;    // Is struct field
 		} Variable;
 		struct {
+			b32 used;
 		} TypeName;
 		struct {
 			b32 used;

+ 83 - 81
src/checker/expr.cpp

@@ -18,8 +18,8 @@ void     update_expr_type          (Checker *c, AstNode *e, Type *type, b32 fina
 b32 check_is_assignable_to_using_subtype(Type *dst, Type *src) {
 	Type *prev_src = src;
 	// Type *prev_dst = dst;
-	src = get_base_type(type_deref(src));
-	// dst = get_base_type(type_deref(dst));
+	src = base_type(type_deref(src));
+	// dst = base_type(type_deref(dst));
 	b32 src_is_ptr = src != prev_src;
 	// b32 dst_is_ptr = dst != prev_dst;
 
@@ -62,8 +62,8 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu
 		return true;
 	}
 
-	Type *src = get_base_type(s);
-	Type *dst = get_base_type(type);
+	Type *src = base_type(s);
+	Type *dst = base_type(type);
 
 
 	if (is_type_untyped(src)) {
@@ -184,7 +184,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
 
 
 void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map<Entity *> *entity_map) {
-	t = get_base_type(type_deref(t));
+	t = base_type(type_deref(t));
 	gbString str = expr_to_string(node);
 	defer (gb_string_free(str));
 
@@ -266,7 +266,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 				AstNode *name = cd->names[i];
 				Entity *e = entities[i];
 				Token name_token = name->Ident;
-				if (name_token.string == make_string("_")) {
+				if (name_token.string == "_") {
 					other_fields[other_field_index++] = e;
 				} else {
 					HashKey key = hash_string(name_token.string);
@@ -288,7 +288,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 			add_entity(c, c->context.scope, td->name, e);
 			check_type_decl(c, e, td->type, NULL, NULL);
 
-			if (name_token.string == make_string("_")) {
+			if (name_token.string == "_") {
 				other_fields[other_field_index++] = e;
 			} else {
 				HashKey key = hash_string(name_token.string);
@@ -299,7 +299,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 					map_set(&entity_map, key, e);
 					other_fields[other_field_index++] = e;
 				}
-				add_entity_use(&c->info, td->name, e);
+				add_entity_use(c, td->name, e);
 			}
 		}
 
@@ -326,7 +326,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 				type->Named.type_name = e;
 				add_entity(c, c->context.scope, name, e);
 
-				if (name_token.string == make_string("_")) {
+				if (name_token.string == "_") {
 					error(name_token, "`_` cannot be used a union subtype");
 					continue;
 				}
@@ -339,7 +339,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 					map_set(&entity_map, key, e);
 					fields[field_index++] = e;
 				}
-				add_entity_use(&c->info, name, e);
+				add_entity_use(c, name, e);
 			}
 		}
 	} else {
@@ -365,7 +365,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 				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);
-				if (name_token.string == make_string("_")) {
+				if (name_token.string == "_") {
 					fields[field_index++] = e;
 				} else {
 					HashKey key = hash_string(name_token.string);
@@ -377,13 +377,13 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 						fields[field_index++] = e;
 						add_entity(c, c->context.scope, name, e);
 					}
-					add_entity_use(&c->info, name, e);
+					add_entity_use(c, name, e);
 				}
 			}
 
 
 			if (vd->is_using) {
-				Type *t = get_base_type(type_deref(type));
+				Type *t = base_type(type_deref(type));
 				if (!is_type_struct(t) && !is_type_raw_union(t)) {
 					Token name_token = vd->names[0]->Ident;
 					error(name_token, "`using` on a field `%.*s` must be a type", LIT(name_token.string));
@@ -619,15 +619,15 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod
 		ast_node(f, FieldValue, field);
 		Token name_token = f->field->Ident;
 
-		if (name_token.string == make_string("count")) {
+		if (name_token.string == "count") {
 			error(name_token, "`count` is a reserved identifier for enumerations");
 			fields[field_index++] = blank_entity;
 			continue;
-		} else if (name_token.string == make_string("min_value")) {
+		} else if (name_token.string == "min_value") {
 			error(name_token, "`min_value` is a reserved identifier for enumerations");
 			fields[field_index++] = blank_entity;
 			continue;
-		} else if (name_token.string == make_string("max_value")) {
+		} else if (name_token.string == "max_value") {
 			error(name_token, "`max_value` is a reserved identifier for enumerations");
 			fields[field_index++] = blank_entity;
 			continue;
@@ -672,7 +672,7 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod
 			add_entity(c, c->context.scope, NULL, e);
 			fields[field_index++] = e;
 		}
-		add_entity_use(&c->info, f->field, e);
+		add_entity_use(c, f->field, e);
 	}
 
 	gb_sort_array(fields, gb_array_count(et->fields), cmp_enum_order);
@@ -802,7 +802,7 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
 	o->expr = n;
 	Entity *e = scope_lookup_entity(c->context.scope, n->Ident.string);
 	if (e == NULL) {
-		if (n->Ident.string == make_string("_")) {
+		if (n->Ident.string == "_") {
 			error(n->Ident, "`_` cannot be used as a value type");
 		} else {
 			error(n->Ident, "Undeclared name: %.*s", LIT(n->Ident.string));
@@ -814,7 +814,7 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
 		}
 		return;
 	}
-	add_entity_use(&c->info, n, e);
+	add_entity_use(c, n, e);
 
 	CycleChecker local_cycle_checker = {};
 	if (cycle_checker == NULL) {
@@ -835,19 +835,21 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
 
 	switch (e->kind) {
 	case Entity_Constant:
-		add_declaration_dependency(c, e);
-		if (type == t_invalid)
+		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:
-		add_declaration_dependency(c, e);
 		e->Variable.used = true;
-		if (type == t_invalid)
+		if (type == t_invalid) {
+			o->type = t_invalid;
 			return;
+		}
 		o->mode = Addressing_Variable;
 		break;
 
@@ -872,7 +874,6 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
 	} break;
 
 	case Entity_Procedure:
-		add_declaration_dependency(c, e);
 		o->mode = Addressing_Value;
 		break;
 
@@ -996,7 +997,7 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c
 
 	case_ast_node(vt, VectorType, e);
 		Type *elem = check_type(c, vt->elem);
-		Type *be = get_base_type(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);
@@ -1097,7 +1098,7 @@ end:
 
 b32 check_unary_op(Checker *c, Operand *o, Token op) {
 	// TODO(bill): Handle errors correctly
-	Type *type = get_base_type(base_vector_type(get_base_type(o->type)));
+	Type *type = base_type(base_vector_type(o->type));
 	gbString str = NULL;
 	defer (gb_string_free(str));
 	switch (op.kind) {
@@ -1132,7 +1133,7 @@ b32 check_unary_op(Checker *c, Operand *o, Token op) {
 
 b32 check_binary_op(Checker *c, Operand *o, Token op) {
 	// TODO(bill): Handle errors correctly
-	Type *type = get_base_type(base_vector_type(o->type));
+	Type *type = base_type(base_vector_type(o->type));
 	switch (op.kind) {
 	case Token_Add:
 	case Token_Sub:
@@ -1285,7 +1286,7 @@ b32 check_is_expr_vector_index(Checker *c, AstNode *expr) {
 		ast_node(ie, IndexExpr, expr);
 		Type *t = type_of_expr(&c->info, ie->expr);
 		if (t != NULL) {
-			return is_type_vector(get_base_type(t));
+			return is_type_vector(base_type(t));
 		}
 	}
 	return false;
@@ -1313,7 +1314,7 @@ void check_unary_expr(Checker *c, Operand *o, Token op, AstNode *node) {
 	}
 
 	if (o->mode == Addressing_Constant) {
-		Type *type = get_base_type(o->type);
+		Type *type = base_type(o->type);
 		GB_ASSERT(type->kind == Type_Basic);
 		i32 precision = 0;
 		if (is_type_unsigned(type))
@@ -1344,13 +1345,13 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
 		switch (op.kind) {
 		case Token_CmpEq:
 		case Token_NotEq:
-			defined = is_type_comparable(get_base_type(x->type));
+			defined = is_type_comparable(base_type(x->type));
 			break;
 		case Token_Lt:
 		case Token_Gt:
 		case Token_LtEq:
 		case Token_GtEq: {
-			defined = is_type_ordered(get_base_type(x->type));
+			defined = is_type_ordered(base_type(x->type));
 		} break;
 		}
 
@@ -1385,8 +1386,8 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
 		update_expr_type(c, y->expr, default_type(y->type), true);
 	}
 
-	if (is_type_vector(get_base_type(y->type))) {
-		x->type = make_type_vector(c->allocator, t_bool, get_base_type(y->type)->Vector.count);
+	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;
 	}
@@ -1461,7 +1462,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
 			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, get_base_type(x->type));
+				check_is_expressible(c, x, base_type(x->type));
 			}
 			return;
 		}
@@ -1491,8 +1492,8 @@ b32 check_is_castable_to(Checker *c, Operand *operand, Type *y) {
 		return true;
 
 	Type *x = operand->type;
-	Type *xb = get_base_type(x);
-	Type *yb = get_base_type(y);
+	Type *xb = base_type(x);
+	Type *yb = base_type(y);
 	if (are_types_identical(xb, yb)) {
 		return true;
 	}
@@ -1561,7 +1562,7 @@ String check_down_cast_name(Type *dst_, Type *src_) {
 	String result = {};
 	Type *dst = type_deref(dst_);
 	Type *src = type_deref(src_);
-	Type *dst_s = get_base_type(dst);
+	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];
@@ -1603,10 +1604,10 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 		b32 is_const_expr = x->mode == Addressing_Constant;
 		b32 can_convert = false;
 
-		Type *base_type = get_base_type(type);
-		if (is_const_expr && is_type_constant_type(base_type)) {
-			if (base_type->kind == Type_Basic) {
-				if (check_value_is_expressible(c, x->value, base_type, &x->value)) {
+		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;
 				}
 			}
@@ -1707,8 +1708,8 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 
 		Type *src = type_deref(x->type);
 		Type *dst = type_deref(type);
-		Type *bsrc = get_base_type(src);
-		Type *bdst = get_base_type(dst);
+		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);
@@ -1823,7 +1824,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 		ExactValue a = x->value;
 		ExactValue b = y->value;
 
-		Type *type = get_base_type(x->type);
+		Type *type = base_type(x->type);
 		GB_ASSERT(type->kind == Type_Basic);
 		if (op.kind == Token_Quo && is_type_integer(type)) {
 			op.kind = Token_QuoEq; // NOTE(bill): Hack to get division of integers
@@ -1869,7 +1870,7 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, b32 final) {
 	}
 
 	if (!final && is_type_untyped(type)) {
-		found->type = get_base_type(type);
+		found->type = base_type(type);
 		map_set(&c->info.untyped, key, *found);
 	} else {
 		ExpressionInfo old = *found;
@@ -1934,7 +1935,7 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type) {
 		return;
 	}
 
-	Type *t = get_enum_base_type(get_base_type(target_type));
+	Type *t = get_enum_base_type(base_type(target_type));
 	switch (t->kind) {
 	case Type_Basic:
 		if (operand->mode == Addressing_Constant) {
@@ -2015,7 +2016,8 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu
 		return false;
 	}
 
-	if (operand.mode == Addressing_Constant) {
+	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);
@@ -2031,7 +2033,7 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu
 			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);
+				            "Index `%s` is out of bounds range 0..<%lld", expr_str, max_count);
 				gb_string_free(expr_str);
 				return false;
 			}
@@ -2062,7 +2064,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
 	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->info, op_expr, e);
+		add_entity_use(c, op_expr, e);
 		if (e != NULL && e->kind == Entity_ImportName) {
 			String sel_name = selector->Ident.string;
 			check_op_expr = false;
@@ -2091,7 +2093,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
 				// NOTE(bill): Not really an error so don't goto error
 			}
 
-			add_entity_use(&c->info, selector, entity);
+			add_entity_use(c, selector, entity);
 		}
 	}
 	if (check_op_expr) {
@@ -2116,7 +2118,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
 	}
 
 
-	add_entity_use(&c->info, selector, entity);
+	add_entity_use(c, selector, entity);
 
 	operand->type = entity->type;
 	operand->expr = node;
@@ -2186,7 +2188,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		Operand op = {};
 		check_expr_or_type(c, &op, ce->args[0]);
 		Type *type = op.type;
-		if (op.mode != Addressing_Type && type == NULL || type == t_invalid) {
+		if ((op.mode != Addressing_Type && type == NULL) || type == t_invalid) {
 			error(ast_node_token(ce->args[0]), "Expected a type for `new`");
 			return false;
 		}
@@ -2198,7 +2200,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		Operand op = {};
 		check_expr_or_type(c, &op, ce->args[0]);
 		Type *type = op.type;
-		if (op.mode != Addressing_Type && type == NULL || type == t_invalid) {
+		if ((op.mode != Addressing_Type && type == NULL) || type == t_invalid) {
 			error(ast_node_token(ce->args[0]), "Expected a type for `new_slice`");
 			return false;
 		}
@@ -2297,7 +2299,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	case BuiltinProc_offset_of: {
 		// offset_val :: proc(Type, field) -> int
 		Operand op = {};
-		Type *type = get_base_type(check_type(c, ce->args[0]));
+		Type *type = base_type(check_type(c, ce->args[0]));
 		if (type != NULL || type == t_invalid) {
 			error(ast_node_token(ce->args[0]), "Expected a type for `offset_of`");
 			return false;
@@ -2345,8 +2347,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			return false;
 
 		Type *type = operand->type;
-		if (get_base_type(type)->kind == Type_Pointer) {
-			Type *p = get_base_type(type);
+		if (base_type(type)->kind == Type_Pointer) {
+			Type *p = base_type(type);
 			if (is_type_struct(p)) {
 				type = p->Pointer.elem;
 			}
@@ -2456,7 +2458,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		// copy :: proc(x, y: []Type) -> int
 		Type *dest_type = NULL, *src_type = NULL;
 
-		Type *d = get_base_type(operand->type);
+		Type *d = base_type(operand->type);
 		if (d->kind == Type_Slice)
 			dest_type = d->Slice.elem;
 
@@ -2464,7 +2466,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		check_expr(c, &op, ce->args[1]);
 		if (op.mode == Addressing_Invalid)
 			return false;
-		Type *s = get_base_type(op.type);
+		Type *s = base_type(op.type);
 		if (s->kind == Type_Slice)
 			src_type = s->Slice.elem;
 
@@ -2495,13 +2497,13 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	case BuiltinProc_append: {
 		// append :: proc(x : ^[]Type, y : Type) -> bool
 		Type *x_type = NULL, *y_type = NULL;
-		x_type = get_base_type(operand->type);
+		x_type = base_type(operand->type);
 
 		Operand op = {};
 		check_expr(c, &op, ce->args[1]);
 		if (op.mode == Addressing_Invalid)
 			return false;
-		y_type = get_base_type(op.type);
+		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");
@@ -2530,7 +2532,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 	case BuiltinProc_swizzle: {
 		// swizzle :: proc(v: {N}T, T...) -> {M}T
-		Type *vector_type = get_base_type(operand->type);
+		Type *vector_type = base_type(operand->type);
 		if (!is_type_vector(vector_type)) {
 			gbString type_str = type_to_string(operand->type);
 			defer (gb_string_free(type_str));
@@ -2549,7 +2551,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			check_expr(c, &op, arg);
 			if (op.mode == Addressing_Invalid)
 				return false;
-			Type *arg_type = get_base_type(op.type);
+			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;
@@ -2581,7 +2583,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	case BuiltinProc_ptr_offset: {
 		// ptr_offset :: proc(ptr: ^T, offset: int) -> ^T
 		// ^T cannot be rawptr
-		Type *ptr_type = get_base_type(operand->type);
+		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));
@@ -2602,7 +2604,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		check_expr(c, &op, offset);
 		if (op.mode == Addressing_Invalid)
 			return false;
-		Type *offset_type = get_base_type(op.type);
+		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;
@@ -2623,7 +2625,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	case BuiltinProc_ptr_sub: {
 		// ptr_sub :: proc(a, b: ^T) -> int
 		// ^T cannot be rawptr
-		Type *ptr_type = get_base_type(operand->type);
+		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));
@@ -2652,7 +2654,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			return false;
 		}
 
-		if (get_base_type(op.type) == t_rawptr) {
+		if (base_type(op.type) == t_rawptr) {
 			error(ast_node_token(call),
 			      "`rawptr` cannot have pointer arithmetic");
 			return false;
@@ -2684,7 +2686,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	case BuiltinProc_slice_ptr: {
 		// slice_ptr :: proc(a: ^T, len: int[, cap: int]) -> []T
 		// ^T cannot be rawptr
-		Type *ptr_type = get_base_type(operand->type);
+		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));
@@ -2744,7 +2746,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 	case BuiltinProc_min: {
 		// min :: proc(a, b: comparable) -> comparable
-		Type *type = get_base_type(operand->type);
+		Type *type = base_type(operand->type);
 		if (!is_type_comparable(type) || !is_type_numeric(type)) {
 			gbString type_str = type_to_string(operand->type);
 			defer (gb_string_free(type_str));
@@ -2804,7 +2806,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 	case BuiltinProc_max: {
 		// min :: proc(a, b: comparable) -> comparable
-		Type *type = get_base_type(operand->type);
+		Type *type = base_type(operand->type);
 		if (!is_type_comparable(type) || !is_type_numeric(type)) {
 			gbString type_str = type_to_string(operand->type);
 			defer (gb_string_free(type_str));
@@ -2864,7 +2866,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 	case BuiltinProc_abs: {
 		// abs :: proc(n: numeric) -> numeric
-		Type *type = get_base_type(operand->type);
+		Type *type = base_type(operand->type);
 		if (!is_type_numeric(type)) {
 			gbString type_str = type_to_string(operand->type);
 			defer (gb_string_free(type_str));
@@ -2894,7 +2896,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	} break;
 
 	case BuiltinProc_enum_to_string: {
-		Type *type = get_base_type(operand->type);
+		Type *type = base_type(operand->type);
 		if (!is_type_enum(type)) {
 			gbString type_str = type_to_string(operand->type);
 			defer (gb_string_free(type_str));
@@ -3003,7 +3005,7 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
 					if (variadic_expand) {
 						check_assignment(c, operand, arg_type, make_string("argument"), true);
 					} else {
-						arg_type = get_base_type(arg_type)->Slice.elem;
+						arg_type = base_type(arg_type)->Slice.elem;
 						check_assignment(c, operand, arg_type, make_string("argument"), true);
 					}
 				} else {
@@ -3033,7 +3035,7 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
 					}
 					Type *arg_type = sig_params[index]->type;
 					if (end_variadic && is_type_slice(arg_type)) {
-						arg_type = get_base_type(arg_type)->Slice.elem;
+						arg_type = base_type(arg_type)->Slice.elem;
 					}
 					check_assignment(c, operand, arg_type, make_string("argument"), true);
 					param_index++;
@@ -3099,7 +3101,7 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 		return builtin_procs[id].kind;
 	}
 
-	Type *proc_type = get_base_type(operand->type);
+	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);
@@ -3225,7 +3227,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 			goto error;
 		}
 
-		Type *t = get_base_type(type);
+		Type *t = base_type(type);
 		switch (t->kind) {
 		case Type_Record: {
 			if (!is_type_struct(t))
@@ -3269,7 +3271,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 						}
 
 						Entity *field = t->Record.fields[sel.index[0]];
-						add_entity_use(&c->info, kv->field, field);
+						add_entity_use(c, kv->field, field);
 
 						if (fields_visited[sel.index[0]]) {
 							error(ast_node_token(elem),
@@ -3425,7 +3427,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 
 		b32 valid = false;
 		i64 max_count = -1;
-		Type *t = get_base_type(o->type);
+		Type *t = base_type(o->type);
 		switch (t->kind) {
 		case Type_Basic:
 			if (is_type_string(t)) {
@@ -3463,7 +3465,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 			break;
 
 		case Type_Pointer: {
-			Type *bt = get_base_type(t->Pointer.elem);
+			Type *bt = base_type(t->Pointer.elem);
 			if (bt->kind == Type_Array) {
 				valid = true;
 				max_count = bt->Array.count;
@@ -3501,7 +3503,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 
 		b32 valid = false;
 		i64 max_count = -1;
-		Type *t = get_base_type(o->type);
+		Type *t = base_type(o->type);
 		switch (t->kind) {
 		case Type_Basic:
 			if (is_type_string(t)) {
@@ -3533,7 +3535,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 			break;
 
 		case Type_Pointer: {
-			Type *bt = get_base_type(t->Pointer.elem);
+			Type *bt = base_type(t->Pointer.elem);
 			if (bt->kind == Type_Array) {
 				valid = true;
 				max_count = bt->Array.count;
@@ -3591,7 +3593,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 		if (o->mode == Addressing_Invalid) {
 			goto error;
 		} else {
-			Type *t = get_base_type(o->type);
+			Type *t = base_type(o->type);
 			if (t->kind == Type_Pointer) {
 				o->mode = Addressing_Variable;
 				o->type = t->Pointer.elem;

+ 63 - 43
src/checker/stmt.cpp

@@ -17,7 +17,10 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
 		Entity *e;
 		DeclInfo *d;
 	};
-	gbArray(Delay) delayed; gb_array_init(delayed, gb_heap_allocator());
+	gbArray(Delay) delayed_const; gb_array_init(delayed_const, gb_heap_allocator());
+	gbArray(Delay) delayed_type;  gb_array_init(delayed_type,  gb_heap_allocator());
+	defer (gb_array_free(delayed_const));
+	defer (gb_array_free(delayed_type));
 
 	gb_for_array(i, stmts) {
 		AstNode *node = stmts[i];
@@ -29,6 +32,7 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
 				ExactValue v = {ExactValue_Invalid};
 
 				Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
+				e->identifier = name;
 				add_entity(c, e->scope, name, e);
 
 				DeclInfo *di = make_declaration_info(c->allocator, e->scope);
@@ -36,7 +40,7 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
 				di->init_expr = value;
 
 				Delay delay = {e, di};
-				gb_array_append(delayed, delay);
+				gb_array_append(delayed_const, delay);
 			}
 
 			isize lhs_count = gb_array_count(cd->names);
@@ -51,27 +55,24 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
 
 		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;
 			add_entity(c, c->context.scope, td->name, e);
 
 			DeclInfo *d = make_declaration_info(c->allocator, e->scope);
 			d->type_expr = td->type;
 
 			Delay delay = {e, d};
-			gb_array_append(delayed, delay);
+			gb_array_append(delayed_type, delay);
 		case_end;
 		}
 	}
 
-	auto check_scope_entity = [](Checker *c, gbArray(Delay) delayed, EntityKind kind) {
-		gb_for_array(i, delayed) {
-			auto delay = delayed[i];
-			if (delay.e->kind == kind) {
-				check_entity_decl(c, delay.e, delay.d, NULL);
-			}
-		}
-	};
-	check_scope_entity(c, delayed, Entity_TypeName);
-	check_scope_entity(c, delayed, Entity_Constant);
+	gb_for_array(i, delayed_type) {
+		check_entity_decl(c, delayed_type[i].e, delayed_type[i].d, NULL);
+	}
+	gb_for_array(i, delayed_const) {
+		check_entity_decl(c, delayed_const[i].e, delayed_const[i].d, NULL);
+	}
 
 	b32 ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
 	u32 f = flags & (~Stmt_FallthroughAllowed);
@@ -227,7 +228,7 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
 
 	// NOTE(bill): Ignore assignments to `_`
 	if (node->kind == AstNode_Ident &&
-	    node->Ident.string == make_string("_")) {
+	    node->Ident.string == "_") {
 		add_entity_definition(&c->info, node, NULL);
 		check_assignment(c, op_a, NULL, make_string("assignment to `_` identifier"));
 		if (op_a->mode == Addressing_Invalid)
@@ -301,8 +302,9 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
 		}
 
 
-		if (e->type == NULL)
+		if (e->type == NULL) {
 			e->type = t_invalid;
+		}
 		return NULL;
 	}
 
@@ -321,8 +323,9 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
 	}
 
 	check_assignment(c, operand, e->type, context_name);
-	if (operand->mode == Addressing_Invalid)
+	if (operand->mode == Addressing_Invalid) {
 		return NULL;
+	}
 
 	return e->type;
 }
@@ -448,9 +451,9 @@ void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def, Cycle
 		gb_array_free(local_cycle_checker.path);
 	});
 
-	Type *base_type = check_type(c, type_expr, named, cycle_checker_add(cycle_checker, e));
-	named->Named.base = base_type;
-	named->Named.base = get_base_type(named->Named.base);
+	Type *bt = check_type(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));
 	}
@@ -474,7 +477,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
 			if (!e->Variable.anonymous)
 				continue;
 			String name = e->token.string;
-			Type *t = get_base_type(type_deref(e->type));
+			Type *t = base_type(type_deref(e->type));
 			if (is_type_struct(t) || is_type_raw_union(t)) {
 				Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
 				GB_ASSERT(found != NULL);
@@ -524,8 +527,8 @@ b32 are_signatures_similar_enough(Type *a_, Type *b_) {
 		return false;
 	}
 	for (isize i = 0; i < a->param_count; i++) {
-		Type *x = get_base_type(a->params->Tuple.variables[i]->type);
-		Type *y = get_base_type(b->params->Tuple.variables[i]->type);
+		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;
 		}
@@ -535,8 +538,8 @@ b32 are_signatures_similar_enough(Type *a_, Type *b_) {
 		}
 	}
 	for (isize i = 0; i < a->result_count; i++) {
-		Type *x = get_base_type(a->results->Tuple.variables[i]->type);
-		Type *y = get_base_type(b->results->Tuple.variables[i]->type);
+		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;
 		}
@@ -559,15 +562,13 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 	defer (check_close_scope(c));
 	check_procedure_type(c, proc_type, pd->type);
 
-
-	b32 is_foreign   = (pd->tags & ProcTag_foreign)   != 0;
-	b32 is_link_name = (pd->tags & ProcTag_link_name) != 0;
-	b32 is_inline    = (pd->tags & ProcTag_inline)    != 0;
-	b32 is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
-
+	b32 is_foreign      = (pd->tags & ProcTag_foreign)   != 0;
+	b32 is_link_name    = (pd->tags & ProcTag_link_name) != 0;
+	b32 is_inline       = (pd->tags & ProcTag_inline)    != 0;
+	b32 is_no_inline    = (pd->tags & ProcTag_no_inline) != 0;
 
 	if ((d->scope->is_file || d->scope->is_global) &&
-	    e->token.string == make_string("main")) {
+	    e->token.string == "main") {
 		if (proc_type != NULL) {
 			auto *pt = &proc_type->Proc;
 			if (pt->param_count != 0 ||
@@ -595,7 +596,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 		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);
+		check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags);
 	}
 
 	if (is_foreign) {
@@ -610,8 +611,8 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 		if (found) {
 			Entity *f = *found;
 			TokenPos pos = f->token.pos;
-			Type *this_type = get_base_type(e->type);
-			Type *other_type = get_base_type(f->type);
+			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"
@@ -639,7 +640,6 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 			map_set(fp, key, e);
 		}
 	}
-
 }
 
 void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
@@ -674,7 +674,7 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
 	}
 
 	AstNodeArray inits;
-	gb_array_init(inits, c->allocator);
+	gb_array_init_reserve(inits, c->allocator, 1);
 	gb_array_append(inits, init_expr);
 	check_init_variables(c, entities, entity_count, inits, make_string("variable declaration"));
 }
@@ -701,9 +701,10 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, Cyc
 		check_proc_decl(c, e, d);
 		return;
 	}
-	Scope *prev = c->context.scope;
+	auto prev = c->context;
 	c->context.scope = d->scope;
-	defer (c->context.scope = prev);
+	c->context.decl  = d;
+	defer (c->context = prev);
 
 	switch (e->kind) {
 	case Entity_Constant:
@@ -790,6 +791,24 @@ void check_var_decl_node(Checker *c, AstNode *node) {
 
 
 void check_stmt(Checker *c, AstNode *node, u32 flags) {
+	u32 prev_stmt_state_flags = c->context.stmt_state_flags;
+	defer (c->context.stmt_state_flags = prev_stmt_state_flags);
+
+	if (node->stmt_state_flags != 0) {
+		u32 in = node->stmt_state_flags;
+		u32 out = c->context.stmt_state_flags;
+		defer (c->context.stmt_state_flags = out);
+
+		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;
+		}
+	}
+
+
 	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
 	switch (node->kind) {
 	case_ast_node(_, EmptyStmt, node); case_end;
@@ -1180,7 +1199,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			      "Expected a pointer to a union for this type match expression, got `%s`", str);
 			break;
 		}
-		Type *base_union = get_base_type(type_deref(x.type));
+		Type *base_union = base_type(type_deref(x.type));
 
 
 		// NOTE(bill): Check for multiple defaults
@@ -1274,7 +1293,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tag_ptr_type);
 				tag_var->Variable.used = true;
 				add_entity(c, c->context.scope, ms->var, tag_var);
-				add_entity_use(&c->info, ms->var, tag_var);
+				add_entity_use(c, ms->var, tag_var);
 			}
 			check_stmt_list(c, cc->stmts, mod_flags);
 			check_close_scope(c);
@@ -1342,7 +1361,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 
 			switch (e->kind) {
 			case Entity_TypeName: {
-				Type *t = get_base_type(e->type);
+				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];
@@ -1404,7 +1423,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				break;
 
 			case Entity_Variable: {
-				Type *t = get_base_type(type_deref(e->type));
+				Type *t = base_type(type_deref(e->type));
 				if (is_type_struct(t) || is_type_raw_union(t)) {
 					Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
 					GB_ASSERT(found != NULL);
@@ -1444,7 +1463,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				ast_node(i, Ident, item);
 				String name = i->string;
 				Entity *e = scope_lookup_entity(c->context.scope, name);
-				Type *t = get_base_type(type_deref(e->type));
+				Type *t = base_type(type_deref(e->type));
 				if (is_type_struct(t) || is_type_raw_union(t)) {
 					Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
 					GB_ASSERT(found != NULL);
@@ -1511,6 +1530,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		// 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;
 		add_entity(c, c->context.scope, pd->name, e);
 
 		DeclInfo *d = make_declaration_info(c->allocator, e->scope);

+ 100 - 79
src/checker/type.cpp

@@ -92,8 +92,8 @@ enum TypeRecordKind {
 };
 
 struct Type {
-	u32 flags; // See parser.cpp `enum TypeFlag`
 	TypeKind kind;
+	u32 flags; // See parser.cpp `enum TypeFlag`
 	union {
 		BasicType Basic;
 		struct {
@@ -157,7 +157,7 @@ struct Type {
 
 gbString type_to_string(Type *type, gbAllocator a = gb_heap_allocator());
 
-Type *get_base_type(Type *t) {
+Type *base_type(Type *t) {
 	for (;;) {
 		if (t == NULL || t->kind != Type_Named) {
 			break;
@@ -259,7 +259,7 @@ Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_coun
 		}
 		GB_ASSERT(params != NULL && params->kind == Type_Tuple);
 		Entity *e = params->Tuple.variables[param_count-1];
-		if (get_base_type(e->type)->kind != Type_Slice) {
+		if (base_type(e->type)->kind != Type_Slice) {
 			// NOTE(bill): For custom calling convention
 			GB_PANIC("variadic parameter must be of type slice");
 		}
@@ -277,7 +277,7 @@ Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_coun
 
 Type *type_deref(Type *t) {
 	if (t != NULL) {
-		Type *bt = get_base_type(t);
+		Type *bt = base_type(t);
 		if (bt == NULL)
 			return NULL;
 		if (bt != NULL && bt->kind == Type_Pointer)
@@ -289,34 +289,34 @@ Type *type_deref(Type *t) {
 
 #define STR_LIT(x) {cast(u8 *)(x), gb_size_of(x)-1}
 gb_global Type basic_types[] = {
-	{0, Type_Basic, {Basic_Invalid,        0,                                      STR_LIT("invalid type")}},
-	{0, Type_Basic, {Basic_bool,           BasicFlag_Boolean,                      STR_LIT("bool")}},
-	{0, Type_Basic, {Basic_i8,             BasicFlag_Integer,                      STR_LIT("i8")}},
-	{0, Type_Basic, {Basic_u8,             BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u8")}},
-	{0, Type_Basic, {Basic_i16,            BasicFlag_Integer,                      STR_LIT("i16")}},
-	{0, Type_Basic, {Basic_u16,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u16")}},
-	{0, Type_Basic, {Basic_i32,            BasicFlag_Integer,                      STR_LIT("i32")}},
-	{0, Type_Basic, {Basic_u32,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u32")}},
-	{0, Type_Basic, {Basic_i64,            BasicFlag_Integer,                      STR_LIT("i64")}},
-	{0, Type_Basic, {Basic_u64,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u64")}},
-	{0, Type_Basic, {Basic_f32,            BasicFlag_Float,                        STR_LIT("f32")}},
-	{0, Type_Basic, {Basic_f64,            BasicFlag_Float,                        STR_LIT("f64")}},
-	{0, Type_Basic, {Basic_int,            BasicFlag_Integer,                      STR_LIT("int")}},
-	{0, Type_Basic, {Basic_uint,           BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("uint")}},
-	{0, Type_Basic, {Basic_rawptr,         BasicFlag_Pointer,                      STR_LIT("rawptr")}},
-	{0, Type_Basic, {Basic_string,         BasicFlag_String,                       STR_LIT("string")}},
-	{0, Type_Basic, {Basic_any,            0,                                      STR_LIT("any")}},
-	{0, Type_Basic, {Basic_UntypedBool,    BasicFlag_Boolean | BasicFlag_Untyped,  STR_LIT("untyped bool")}},
-	{0, Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped,  STR_LIT("untyped integer")}},
-	{0, Type_Basic, {Basic_UntypedFloat,   BasicFlag_Float   | BasicFlag_Untyped,  STR_LIT("untyped float")}},
-	{0, Type_Basic, {Basic_UntypedPointer, BasicFlag_Pointer | BasicFlag_Untyped,  STR_LIT("untyped pointer")}},
-	{0, Type_Basic, {Basic_UntypedString,  BasicFlag_String  | BasicFlag_Untyped,  STR_LIT("untyped string")}},
-	{0, Type_Basic, {Basic_UntypedRune,    BasicFlag_Integer | BasicFlag_Untyped,  STR_LIT("untyped rune")}},
+	{Type_Basic, 0, {Basic_Invalid,        0,                                      STR_LIT("invalid type")}},
+	{Type_Basic, 0, {Basic_bool,           BasicFlag_Boolean,                      STR_LIT("bool")}},
+	{Type_Basic, 0, {Basic_i8,             BasicFlag_Integer,                      STR_LIT("i8")}},
+	{Type_Basic, 0, {Basic_u8,             BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u8")}},
+	{Type_Basic, 0, {Basic_i16,            BasicFlag_Integer,                      STR_LIT("i16")}},
+	{Type_Basic, 0, {Basic_u16,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u16")}},
+	{Type_Basic, 0, {Basic_i32,            BasicFlag_Integer,                      STR_LIT("i32")}},
+	{Type_Basic, 0, {Basic_u32,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u32")}},
+	{Type_Basic, 0, {Basic_i64,            BasicFlag_Integer,                      STR_LIT("i64")}},
+	{Type_Basic, 0, {Basic_u64,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u64")}},
+	{Type_Basic, 0, {Basic_f32,            BasicFlag_Float,                        STR_LIT("f32")}},
+	{Type_Basic, 0, {Basic_f64,            BasicFlag_Float,                        STR_LIT("f64")}},
+	{Type_Basic, 0, {Basic_int,            BasicFlag_Integer,                      STR_LIT("int")}},
+	{Type_Basic, 0, {Basic_uint,           BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("uint")}},
+	{Type_Basic, 0, {Basic_rawptr,         BasicFlag_Pointer,                      STR_LIT("rawptr")}},
+	{Type_Basic, 0, {Basic_string,         BasicFlag_String,                       STR_LIT("string")}},
+	{Type_Basic, 0, {Basic_any,            0,                                      STR_LIT("any")}},
+	{Type_Basic, 0, {Basic_UntypedBool,    BasicFlag_Boolean | BasicFlag_Untyped,  STR_LIT("untyped bool")}},
+	{Type_Basic, 0, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped,  STR_LIT("untyped integer")}},
+	{Type_Basic, 0, {Basic_UntypedFloat,   BasicFlag_Float   | BasicFlag_Untyped,  STR_LIT("untyped float")}},
+	{Type_Basic, 0, {Basic_UntypedPointer, BasicFlag_Pointer | BasicFlag_Untyped,  STR_LIT("untyped pointer")}},
+	{Type_Basic, 0, {Basic_UntypedString,  BasicFlag_String  | BasicFlag_Untyped,  STR_LIT("untyped string")}},
+	{Type_Basic, 0, {Basic_UntypedRune,    BasicFlag_Integer | BasicFlag_Untyped,  STR_LIT("untyped rune")}},
 };
 
 gb_global Type basic_type_aliases[] = {
-	{0, Type_Basic, {Basic_byte, BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("byte")}},
-	{0, Type_Basic, {Basic_rune, BasicFlag_Integer,                      STR_LIT("rune")}},
+	{Type_Basic, 0, {Basic_byte, BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("byte")}},
+	{Type_Basic, 0, {Basic_rune, BasicFlag_Integer,                      STR_LIT("rune")}},
 };
 
 gb_global Type *t_invalid         = &basic_types[Basic_Invalid];
@@ -342,8 +342,8 @@ gb_global Type *t_untyped_float   = &basic_types[Basic_UntypedFloat];
 gb_global Type *t_untyped_pointer = &basic_types[Basic_UntypedPointer];
 gb_global Type *t_untyped_string  = &basic_types[Basic_UntypedString];
 gb_global Type *t_untyped_rune    = &basic_types[Basic_UntypedRune];
-gb_global Type *t_byte            = &basic_type_aliases[Basic_byte];
-gb_global Type *t_rune            = &basic_type_aliases[Basic_rune];
+gb_global Type *t_byte            = &basic_type_aliases[0];
+gb_global Type *t_rune            = &basic_type_aliases[1];
 
 
 gb_global Type *t_type_info            = NULL;
@@ -377,140 +377,161 @@ gb_global Type *t_context_ptr          = NULL;
 
 
 b32 is_type_named(Type *t) {
-	if (t->kind == Type_Basic)
+	if (t->kind == Type_Basic) {
 		return true;
+	}
 	return t->kind == Type_Named;
 }
 b32 is_type_boolean(Type *t) {
-	t = get_base_type(t);
-	if (t->kind == Type_Basic)
+	t = base_type(t);
+	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Boolean) != 0;
+	}
 	return false;
 }
 b32 is_type_integer(Type *t) {
-	t = get_base_type(t);
-	if (t->kind == Type_Basic)
+	t = base_type(t);
+	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Integer) != 0;
+	}
 	return false;
 }
 b32 is_type_unsigned(Type *t) {
-	t = get_base_type(t);
-	if (t->kind == Type_Basic)
+	t = base_type(t);
+	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Unsigned) != 0;
+	}
 	return false;
 }
 b32 is_type_numeric(Type *t) {
-	t = get_base_type(t);
-	if (t->kind == Type_Basic)
+	t = base_type(t);
+	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Numeric) != 0;
-	if (t->kind == Type_Vector)
+	}
+	if (t->kind == Type_Vector) {
 		return is_type_numeric(t->Vector.elem);
+	}
 	return false;
 }
 b32 is_type_string(Type *t) {
-	t = get_base_type(t);
-	if (t->kind == Type_Basic)
+	t = base_type(t);
+	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_String) != 0;
+	}
 	return false;
 }
 b32 is_type_typed(Type *t) {
-	t = get_base_type(t);
-	if (t->kind == Type_Basic)
+	t = base_type(t);
+	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Untyped) == 0;
+	}
 	return true;
 }
 b32 is_type_untyped(Type *t) {
-	t = get_base_type(t);
-	if (t->kind == Type_Basic)
+	t = base_type(t);
+	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Untyped) != 0;
+	}
 	return false;
 }
 b32 is_type_ordered(Type *t) {
-	t = get_base_type(t);
-	if (t->kind == Type_Basic)
+	t = base_type(t);
+	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Ordered) != 0;
-	if (t->kind == Type_Pointer)
+	}
+	if (t->kind == Type_Pointer) {
 		return true;
+	}
 	return false;
 }
 b32 is_type_constant_type(Type *t) {
-	t = get_base_type(t);
-	if (t->kind == Type_Basic)
+	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;
 }
 b32 is_type_float(Type *t) {
-	t = get_base_type(t);
-	if (t->kind == Type_Basic)
+	t = base_type(t);
+	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Float) != 0;
+	}
 	return false;
 }
 b32 is_type_pointer(Type *t) {
-	t = get_base_type(t);
-	if (t->kind == Type_Basic)
+	t = base_type(t);
+	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Pointer) != 0;
+	}
 	return t->kind == Type_Pointer;
 }
 
 b32 is_type_int_or_uint(Type *t) {
-	if (t->kind == Type_Basic)
+	if (t->kind == Type_Basic) {
 		return (t->Basic.kind == Basic_int) || (t->Basic.kind == Basic_uint);
+	}
 	return false;
 }
 b32 is_type_rawptr(Type *t) {
-	if (t->kind == Type_Basic)
+	if (t->kind == Type_Basic) {
 		return t->Basic.kind == Basic_rawptr;
+	}
 	return false;
 }
 b32 is_type_u8(Type *t) {
-	if (t->kind == Type_Basic)
+	if (t->kind == Type_Basic) {
 		return t->Basic.kind == Basic_u8;
+	}
 	return false;
 }
 b32 is_type_slice(Type *t) {
-	t = get_base_type(t);
+	t = base_type(t);
 	return t->kind == Type_Slice;
 }
 b32 is_type_u8_slice(Type *t) {
-	t = get_base_type(t);
-	if (t->kind == Type_Slice)
+	t = base_type(t);
+	if (t->kind == Type_Slice) {
 		return is_type_u8(t->Slice.elem);
+	}
 	return false;
 }
 b32 is_type_vector(Type *t) {
 	return t->kind == Type_Vector;
 }
 b32 is_type_proc(Type *t) {
-	t = get_base_type(t);
+	t = base_type(t);
 	return t->kind == Type_Proc;
 }
 Type *base_vector_type(Type *t) {
 	if (is_type_vector(t)) {
-		return t->Vector.elem;
+		return base_type(t)->Vector.elem;
 	}
 	return t;
 }
 
 
 b32 is_type_enum(Type *t) {
-	t = get_base_type(t);
+	t = base_type(t);
 	return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum);
 }
 b32 is_type_struct(Type *t) {
-	t = get_base_type(t);
+	t = base_type(t);
 	return (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct);
 }
 b32 is_type_union(Type *t) {
-	t = get_base_type(t);
+	t = base_type(t);
 	return (t->kind == Type_Record && t->Record.kind == TypeRecord_Union);
 }
 b32 is_type_raw_union(Type *t) {
-	t = get_base_type(t);
+	t = base_type(t);
 	return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion);
 }
 
 Type *get_enum_base_type(Type *t) {
-	Type *bt = get_base_type(t);
+	Type *bt = base_type(t);
 	if (is_type_enum(bt)) {
 		return bt->Record.enum_base;
 	}
@@ -518,14 +539,14 @@ Type *get_enum_base_type(Type *t) {
 }
 
 b32 is_type_any(Type *t) {
-	t = get_base_type(t);
+	t = base_type(t);
 	return (t->kind == Type_Basic && t->Basic.kind == Basic_any);
 }
 
 
 
 b32 is_type_comparable(Type *t) {
-	t = get_base_type(t);
+	t = base_type(t);
 	switch (t->kind) {
 	case Type_Basic:
 		return true;
@@ -722,13 +743,13 @@ gb_global Entity *entity__slice_capacity = NULL;
 Selection lookup_field(gbAllocator a, Type *type_, String field_name, b32 is_type, Selection sel = empty_selection) {
 	GB_ASSERT(type_ != NULL);
 
-	if (field_name == make_string("_")) {
+	if (field_name == "_") {
 		return empty_selection;
 	}
 
 	Type *type = type_deref(type_);
 	b32 is_ptr = type != type_;
-	type = get_base_type(type);
+	type = base_type(type);
 
 	if (type->kind == Type_Basic) {
 		switch (type->Basic.kind) {
@@ -850,13 +871,13 @@ Selection lookup_field(gbAllocator a, Type *type_, String field_name, b32 is_typ
 		}
 
 		if (is_type_enum(type)) {
-			if (field_name == make_string("count")) {
+			if (field_name == "count") {
 				sel.entity = type->Record.enum_count;
 				return sel;
-			} else if (field_name == make_string("min_value")) {
+			} else if (field_name == "min_value") {
 				sel.entity = type->Record.min_value;
 				return sel;
-			} else if (field_name == make_string("max_value")) {
+			} else if (field_name == "max_value") {
 				sel.entity = type->Record.max_value;
 				return sel;
 			}
@@ -907,7 +928,7 @@ i64 align_formula(i64 size, i64 align) {
 }
 
 i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
-	t = get_base_type(t);
+	t = base_type(t);
 
 	switch (t->kind) {
 	case Type_Array:
@@ -996,7 +1017,7 @@ b32 type_set_offsets(BaseTypeSizes s, gbAllocator allocator, Type *t) {
 }
 
 i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
-	t = get_base_type(t);
+	t = base_type(t);
 
 	switch (t->kind) {
 	case Type_Basic: {
@@ -1107,7 +1128,7 @@ i64 type_offset_of_from_selection(BaseTypeSizes s, gbAllocator allocator, Type *
 	i64 offset = 0;
 	for (isize i = 0; i < gb_array_count(sel.index); i++) {
 		isize index = sel.index[i];
-		t = get_base_type(t);
+		t = base_type(t);
 		if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) {
 			type_set_offsets(s, allocator, t);
 			GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));

+ 24 - 12
src/codegen/codegen.cpp

@@ -19,7 +19,7 @@ b32 ssa_gen_init(ssaGen *s, Checker *c) {
 	s->module.generate_debug_info = false;
 
 	// TODO(bill): generate appropriate output name
-	isize pos = string_extension_position(c->parser->init_fullpath);
+	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;
@@ -59,18 +59,15 @@ String ssa_mangle_name(ssaGen *s, String path, String name) {
 	isize new_name_len = gb_snprintf(
 		cast(char *)new_name, max_len,
 		"%.*s-%u.%.*s",
-		base_len, base,
+		cast(int)base_len, base,
 		file->id,
 		LIT(name));
 
 	return make_string(new_name, new_name_len-1);
 }
 
+
 void ssa_gen_tree(ssaGen *s) {
-	struct ssaGlobalVariable {
-		ssaValue *var, *init;
-		DeclInfo *decl;
-	};
 
 	ssaModule *m = &s->module;
 	CheckerInfo *info = m->info;
@@ -87,31 +84,46 @@ void ssa_gen_tree(ssaGen *s) {
 	}
 
 	isize global_variable_max_count = 0;
+	Entity *entry_point = NULL;
 
 	gb_for_array(i, info->entities.entries) {
 		auto *entry = &info->entities.entries[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 && name == "main") {
+				entry_point = e;
+			}
 		}
 	}
 
-
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
-	defer (gb_temp_arena_memory_end(tmp));
-
+	struct ssaGlobalVariable {
+		ssaValue *var, *init;
+		DeclInfo *decl;
+	};
 	gbArray(ssaGlobalVariable) global_variables;
 	gb_array_init_reserve(global_variables, m->tmp_allocator, global_variable_max_count);
 
-
+	auto min_dep_map = generate_minimum_dependency_map(info, entry_point);
+	defer (map_destroy(&min_dep_map));
 
 	gb_for_array(i, info->entities.entries) {
 		auto *entry = &info->entities.entries[i];
 		Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
 		String name = e->token.string;
-
 		DeclInfo *decl = entry->value;
 		Scope *scope = e->scope;
+
+		if (entry_point != NULL) {
+			auto found = map_get(&min_dep_map, hash_pointer(e));
+			if (found == 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);
 		}

+ 10 - 10
src/codegen/print_llvm.cpp

@@ -194,7 +194,7 @@ void ssa_print_type(ssaFileBuffer *f, ssaModule *m, Type *t) {
 					ssa_fprintf(f, ", ");
 				}
 				Type *ft = t->Record.fields[i]->type;
-				Type *bft = get_base_type(ft);
+				Type *bft = base_type(ft);
 				if (!is_type_struct(bft)) {
 					ft = bft;
 				}
@@ -229,7 +229,7 @@ void ssa_print_type(ssaFileBuffer *f, ssaModule *m, Type *t) {
 			ssa_print_encoded_local(f, *name);
 			// ssa_print_encoded_local(f, t->Named.name);
 		} else {
-			ssa_print_type(f, m, get_base_type(t));
+			ssa_print_type(f, m, base_type(t));
 		}
 		break;
 	case Type_Tuple:
@@ -264,7 +264,7 @@ void ssa_print_type(ssaFileBuffer *f, ssaModule *m, Type *t) {
 }
 
 void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Type *type) {
-	type = get_base_type(type);
+	type = base_type(type);
 	if (is_type_float(type)) {
 		value = exact_value_to_float(value);
 	} else if (is_type_integer(type)) {
@@ -275,7 +275,7 @@ void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Typ
 
 	switch (value.kind) {
 	case ExactValue_Bool:
-		ssa_fprintf(f, (value.value_bool ? "true" : "false"));
+		ssa_fprintf(f, "%s", (value.value_bool ? "true" : "false"));
 		break;
 	case ExactValue_String: {
 		ssa_fprintf(f, "c\"");
@@ -528,10 +528,10 @@ void ssa_print_instr(ssaFileBuffer *f, ssaModule *m, ssaValue *value) {
 
 	case ssaInstr_BinaryOp: {
 		auto *bo = &value->Instr.BinaryOp;
-		Type *type = get_base_type(ssa_type(bo->left));
+		Type *type = base_type(ssa_type(bo->left));
 		Type *elem_type = type;
 		while (elem_type->kind == Type_Vector) {
-			elem_type = get_base_type(elem_type->Vector.elem);
+			elem_type = base_type(elem_type->Vector.elem);
 		}
 
 		ssa_fprintf(f, "%%%d = ", value->id);
@@ -651,7 +651,7 @@ void ssa_print_instr(ssaFileBuffer *f, ssaModule *m, ssaValue *value) {
 
 		ssa_fprintf(f, "(");
 		if (call->arg_count > 0) {
-			Type *proc_type = get_base_type(ssa_type(call->value));
+			Type *proc_type = base_type(ssa_type(call->value));
 			GB_ASSERT(proc_type->kind == Type_Proc);
 			auto *params = &proc_type->Proc.params->Tuple;
 			for (isize i = 0; i < call->arg_count; i++) {
@@ -848,13 +848,13 @@ void ssa_print_proc(ssaFileBuffer *f, ssaModule *m, ssaProcedure *proc) {
 
 void ssa_print_type_name(ssaFileBuffer *f, ssaModule *m, ssaValue *v) {
 	GB_ASSERT(v->kind == ssaValue_TypeName);
-	Type *base_type = get_base_type(ssa_type(v));
-	if (!is_type_struct(base_type) && !is_type_union(base_type)) {
+	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, get_base_type(v->TypeName.type));
+	ssa_print_type(f, m, base_type(v->TypeName.type));
 	ssa_fprintf(f, "\n");
 }
 

+ 81 - 68
src/codegen/ssa.cpp

@@ -497,7 +497,7 @@ Type *ssa_type(ssaInstr *instr) {
 	case ssaInstr_Select:
 		return ssa_type(instr->Select.true_value);
 	case ssaInstr_Call: {
-		Type *pt = get_base_type(instr->Call.type);
+		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;
@@ -507,7 +507,7 @@ Type *ssa_type(ssaInstr *instr) {
 	} break;
 	case ssaInstr_ExtractElement: {
 		Type *vt = ssa_type(instr->ExtractElement.vector);
-		Type *bt = base_vector_type(get_base_type(vt));
+		Type *bt = base_vector_type(vt);
 		GB_ASSERT(!is_type_vector(bt));
 		return bt;
 	} break;
@@ -768,7 +768,7 @@ ssaValue *ssa_make_instr_shuffle_vector(ssaProcedure *p, ssaValue *vector, i32 *
 	v->Instr.ShuffleVector.indices     = indices;
 	v->Instr.ShuffleVector.index_count = index_count;
 
-	Type *vt = get_base_type(ssa_type(vector));
+	Type *vt = base_type(ssa_type(vector));
 	v->Instr.ShuffleVector.type = make_type_vector(p->module->allocator, vt->Vector.elem, index_count);
 
 	return v;
@@ -950,7 +950,7 @@ ssaValue *ssa_add_param(ssaProcedure *proc, Entity *e) {
 
 
 ssaValue *ssa_emit_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count) {
-	Type *pt = get_base_type(ssa_type(value));
+	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));
@@ -1077,7 +1077,7 @@ ssaValue *ssa_lvalue_store(ssaProcedure *proc, ssaAddr lval, ssaValue *value) {
 	if (lval.addr != NULL) {
 		if (lval.is_vector) {
 			ssaValue *v = ssa_emit_load(proc, lval.addr);
-			Type *elem_type = get_base_type(ssa_type(v))->Vector.elem;
+			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, lval.index));
 			return ssa_emit_store(proc, lval.addr, out);
@@ -1095,7 +1095,7 @@ ssaValue *ssa_lvalue_load(ssaProcedure *proc, ssaAddr lval) {
 			return ssa_emit(proc, ssa_make_instr_extract_element(proc, v, lval.index));
 		}
 		// NOTE(bill): Imported procedures don't require a load as they are pointers
-		Type *t = get_base_type(ssa_type(lval.addr));
+		Type *t = base_type(ssa_type(lval.addr));
 		if (t->kind == Type_Proc) {
 			return lval.addr;
 		}
@@ -1214,8 +1214,8 @@ ssaValue *ssa_emit_arith(ssaProcedure *proc, Token op, ssaValue *left, ssaValue
 }
 
 ssaValue *ssa_emit_comp(ssaProcedure *proc, Token op, ssaValue *left, ssaValue *right) {
-	Type *a = get_base_type(ssa_type(left));
-	Type *b = get_base_type(ssa_type(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
@@ -1280,7 +1280,7 @@ ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, S
 			e = ssa_emit_load(proc, e);
 			e = ssa_emit_ptr_offset(proc, e, v_zero);
 		}
-		type = get_base_type(type);
+		type = base_type(type);
 
 
 		if (is_type_raw_union(type)) {
@@ -1329,7 +1329,7 @@ ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Se
 			e = ssa_emit_load(proc, e);
 			e = ssa_emit_ptr_offset(proc, e, v_zero);
 		}
-		type = get_base_type(type);
+		type = base_type(type);
 
 
 		if (is_type_raw_union(type)) {
@@ -1465,20 +1465,20 @@ ssaValue *ssa_add_local_slice(ssaProcedure *proc, Type *slice_type, ssaValue *ba
 	// TODO(bill): array bounds checking for slice creation
 	// TODO(bill): check that low < high <= max
 	gbAllocator a = proc->module->allocator;
-	Type *base_type = get_base_type(ssa_type(base));
+	Type *bt = base_type(ssa_type(base));
 
 	if (low == NULL) {
 		low = v_zero;
 	}
 	if (high == NULL) {
-		switch (base_type->kind) {
+		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 (base_type->kind) {
+		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;
@@ -1491,7 +1491,7 @@ ssaValue *ssa_add_local_slice(ssaProcedure *proc, Type *slice_type, ssaValue *ba
 	ssaValue *cap = ssa_emit_arith(proc, op_sub, max,  low, t_int);
 
 	ssaValue *elem = NULL;
-	switch (base_type->kind) {
+	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;
@@ -1565,8 +1565,8 @@ ssaValue *ssa_emit_global_string(ssaProcedure *proc, String str) {
 String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
 	Type *prev_src = src;
 	// Type *prev_dst = dst;
-	src = get_base_type(type_deref(src));
-	// dst = get_base_type(type_deref(dst));
+	src = base_type(type_deref(src));
+	// dst = base_type(type_deref(dst));
 	b32 src_is_ptr = src != prev_src;
 	// b32 dst_is_ptr = dst != prev_dst;
 
@@ -1599,8 +1599,8 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg
 	}
 
 
-	Type *src = get_enum_base_type(get_base_type(src_type));
-	Type *dst = get_enum_base_type(get_base_type(t));
+	Type *src = get_enum_base_type(base_type(src_type));
+	Type *dst = get_enum_base_type(base_type(t));
 
 	if (value->kind == ssaValue_Constant) {
 		if (is_type_any(dst)) {
@@ -1718,7 +1718,7 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg
 	// NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's
 	// subtype polymorphism casting
 	if (is_argument) {
-		Type *sb = get_base_type(type_deref(src));
+		Type *sb = base_type(type_deref(src));
 		b32 src_is_ptr = src != sb;
 		if (is_type_struct(sb)) {
 			String field_name = lookup_polymorphic_field(proc->module->info, t, src);
@@ -1840,8 +1840,8 @@ ssaValue *ssa_emit_transmute(ssaProcedure *proc, ssaValue *value, Type *t) {
 		return value;
 	}
 
-	Type *src = get_base_type(src_type);
-	Type *dst = get_base_type(t);
+	Type *src = base_type(src_type);
+	Type *dst = base_type(t);
 	if (are_types_identical(t, src_type))
 		return value;
 
@@ -2080,17 +2080,17 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 	case_ast_node(cl, CompoundLit, expr);
 		ssa_emit_comment(proc, make_string("CompoundLit"));
 		Type *type = type_of_expr(proc->module->info, expr);
-		Type *base_type = get_base_type(type);
+		Type *bt = base_type(type);
 		ssaValue *v = ssa_add_local_generated(proc, type);
 
 		Type *et = NULL;
-		switch (base_type->kind) {
-		case Type_Vector: et = base_type->Vector.elem; break;
-		case Type_Array:  et = base_type->Array.elem;  break;
-		case Type_Slice:  et = base_type->Slice.elem;  break;
+		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 (base_type->kind) {
+		switch (bt->kind) {
 		default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
 
 		case Type_Vector: {
@@ -2105,8 +2105,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 				ssaValue *i = ssa_make_const_int(proc->module->allocator, index);
 				result = ssa_emit(proc, ssa_make_instr_insert_element(proc, result, ev, i));
 			}
-			if (index == 1 && base_type->Vector.count > 1) {
-				isize index_count = base_type->Vector.count;
+			if (index == 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;
@@ -2120,8 +2120,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 		} break;
 
 		case Type_Record: {
-			GB_ASSERT(is_type_struct(base_type));
-			auto *st = &base_type->Record;
+			GB_ASSERT(is_type_struct(bt));
+			auto *st = &bt->Record;
 			if (cl->elems != NULL && gb_array_count(cl->elems) > 0) {
 				gb_for_array(field_index, cl->elems) {
 					isize index = field_index;
@@ -2131,11 +2131,11 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 
 					if (elem->kind == AstNode_FieldValue) {
 						ast_node(kv, FieldValue, elem);
-						Selection sel = lookup_field(proc->module->allocator, base_type, kv->field->Ident.string, false);
+						Selection sel = lookup_field(proc->module->allocator, bt, kv->field->Ident.string, false);
 						index = sel.index[0];
 						field_expr = ssa_build_expr(proc, kv->value);
 					} else {
-						Selection sel = lookup_field(proc->module->allocator, base_type, st->fields_in_src_order[field_index]->token.string, false);
+						Selection sel = lookup_field(proc->module->allocator, bt, st->fields_in_src_order[field_index]->token.string, false);
 						index = sel.index[0];
 						field_expr = ssa_build_expr(proc, elem);
 					}
@@ -2164,7 +2164,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 		} break;
 		case Type_Slice: {
 			i64 count = gb_array_count(cl->elems);
-			Type *elem_type = base_type->Slice.elem;
+			Type *elem_type = bt->Slice.elem;
 			Type *elem_ptr_type = make_type_pointer(proc->module->allocator, elem_type);
 			ssaValue *array = ssa_add_local_generated(proc, make_type_array(proc->module->allocator, elem_type, count));
 
@@ -2334,7 +2334,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					AstNode *src_node = ce->args[1];
 					ssaValue *dst_slice = ssa_build_expr(proc, dst_node);
 					ssaValue *src_slice = ssa_build_expr(proc, src_node);
-					Type *slice_type = get_base_type(ssa_type(dst_slice));
+					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);
@@ -2455,7 +2455,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssa_emit_comment(proc, make_string("ptr_sub"));
 					ssaValue *ptr_a = ssa_build_expr(proc, ce->args[0]);
 					ssaValue *ptr_b = ssa_build_expr(proc, ce->args[1]);
-					Type *ptr_type = get_base_type(ssa_type(ptr_a));
+					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);
 					Token sub = {Token_Sub};
@@ -2495,7 +2495,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssa_emit_comment(proc, make_string("min"));
 					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
 					ssaValue *y = ssa_build_expr(proc, ce->args[1]);
-					Type *t = get_base_type(ssa_type(x));
+					Type *t = base_type(ssa_type(x));
 					Token lt = {Token_Lt};
 					ssaValue *cond = ssa_emit_comp(proc, lt, x, y);
 					return ssa_emit_select(proc, cond, x, y);
@@ -2505,7 +2505,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssa_emit_comment(proc, make_string("max"));
 					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
 					ssaValue *y = ssa_build_expr(proc, ce->args[1]);
-					Type *t = get_base_type(ssa_type(x));
+					Type *t = base_type(ssa_type(x));
 					Token gt = {Token_Gt};
 					ssaValue *cond = ssa_emit_comp(proc, gt, x, y);
 					return ssa_emit_select(proc, cond, x, y);
@@ -2544,7 +2544,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 
 		// NOTE(bill): Regular call
 		ssaValue *value = ssa_build_expr(proc, ce->proc);
-		Type *proc_type_ = get_base_type(ssa_type(value));
+		Type *proc_type_ = base_type(ssa_type(value));
 		GB_ASSERT(proc_type_->kind == Type_Proc);
 		auto *type = &proc_type_->Proc;
 
@@ -2553,7 +2553,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 		isize arg_count = 0;
 		gb_for_array(i, ce->args) {
 			AstNode *a = ce->args[i];
-			Type *at = get_base_type(type_of_expr(proc->module->info, a));
+			Type *at = base_type(type_of_expr(proc->module->info, a));
 			if (at->kind == Type_Tuple) {
 				arg_count += at->Tuple.variable_count;
 			} else {
@@ -2588,7 +2588,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 			if (!vari_expand) {
 				Type *variadic_type = pt->variables[i]->type;
 				GB_ASSERT(is_type_slice(variadic_type));
-				variadic_type = get_base_type(variadic_type)->Slice.elem;
+				variadic_type = base_type(variadic_type)->Slice.elem;
 				for (; i < arg_count; i++) {
 					args[i] = ssa_emit_conv(proc, args[i], variadic_type, true);
 				}
@@ -2603,7 +2603,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 			ssa_emit_comment(proc, make_string("variadic call argument generation"));
 			gbAllocator allocator = proc->module->allocator;
 			Type *slice_type = pt->variables[type->param_count-1]->type;
-			Type *elem_type  = get_base_type(slice_type)->Slice.elem;
+			Type *elem_type  = base_type(slice_type)->Slice.elem;
 			Type *elem_ptr_type = make_type_pointer(allocator, elem_type);
 			ssaValue *slice = ssa_add_local_generated(proc, slice_type);
 			isize slice_len = arg_count+1 - type->param_count;
@@ -2716,7 +2716,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
 		Entity *e = entity_of_ident(proc->module->info, expr);
 
 		if (e->kind == Entity_Constant) {
-			if (get_base_type(e->type) == t_string) {
+			if (base_type(e->type) == t_string) {
 				// HACK TODO(bill): This is lazy but it works
 				String str = e->Constant.value.value_string;
 				ssaValue *global_array = ssa_add_global_string_array(proc->module, str);
@@ -2752,7 +2752,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
 	case_ast_node(se, SelectorExpr, expr);
 		ssa_emit_comment(proc, make_string("SelectorExpr"));
 		String selector = unparen_expr(se->selector)->Ident.string;
-		Type *type = get_base_type(type_of_expr(proc->module->info, se->expr));
+		Type *type = base_type(type_of_expr(proc->module->info, se->expr));
 
 		if (type == t_invalid) {
 			// NOTE(bill): Imports
@@ -2807,7 +2807,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
 
 	case_ast_node(ie, IndexExpr, expr);
 		ssa_emit_comment(proc, make_string("IndexExpr"));
-		Type *t = get_base_type(type_of_expr(proc->module->info, ie->expr));
+		Type *t = base_type(type_of_expr(proc->module->info, ie->expr));
 		gbAllocator a = proc->module->allocator;
 
 		switch (t->kind) {
@@ -2874,7 +2874,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
 		if (se->high != NULL)    high = ssa_build_expr(proc, se->high);
 		if (se->triple_indexed)  max  = ssa_build_expr(proc, se->max);
 		ssaAddr base = ssa_build_addr(proc, se->expr);
-		Type *type = get_base_type(ssa_addr_type(base));
+		Type *type = base_type(ssa_addr_type(base));
 
 		// TODO(bill): Cleanup like mad!
 
@@ -3052,7 +3052,7 @@ void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) {
 	ssa_module_add_value(m, e, t);
 	map_set(&m->members, hash_string(name), t);
 
-	Type *bt = get_base_type(e->type);
+	Type *bt = base_type(e->type);
 	if (bt->kind == Type_Record) {
 		auto *s = &bt->Record;
 		for (isize j = 0; j < s->other_field_count; j++) {
@@ -3232,7 +3232,6 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 				name = pd->foreign_name;
 			}
 
-			Entity *f = *map_get(&info->foreign_procs, hash_string(name));
 			ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
 			                                           proc->module, e, e->type, pd->type, pd->body, name);
 
@@ -3240,8 +3239,15 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 			ssa_module_add_value(proc->module, e, value);
 			ssa_build_proc(value, proc);
-			if (e == f) {
-				// NOTE(bill): Don't do mutliple declarations in the IR
+
+			if (value->Proc.tags & ProcTag_foreign) {
+				HashKey key = hash_string(name);
+				auto *prev_value = map_get(&proc->module->members, key);
+				if (prev_value == NULL) {
+					// NOTE(bill): Don't do mutliple declarations in the IR
+					map_set(&proc->module->members, key, value);
+				}
+			} else {
 				gb_array_append(proc->children, &value->Proc);
 			}
 		}
@@ -3685,13 +3691,13 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 
 
-			Type *base_type = type_deref(tag_var_entity->type);
+			Type *bt = type_deref(tag_var_entity->type);
 			ssaValue *index = NULL;
-			Type *ut = get_base_type(union_type);
+			Type *ut = base_type(union_type);
 			GB_ASSERT(ut->Record.kind == TypeRecord_Union);
 			for (isize field_index = 1; field_index < ut->Record.field_count; field_index++) {
-				Entity *f = get_base_type(union_type)->Record.fields[field_index];
-				if (are_types_identical(f->type, base_type)) {
+				Entity *f = base_type(union_type)->Record.fields[field_index];
+				if (are_types_identical(f->type, bt)) {
 					index = ssa_make_const_int(allocator, field_index);
 					break;
 				}
@@ -3739,14 +3745,22 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 	case_ast_node(bs, BranchStmt, node);
 		ssaBlock *block = NULL;
-		#define branch_case(x) case GB_JOIN2(Token_, x): \
-			for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) { \
-				block = GB_JOIN3(t->, x, _); \
-			} break
 		switch (bs->token.kind) {
-		branch_case(break);
-		branch_case(continue);
-		branch_case(fallthrough);
+		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;
 		}
 		// TODO(bill): Handle fallthrough scope exit correctly
 		// if (block != NULL && bs->token.kind != Token_fallthrough) {
@@ -3806,14 +3820,14 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 
 void ssa_emit_startup_runtime(ssaProcedure *proc) {
-	GB_ASSERT(proc->parent == NULL && proc->name == make_string("main"));
+	GB_ASSERT(proc->parent == NULL && proc->name == "main");
 
 	ssa_emit(proc, ssa_alloc_instr(proc, ssaInstr_StartupRuntime));
 }
 
 void ssa_insert_code_before_proc(ssaProcedure* proc, ssaProcedure *parent) {
 	if (parent == NULL) {
-		if (proc->name == make_string("main")) {
+		if (proc->name == "main") {
 			ssa_emit_startup_runtime(proc);
 		}
 	}
@@ -3834,7 +3848,6 @@ void ssa_build_proc(ssaValue *value, ssaProcedure *parent) {
 		AstFile *f = *found;
 		ssaDebugInfo *di_file = NULL;
 
-
 		ssaDebugInfo **di_file_found = map_get(&m->debug_info, hash_pointer(f));
 		if (di_file_found) {
 			di_file = *di_file_found;
@@ -3856,11 +3869,11 @@ void ssa_build_proc(ssaValue *value, ssaProcedure *parent) {
 			defer (proc->module->stmt_state_flags = out);
 
 			if (in & ProcTag_bounds_check) {
-				out |= ProcTag_bounds_check;
-				out &= ~ProcTag_no_bounds_check;
+				out |= StmtStateFlag_bounds_check;
+				out &= ~StmtStateFlag_no_bounds_check;
 			} else if (in & ProcTag_no_bounds_check) {
-				out |= ProcTag_no_bounds_check;
-				out &= ~ProcTag_bounds_check;
+				out |= StmtStateFlag_no_bounds_check;
+				out &= ~StmtStateFlag_bounds_check;
 			}
 		}
 

+ 12 - 9
src/common.cpp

@@ -55,7 +55,7 @@ struct BlockTimer {
 	}
 	~BlockTimer() {
 		finish = gb_utc_time_now();
-		gb_printf_err("%s - %llu us\n", finish-start);
+		gb_printf_err("%llu us\n", finish-start);
 	}
 };
 
@@ -63,7 +63,10 @@ struct BlockTimer {
 // Hasing
 
 struct HashKey {
-	u64 key;
+	union {
+		u64   key;
+		void *ptr;
+	};
 	b32 is_string;
 	String string; // if String, s.len > 0
 };
@@ -182,13 +185,13 @@ template <typename T> void map_clear  (Map<T> *h);
 template <typename T> void map_grow   (Map<T> *h);
 template <typename T> void map_rehash (Map<T> *h, isize new_count);
 
-template <typename T> typename MapEntry<T> *multi_map_find_first(Map<T> *h, HashKey key);
-template <typename T> typename MapEntry<T> *multi_map_find_next (Map<T> *h, typename MapEntry<T> *e);
+template <typename T> MapEntry<T> *multi_map_find_first(Map<T> *h, HashKey key);
+template <typename T> MapEntry<T> *multi_map_find_next (Map<T> *h, MapEntry<T> *e);
 
 template <typename T> isize multi_map_count     (Map<T> *h, HashKey key);
 template <typename T> void  multi_map_get_all   (Map<T> *h, HashKey key, T *items);
 template <typename T> void  multi_map_insert    (Map<T> *h, HashKey key, T value);
-template <typename T> void  multi_map_remove    (Map<T> *h, HashKey key, typename MapEntry<T> *e);
+template <typename T> void  multi_map_remove    (Map<T> *h, HashKey key, MapEntry<T> *e);
 template <typename T> void  multi_map_remove_all(Map<T> *h, HashKey key);
 
 
@@ -232,7 +235,7 @@ gb_internal MapFindResult map__find(Map<T> *h, HashKey key) {
 }
 
 template <typename T>
-gb_internal MapFindResult map__find(Map<T> *h, typename MapEntry<T> *e) {
+gb_internal MapFindResult map__find(Map<T> *h, MapEntry<T> *e) {
 	MapFindResult fr = {-1, -1, -1};
 	if (gb_array_count(h->hashes) > 0) {
 		fr.hash_index  = e->key.key % gb_array_count(h->hashes);
@@ -359,7 +362,7 @@ gb_inline void map_clear(Map<T> *h) {
 
 
 template <typename T>
-typename MapEntry<T> *multi_map_find_first(Map<T> *h, HashKey key) {
+MapEntry<T> *multi_map_find_first(Map<T> *h, HashKey key) {
 	isize i = map__find(h, key).entry_index;
 	if (i < 0) {
 		return NULL;
@@ -368,7 +371,7 @@ typename MapEntry<T> *multi_map_find_first(Map<T> *h, HashKey key) {
 }
 
 template <typename T>
-typename MapEntry<T> *multi_map_find_next(Map<T> *h, typename MapEntry<T> *e) {
+MapEntry<T> *multi_map_find_next(Map<T> *h, MapEntry<T> *e) {
 	isize i = e->next;
 	while (i >= 0) {
 		if (hash_key_equal(h->entries[i].key, e->key)) {
@@ -420,7 +423,7 @@ void multi_map_insert(Map<T> *h, HashKey key, T value) {
 }
 
 template <typename T>
-void multi_map_remove(Map<T> *h, HashKey key, typename MapEntry<T> *e) {
+void multi_map_remove(Map<T> *h, HashKey key, MapEntry<T> *e) {
 	MapFindResult fr = map__find(h, e);
 	if (fr.entry_index >= 0) {
 		map__erase(h, fr);

+ 8 - 8
src/gb/gb.h

@@ -6724,7 +6724,7 @@ isize gb_utf8_encode_rune(u8 buf[4], Rune r) {
 	}
 	if (i <= (1<<11)-1) {
 		buf[0] = 0xc0 | cast(u8)(r>>6);
-		buf[1] = 0x80 | cast(u8)(r)&mask;
+		buf[1] = 0x80 | (cast(u8)(r)&mask);
 		return 2;
 	}
 
@@ -6734,22 +6734,22 @@ isize gb_utf8_encode_rune(u8 buf[4], Rune r) {
 		r = GB_RUNE_INVALID;
 
 		buf[0] = 0xe0 | cast(u8)(r>>12);
-		buf[1] = 0x80 | cast(u8)(r>>6)&mask;
-		buf[2] = 0x80 | cast(u8)(r)&mask;
+		buf[1] = 0x80 | (cast(u8)(r>>6)&mask);
+		buf[2] = 0x80 | (cast(u8)(r)&mask);
 		return 3;
 	}
 
 	if (i <= (1<<16)-1) {
 		buf[0] = 0xe0 | cast(u8)(r>>12);
-		buf[1] = 0x80 | cast(u8)(r>>6)&mask;
-		buf[2] = 0x80 | cast(u8)(r)&mask;
+		buf[1] = 0x80 | (cast(u8)(r>>6)&mask);
+		buf[2] = 0x80 | (cast(u8)(r)&mask);
 		return 3;
 	}
 
 	buf[0] = 0xf0 | cast(u8)(r>>18);
-	buf[1] = 0x80 | cast(u8)(r>>12)&mask;
-	buf[2] = 0x80 | cast(u8)(r>>6)&mask;
-	buf[3] = 0x80 | cast(u8)(r)&mask;
+	buf[1] = 0x80 | (cast(u8)(r>>12)&mask);
+	buf[2] = 0x80 | (cast(u8)(r>>6)&mask);
+	buf[3] = 0x80 | (cast(u8)(r)&mask);
 	return 4;
 }
 

+ 3 - 1
src/main.cpp

@@ -1,3 +1,5 @@
+#define DISPLAY_TIMING
+
 #include "common.cpp"
 #include "unicode.cpp"
 #include "tokenizer.cpp"
@@ -49,7 +51,6 @@ i32 win32_exec_command_line_app(char *fmt, ...) {
 	}
 }
 
-// #define DISPLAY_TIMING
 #if defined(DISPLAY_TIMING)
 #define INIT_TIMER() f64 start_time = gb_time_now(), end_time = 0, total_time = 0
 #define PRINT_TIMER(section) do { \
@@ -111,6 +112,7 @@ int main(int argc, char **argv) {
 		gb_printf_err("using: %s [run] <filename> \n", argv[0]);
 		return 1;
 	}
+
 	init_string_buffer_memory();
 
 	String module_dir = get_module_dir(gb_heap_allocator());

File diff ditekan karena terlalu besar
+ 206 - 182
src/parser.cpp


+ 12 - 1
src/string.cpp

@@ -14,7 +14,7 @@ typedef struct String {
 	isize len;
 } String;
 // NOTE(bill): used for printf style arguments
-#define LIT(x) (x).len, (x).text
+#define LIT(x) ((int)(x).len), (x).text
 
 
 typedef struct String16 {
@@ -46,6 +46,11 @@ gb_inline String make_string(char *text) {
 	return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
 }
 
+template <size_t N>
+gb_inline String make_string(char const (&text)[N]) {
+	return make_string(cast(u8 *)cast(void *)text, N-1);
+}
+
 gb_inline b32 are_strings_equal(String a, String b) {
 	if (a.len == b.len) {
 		return gb_memcompare(a.text, b.text, a.len) == 0;
@@ -117,6 +122,12 @@ bool operator > (String a, String b) { return string_compare(a, b) > 0; }
 bool operator <=(String a, String b) { return string_compare(a, b) <= 0; }
 bool operator >=(String a, String b) { return string_compare(a, b) >= 0; }
 
+template <size_t N> bool operator ==(String a, char const (&b)[N]) { return a == make_string(b); }
+template <size_t N> bool operator !=(String a, char const (&b)[N]) { return a != make_string(b); }
+template <size_t N> bool operator ==(char const (&a)[N], String b) { return make_string(a) == b; }
+template <size_t N> bool operator !=(char const (&a)[N], String b) { return make_string(a) != b; }
+
+
 
 
 gb_inline isize string_extension_position(String str) {

+ 35 - 38
src/tokenizer.cpp

@@ -1,6 +1,7 @@
 #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"), \
@@ -317,8 +318,7 @@ struct Tokenizer {
 };
 
 
-#define tokenizer_err(t, msg, ...) tokenizer_err_(t, __FUNCTION__, msg, ##__VA_ARGS__)
-void tokenizer_err_(Tokenizer *t, char *function, char *msg, ...) {
+void tokenizer_err(Tokenizer *t, char *msg, ...) {
 	va_list va;
 	isize column = t->read_curr - t->line+1;
 	if (column < 1)
@@ -428,41 +428,8 @@ gb_inline void destroy_tokenizer(Tokenizer *t) {
 }
 
 void tokenizer_skip_whitespace(Tokenizer *t) {
-	for (;;) {
-		if (rune_is_whitespace(t->curr_rune)) {
-			advance_to_next_rune(t);
-		} else if (t->curr_rune == '/') {
-			if (t->read_curr[0] == '/') { // Line comment //
-				while (t->curr_rune != '\n') {
-					advance_to_next_rune(t);
-				}
-			} else if (t->read_curr[0] == '*') { // (Nested) Block comment /**/
-				advance_to_next_rune(t);
-				advance_to_next_rune(t);
-				isize comment_scope = 1;
-				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);
-					}
-				}
-			} else {
-				break;
-			}
-		} else {
-			break;
-		}
+	while (rune_is_whitespace(t->curr_rune)) {
+		advance_to_next_rune(t);
 	}
 }
 
@@ -787,13 +754,43 @@ Token tokenizer_get_token(Tokenizer *t) {
 		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_Quo,   Token_QuoEq);     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 (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 == '-') {

+ 4 - 4
src/unicode.cpp

@@ -1,15 +1,15 @@
 #pragma warning(push)
 #pragma warning(disable: 4245)
 
-#include "utf8proc/utf8proc.h"
-// #define UTF8PROC_IMPLEMENTATION
-// #include "utf8proc/utf8proc_new.h"
+// #include "utf8proc/utf8proc.h"
+#include "utf8proc/utf8proc.c"
 
 #pragma warning(pop)
 
 // TODO(bill): Unicode support
 b32 rune_is_letter(Rune r) {
-	if (r < 0x80 && gb_char_is_alpha(cast(char)r) || r == '_') {
+	if ((r < 0x80 && gb_char_is_alpha(cast(char)r)) ||
+	    r == '_') {
 		return true;
 	}
 	switch (utf8proc_category(r)) {

+ 3 - 3
src/utf8proc/utf8proc.c

@@ -458,12 +458,12 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_char(utf8proc_int32_t uc,
       category == UTF8PROC_CATEGORY_ME) return 0;
   }
   if (options & UTF8PROC_CASEFOLD) {
-    if (property->casefold_seqindex != UINT16_MAX) {
+    if ((utf8proc_int16_t)property->casefold_seqindex != UINT16_MAX) {
       return seqindex_write_char_decomposed(property->casefold_seqindex, dst, bufsize, options, last_boundclass);
     }
   }
   if (options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) {
-    if (property->decomp_seqindex != UINT16_MAX &&
+    if ((utf8proc_int16_t)property->decomp_seqindex != UINT16_MAX &&
         (!property->decomp_type || (options & UTF8PROC_COMPAT))) {
       return seqindex_write_char_decomposed(property->decomp_seqindex, dst, bufsize, options, last_boundclass);
     }
@@ -621,7 +621,7 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer,
           starter_property = unsafe_get_property(*starter);
         }
         if (starter_property->comb_index < 0x8000 &&
-            current_property->comb_index != UINT16_MAX &&
+            (utf8proc_int16_t)current_property->comb_index != UINT16_MAX &&
             current_property->comb_index >= 0x8000) {
           int sidx = starter_property->comb_index;
           int idx = (current_property->comb_index & 0x3FFF) - utf8proc_combinations[sidx];

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini