Browse Source

Remove `when` suffixes; Implement file scope `when` statement, evaluated in source order

Ginger Bill 8 years ago
parent
commit
e6e0aba8c3
12 changed files with 366 additions and 393 deletions
  1. 3 1
      core/atomics.odin
  2. 7 4
      core/opengl.odin
  3. 3 3
      core/os.odin
  4. 2 2
      core/sync.odin
  5. 1 1
      core/sync_windows.odin
  6. 1 1
      core/sys/wgl.odin
  7. 7 5
      core/sys/windows.odin
  8. 5 2
      examples/demo.odin
  9. 5 5
      src/check_expr.cpp
  10. 283 320
      src/checker.cpp
  11. 2 9
      src/map.cpp
  12. 47 40
      src/parser.cpp

+ 3 - 1
core/atomics.odin

@@ -1,7 +1,9 @@
 // TODO(bill): Use assembly instead here to implement atomics
 // Inline vs external file?
 
-import win32 "core:sys/windows.odin" when ODIN_OS == "windows";
+when ODIN_OS == "windows" {
+	import win32 "core:sys/windows.odin";
+}
 _ :: compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version
 
 

+ 7 - 4
core/opengl.odin

@@ -1,8 +1,11 @@
-foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows";
-foreign_system_library lib "gl" when ODIN_OS == "linux";
+when ODIN_OS == "windows" do foreign_system_library lib "opengl32.lib";
+when ODIN_OS == "linux"  do foreign_system_library lib "gl";
+
+when ODIN_OS == "windows" {
+	import win32 "core:sys/windows.odin";
+	import "core:sys/wgl.odin";
+}
 
-import win32 "core:sys/windows.odin" when ODIN_OS == "windows";
-import "core:sys/wgl.odin"           when ODIN_OS == "windows";
 export "core:opengl_constants.odin";
 
 _ := compile_assert(ODIN_OS != "osx");

+ 3 - 3
core/os.odin

@@ -1,6 +1,6 @@
-export "core:os_windows.odin" when ODIN_OS == "windows";
-export "core:os_x.odin"       when ODIN_OS == "osx";
-export "core:os_linux.odin"   when ODIN_OS == "linux";
+when ODIN_OS == "windows" do export "core:os_windows.odin";
+when ODIN_OS == "osx"     do export "core:os_x.odin";
+when ODIN_OS == "linux"   do export "core:os_linux.odin";
 
 write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
 	return write(fd, cast([]u8)str);

+ 2 - 2
core/sync.odin

@@ -1,2 +1,2 @@
-export "core:sync_windows.odin" when ODIN_OS == "windows";
-export "core:sync_linux.odin"   when ODIN_OS == "linux";
+when ODIN_OS == "windows" do export "core:sync_windows.odin";
+when ODIN_OS == "linux"   do export "core:sync_linux.odin";

+ 1 - 1
core/sync_windows.odin

@@ -1,4 +1,4 @@
-import win32 "core:sys/windows.odin" when ODIN_OS == "windows";
+when ODIN_OS == "windows" do import win32 "core:sys/windows.odin";
 import "core:atomics.odin";
 
 Semaphore :: struct {

+ 1 - 1
core/sys/wgl.odin

@@ -1,4 +1,4 @@
-foreign_system_library "opengl32.lib" when ODIN_OS == "windows";
+when ODIN_OS == "windows" do foreign_system_library "opengl32.lib";
 using import "core:sys/windows.odin";
 
 

+ 7 - 5
core/sys/windows.odin

@@ -1,8 +1,10 @@
-foreign_system_library "kernel32.lib" when ODIN_OS == "windows";
-foreign_system_library "user32.lib"   when ODIN_OS == "windows";
-foreign_system_library "gdi32.lib"    when ODIN_OS == "windows";
-foreign_system_library "winmm.lib"    when ODIN_OS == "windows";
-foreign_system_library "shell32.lib"  when ODIN_OS == "windows";
+when ODIN_OS == "windows" {
+	foreign_system_library "kernel32.lib";
+	foreign_system_library "user32.lib";
+	foreign_system_library "gdi32.lib";
+	foreign_system_library "winmm.lib";
+	foreign_system_library "shell32.lib";
+}
 
 Handle    :: rawptr;
 Hwnd      :: Handle;

+ 5 - 2
examples/demo.odin

@@ -1,8 +1,6 @@
 import "core:fmt.odin";
 import "core:strconv.odin";
 import "core:mem.odin";
-import "core:thread.odin"            when ODIN_OS == "windows";
-import win32 "core:sys/windows.odin" when ODIN_OS == "windows";
 import "core:atomics.odin";
 import "core:bits.odin";
 import "core:hash.odin";
@@ -17,6 +15,11 @@ import "core:types.odin";
 import "core:utf8.odin";
 import "core:utf16.odin";
 
+when ODIN_OS == "windows" {
+	import "core:thread.odin";
+	import win32 "core:sys/windows.odin";
+}
+
 general_stuff :: proc() {
 	{ // `do` for inline statmes rather than block
 		foo :: proc() do fmt.println("Foo!");

+ 5 - 5
src/check_expr.cpp

@@ -98,7 +98,7 @@ void check_scope_decls(Checker *c, Array<AstNode *> nodes, isize reserve_size) {
 	Scope *s = c->context.scope;
 	GB_ASSERT(s->file == nullptr);
 
-	check_collect_entities(c, nodes, false);
+	check_collect_entities(c, nodes);
 
 	for_array(i, s->elements.entries) {
 		Entity *e = s->elements.entries[i].value;
@@ -995,14 +995,14 @@ Array<Entity *> check_fields(Checker *c, AstNode *node, Array<AstNode *> decls,
 	array_init(&fields, heap_allocator(), init_field_capacity);
 
 	Map<Entity *> entity_map = {};
-	map_init_with_reserve(&entity_map, c->tmp_allocator, 2*init_field_capacity);
+	map_init(&entity_map, c->tmp_allocator, 2*init_field_capacity);
 
 
 	if (node != nullptr) {
 		GB_ASSERT(node->kind != AstNode_UnionType);
 	}
 
-	check_collect_entities(c, decls, false);
+	check_collect_entities(c, decls);
 	for_array(i, c->context.scope->elements.entries) {
 		Entity *e = c->context.scope->elements.entries[i].value;
 		DeclInfo *d = nullptr;
@@ -1442,7 +1442,7 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod
 	enum_type->Enum.base_type = base_type;
 
 	Map<Entity *> entity_map = {}; // Key: String
-	map_init_with_reserve(&entity_map, c->tmp_allocator, 2*(et->fields.count));
+	map_init(&entity_map, c->tmp_allocator, 2*(et->fields.count));
 
 	Array<Entity *> fields = {};
 	array_init(&fields, c->allocator, et->fields.count);
@@ -1562,7 +1562,7 @@ void check_bit_field_type(Checker *c, Type *bit_field_type, AstNode *node) {
 	defer (gb_temp_arena_memory_end(tmp));
 
 	Map<Entity *> entity_map = {}; // Key: String
-	map_init_with_reserve(&entity_map, c->tmp_allocator, 2*(bft->fields.count));
+	map_init(&entity_map, c->tmp_allocator, 2*(bft->fields.count));
 
 	isize field_count = 0;
 	Entity **fields  = gb_alloc_array(c->allocator, Entity *, bft->fields.count);

+ 283 - 320
src/checker.cpp

@@ -342,6 +342,7 @@ struct CheckerContext {
 	DeclInfo * curr_proc_decl;
 	AstNode *  curr_foreign_library;
 
+	bool       allow_file_when_statement;
 	bool       allow_polymorphic_types;
 	bool       no_polymorphic_errors;
 	Scope *    polymorphic_scope;
@@ -380,8 +381,7 @@ struct Checker {
 	Scope *                    global_scope;
 	// NOTE(bill): Procedures to check
 	Map<ProcedureInfo>         procs; // Key: DeclInfo *
-	Array<DelayedDecl>         delayed_imports;
-	Array<DelayedDecl>         delayed_foreign_libraries;
+	Map<Scope *>               file_scopes; // Key: String (fullpath)
 
 	Pool                       pool;
 	gbAllocator                allocator;
@@ -870,8 +870,6 @@ void init_checker(Checker *c, Parser *parser) {
 
 	array_init(&c->proc_stack, a);
 	map_init(&c->procs, a);
-	array_init(&c->delayed_imports, a);
-	array_init(&c->delayed_foreign_libraries, a);
 
 	// NOTE(bill): Is this big enough or too small?
 	isize item_size = gb_max3(gb_size_of(Entity), gb_size_of(Type), gb_size_of(Scope));
@@ -892,6 +890,8 @@ void init_checker(Checker *c, Parser *parser) {
 
 	c->global_scope = create_scope(universal_scope, c->allocator);
 	c->context.scope = c->global_scope;
+
+	map_init(&c->file_scopes, heap_allocator());
 }
 
 void destroy_checker(Checker *c) {
@@ -899,11 +899,11 @@ void destroy_checker(Checker *c) {
 	destroy_scope(c->global_scope);
 	array_free(&c->proc_stack);
 	map_destroy(&c->procs);
-	array_free(&c->delayed_imports);
-	array_free(&c->delayed_foreign_libraries);
 
 	pool_destroy(&c->pool);
 	gb_arena_free(&c->tmp_arena);
+
+	map_destroy(&c->file_scopes);
 	// gb_arena_free(&c->arena);
 }
 
@@ -1615,8 +1615,9 @@ void init_preload(Checker *c) {
 
 
 bool check_arity_match(Checker *c, AstNodeValueDecl *vd, bool is_global = false);
-void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_scope);
-void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool is_file_scope);
+void check_collect_entities(Checker *c, Array<AstNode *> nodes);
+void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws);
+void check_delayed_file_import_entities(Checker *c, AstNode *decl);
 
 bool check_is_entity_overloaded(Entity *e) {
 	if (e->kind != Entity_Procedure) {
@@ -1772,7 +1773,7 @@ bool check_arity_match(Checker *c, AstNodeValueDecl *vd, bool is_global) {
 	return true;
 }
 
-void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool is_file_scope) {
+void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws) {
 	Operand operand = {Addressing_Invalid};
 	check_expr(c, &operand, ws->cond);
 	if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
@@ -1786,14 +1787,14 @@ void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool
 	} else {
 		if (operand.value.kind == ExactValue_Bool &&
 		    operand.value.value_bool) {
-			check_collect_entities(c, ws->body->BlockStmt.stmts, is_file_scope);
+			check_collect_entities(c, ws->body->BlockStmt.stmts);
 		} else if (ws->else_stmt) {
 			switch (ws->else_stmt->kind) {
 			case AstNode_BlockStmt:
-				check_collect_entities(c, ws->else_stmt->BlockStmt.stmts, is_file_scope);
+				check_collect_entities(c, ws->else_stmt->BlockStmt.stmts);
 				break;
 			case AstNode_WhenStmt:
-				check_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt, is_file_scope);
+				check_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt);
 				break;
 			default:
 				error(ws->else_stmt, "Invalid `else` statement in `when` statement");
@@ -1804,14 +1805,7 @@ void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool
 }
 
 // NOTE(bill): If file_scopes == nullptr, this will act like a local scope
-void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_scope) {
-	// NOTE(bill): File scope and local scope are different kinds of scopes
-	if (is_file_scope) {
-		GB_ASSERT(c->context.scope->is_file);
-	} else {
-		GB_ASSERT(!c->context.scope->is_file);
-	}
-
+void check_collect_entities(Checker *c, Array<AstNode *> nodes) {
 	for_array(decl_index, nodes) {
 		AstNode *decl = nodes[decl_index];
 		if (!is_ast_node_decl(decl) && !is_ast_node_when_stmt(decl)) {
@@ -1823,11 +1817,7 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 		case_end;
 
 		case_ast_node(ws, WhenStmt, decl);
-			if (c->context.scope->is_file) {
-				error(decl, "`when` statements are not allowed at file scope");
-			} else {
-				// Will be handled later
-			}
+			// Will be handled later
 		case_end;
 
 		case_ast_node(vd, ValueDecl, decl);
@@ -1975,8 +1965,9 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 				// TODO(bill): Better error handling if it isn't
 				continue;
 			}
-			DelayedDecl di = {c->context.scope, decl};
-			array_add(&c->delayed_imports, di);
+			if (c->context.allow_file_when_statement) {
+				check_delayed_file_import_entities(c, decl);
+			}
 		case_end;
 
 		case_ast_node(id, ExportDecl, decl);
@@ -1986,8 +1977,9 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 				// TODO(bill): Better error handling if it isn't
 				continue;
 			}
-			DelayedDecl di = {c->context.scope, decl};
-			array_add(&c->delayed_imports, di);
+			if (c->context.allow_file_when_statement) {
+				check_delayed_file_import_entities(c, decl);
+			}
 		case_end;
 
 		case_ast_node(fl, ForeignLibraryDecl, decl);
@@ -1997,22 +1989,9 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 				// TODO(bill): Better error handling if it isn't
 				continue;
 			}
-
-			if (fl->cond != nullptr) {
-				Operand operand = {Addressing_Invalid};
-				check_expr(c, &operand, fl->cond);
-				if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
-					error(fl->cond, "Non-constant boolean `when` condition");
-					continue;
-				}
-				if (operand.value.kind == ExactValue_Bool &&
-					!operand.value.value_bool) {
-					continue;
-				}
+			if (c->context.allow_file_when_statement) {
+				check_delayed_file_import_entities(c, decl);
 			}
-
-			DelayedDecl di = {c->context.scope, decl};
-			array_add(&c->delayed_foreign_libraries, di);
 		case_end;
 
 		case_ast_node(fb, ForeignBlockDecl, decl);
@@ -2024,7 +2003,7 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 
 			CheckerContext prev_context = c->context;
 			c->context.curr_foreign_library = foreign_library;
-			check_collect_entities(c, fb->decls, is_file_scope);
+			check_collect_entities(c, fb->decls);
 			c->context = prev_context;
 		case_end;
 
@@ -2038,12 +2017,12 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 
 	// NOTE(bill): `when` stmts need to be handled after the other as the condition may refer to something
 	// declared after this stmt in source
-	if (!c->context.scope->is_file) {
+	if (!c->context.scope->is_file || c->context.allow_file_when_statement) {
 		for_array(i, nodes) {
 			AstNode *node = nodes[i];
 			switch (node->kind) {
 			case_ast_node(ws, WhenStmt, node);
-					check_collect_entities_from_when_stmt(c, ws, is_file_scope);
+					check_collect_entities_from_when_stmt(c, ws);
 			case_end;
 			}
 		}
@@ -2196,7 +2175,6 @@ void import_graph_node_set_remove(ImportGraphNodeSet *s, ImportGraphNode *n) {
 
 struct ImportGraphNode {
 	Scope *            scope;
-	Array<AstNode *>   decls; // AstNodeImportDecl or AstNodeExportDecl
 	String             path;
 	isize              file_id;
 	ImportGraphNodeSet pred;
@@ -2210,7 +2188,6 @@ ImportGraphNode *import_graph_node_create(gbAllocator a, Scope *scope) {
 	n->scope   = scope;
 	n->path    = scope->file->tokenizer.fullpath;
 	n->file_id = scope->file->id;
-	array_init(&n->decls, heap_allocator());
 	return n;
 }
 
@@ -2218,7 +2195,6 @@ ImportGraphNode *import_graph_node_create(gbAllocator a, Scope *scope) {
 void import_graph_node_destroy(ImportGraphNode *n, gbAllocator a) {
 	import_graph_node_set_destroy(&n->pred);
 	import_graph_node_set_destroy(&n->succ);
-	array_free(&n->decls);
 	gb_free(a, n);
 }
 
@@ -2256,98 +2232,122 @@ GB_COMPARE_PROC(ast_node_cmp) {
 }
 
 
-Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c, Map<Scope *> *file_scopes) {
-	gbAllocator a = heap_allocator();
+void add_import_dependency_node(Checker *c, AstNode *decl, Map<ImportGraphNode *> *M) {
+	Scope *parent_file_scope = decl->file->scope;
 
-	Map<ImportGraphNode *> M = {}; // Key: Scope *
-	map_init(&M, a);
-	defer (map_destroy(&M));
+	switch (decl->kind) {
+	case_ast_node(id, ImportDecl, decl);
+		String path = id->fullpath;
+		HashKey key = hash_string(path);
+		Scope **found = map_get(&c->file_scopes, key);
+		if (found == nullptr) {
+			for_array(scope_index, c->file_scopes.entries) {
+				Scope *scope = c->file_scopes.entries[scope_index].value;
+				gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath));
+			}
+			Token token = ast_node_token(decl);
+			gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column);
+			GB_PANIC("Unable to find scope for file: %.*s", LIT(path));
+		}
+		Scope *scope = *found;
+		GB_ASSERT(scope != nullptr);
 
-	for_array(i, c->parser->files) {
-		Scope *scope = c->parser->files[i]->scope;
+		ImportGraphNode *m = nullptr;
+		ImportGraphNode *n  = nullptr;
 
-		ImportGraphNode *n = import_graph_node_create(heap_allocator(), scope);
-		map_set(&M, hash_pointer(scope), n);
-	}
+		ImportGraphNode **found_node = map_get(M, hash_pointer(parent_file_scope));
+		GB_ASSERT(found_node != nullptr);
+		m = *found_node;
 
+		found_node = map_get(M, hash_pointer(scope));
+		GB_ASSERT(found_node != nullptr);
+		n = *found_node;
 
-	// Calculate edges for graph M
-	for_array(i, c->delayed_imports) {
-		Scope *parent = c->delayed_imports[i].parent;
-		AstNode *decl = c->delayed_imports[i].decl;
-		GB_ASSERT(parent->is_file);
+		if (id->is_using) {
+			import_graph_node_set_add(&n->pred, m);
+			import_graph_node_set_add(&m->succ, n);
+			ptr_set_add(&m->scope->imported, n->scope);
+		}
+	case_end;
 
-		switch (decl->kind) {
-		case_ast_node(id, ImportDecl, decl);
-			String path = id->fullpath;
-			HashKey key = hash_string(path);
-			Scope **found = map_get(file_scopes, key);
-			if (found == nullptr) {
-				for_array(scope_index, file_scopes->entries) {
-					Scope *scope = file_scopes->entries[scope_index].value;
-					gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath));
-				}
-				Token token = ast_node_token(decl);
-				gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column);
-				GB_PANIC("Unable to find scope for file: %.*s", LIT(path));
+
+	case_ast_node(ed, ExportDecl, decl);
+		String path = ed->fullpath;
+		HashKey key = hash_string(path);
+		Scope **found = map_get(&c->file_scopes, key);
+		if (found == nullptr) {
+			for_array(scope_index, c->file_scopes.entries) {
+				Scope *scope = c->file_scopes.entries[scope_index].value;
+				gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath));
 			}
-			Scope *scope = *found;
-			GB_ASSERT(scope != nullptr);
+			Token token = ast_node_token(decl);
+			gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column);
+			GB_PANIC("Unable to find scope for file: %.*s", LIT(path));
+		}
+		Scope *scope = *found;
+		GB_ASSERT(scope != nullptr);
 
-			ImportGraphNode *m = nullptr;
-			ImportGraphNode *n  = nullptr;
+		ImportGraphNode *m = nullptr;
+		ImportGraphNode *n  = nullptr;
 
-			ImportGraphNode **found_node = map_get(&M, hash_pointer(parent));
-			GB_ASSERT(found_node != nullptr);
-			m = *found_node;
+		ImportGraphNode **found_node = map_get(M, hash_pointer(parent_file_scope));
+		GB_ASSERT(found_node != nullptr);
+		m = *found_node;
 
-			found_node = map_get(&M, hash_pointer(scope));
-			GB_ASSERT(found_node != nullptr);
-			n = *found_node;
+		found_node = map_get(M, hash_pointer(scope));
+		GB_ASSERT(found_node != nullptr);
+		n = *found_node;
 
-			array_add(&m->decls, decl);
+		import_graph_node_set_add(&n->pred, m);
+		import_graph_node_set_add(&m->succ, n);
+		ptr_set_add(&m->scope->imported, n->scope);
+	case_end;
 
-			if (id->is_using) {
-				import_graph_node_set_add(&n->pred, m);
-				import_graph_node_set_add(&m->succ, n);
-				ptr_set_add(&m->scope->imported, n->scope);
+	case_ast_node(ws, WhenStmt, decl);
+		if (ws->body != nullptr) {
+			auto stmts = ws->body->BlockStmt.stmts;
+			for_array(i, stmts) {
+				add_import_dependency_node(c, stmts[i], M);
 			}
-		case_end;
-
+		}
 
-		case_ast_node(ed, ExportDecl, decl);
-			String path = ed->fullpath;
-			HashKey key = hash_string(path);
-			Scope **found = map_get(file_scopes, key);
-			if (found == nullptr) {
-				for_array(scope_index, file_scopes->entries) {
-					Scope *scope = file_scopes->entries[scope_index].value;
-					gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath));
+		if (ws->else_stmt != nullptr) {
+			switch (ws->else_stmt->kind) {
+			case AstNode_BlockStmt: {
+				auto stmts = ws->else_stmt->BlockStmt.stmts;
+				for_array(i, stmts) {
+					add_import_dependency_node(c, stmts[i], M);
 				}
-				Token token = ast_node_token(decl);
-				gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column);
-				GB_PANIC("Unable to find scope for file: %.*s", LIT(path));
+			} break;
+			case AstNode_WhenStmt:
+				add_import_dependency_node(c, ws->else_stmt, M);
+				break;
 			}
-			Scope *scope = *found;
-			GB_ASSERT(scope != nullptr);
+		}
+	case_end;
+	}
+}
 
-			ImportGraphNode *m = nullptr;
-			ImportGraphNode *n  = nullptr;
+Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c) {
+	gbAllocator a = heap_allocator();
 
-			ImportGraphNode **found_node = map_get(&M, hash_pointer(parent));
-			GB_ASSERT(found_node != nullptr);
-			m = *found_node;
+	Map<ImportGraphNode *> M = {}; // Key: Scope *
+	map_init(&M, a);
+	defer (map_destroy(&M));
 
-			found_node = map_get(&M, hash_pointer(scope));
-			GB_ASSERT(found_node != nullptr);
-			n = *found_node;
+	for_array(i, c->parser->files) {
+		Scope *scope = c->parser->files[i]->scope;
 
-			array_add(&m->decls, decl);
+		ImportGraphNode *n = import_graph_node_create(heap_allocator(), scope);
+		map_set(&M, hash_pointer(scope), n);
+	}
 
-			import_graph_node_set_add(&n->pred, m);
-			import_graph_node_set_add(&m->succ, n);
-			ptr_set_add(&m->scope->imported, n->scope);
-		case_end;
+	// Calculate edges for graph M
+	for_array(i, c->parser->files) {
+		AstFile *f = c->parser->files[i];
+		for_array(j, f->decls) {
+			AstNode *decl = f->decls[j];
+			add_import_dependency_node(c, decl, &M);
 		}
 	}
 
@@ -2355,10 +2355,7 @@ Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c, Map<Scope
 	array_init(&G, a);
 
 	for_array(i, M.entries) {
-		auto *entry = &M.entries[i];
-		ImportGraphNode *n = entry->value;
-		gb_sort_array(n->decls.data, n->decls.count, ast_node_cmp);
-		array_add(&G, n);
+		array_add(&G, M.entries[i].value);
 	}
 
 	for_array(i, G) {
@@ -2413,8 +2410,154 @@ Array<Scope *> find_import_path(Map<Scope *> *file_scopes, Scope *start, Scope *
 	return empty_path;
 }
 
-void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
-	Array<ImportGraphNode *> dep_graph = generate_import_dependency_graph(c, file_scopes);
+void check_delayed_file_import_entities(Checker *c, AstNode *decl) {
+	GB_ASSERT(c->context.allow_file_when_statement);
+
+	Scope *parent_scope = c->context.scope;
+	GB_ASSERT(parent_scope->is_file);
+
+	switch (decl->kind) {
+	case_ast_node(ws, WhenStmt, decl);
+		check_collect_entities_from_when_stmt(c, ws);
+	case_end;
+
+	case_ast_node(id, ImportDecl, decl);
+		Token token = id->relpath;
+		HashKey key = hash_string(id->fullpath);
+		Scope **found = map_get(&c->file_scopes, key);
+		if (found == nullptr) {
+			for_array(scope_index, c->file_scopes.entries) {
+				Scope *scope = c->file_scopes.entries[scope_index].value;
+				gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath));
+			}
+			gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column);
+			GB_PANIC("Unable to find scope for file: %.*s", LIT(id->fullpath));
+		}
+		Scope *scope = *found;
+
+		if (scope->is_global) {
+			error(token, "Importing a #shared_global_scope is disallowed and unnecessary");
+			return;
+		}
+
+		if (ptr_set_exists(&parent_scope->imported, scope)) {
+			// error(token, "Multiple import of the same file within this scope");
+		} else {
+			ptr_set_add(&parent_scope->imported, scope);
+		}
+
+		scope->has_been_imported = true;
+
+		if (id->is_using) {
+			if (parent_scope->is_global) {
+				error(id->import_name, "#shared_global_scope imports cannot use using");
+			} else {
+				// NOTE(bill): Add imported entities to this file's scope
+				for_array(elem_index, scope->elements.entries) {
+					Entity *e = scope->elements.entries[elem_index].value;
+					if (e->scope == parent_scope) return;
+
+					if (!is_entity_kind_exported(e->kind)) {
+						return;
+					}
+					if (is_entity_exported(e)) {
+						// TODO(bill): Should these entities be imported but cause an error when used?
+						bool ok = add_entity(c, parent_scope, e->identifier, e);
+						if (ok) map_set(&parent_scope->implicit, hash_entity(e), true);
+					}
+				}
+			}
+		} else {
+			String import_name = path_to_entity_name(id->import_name.string, id->fullpath);
+			if (is_blank_ident(import_name)) {
+				error(token, "File name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
+			} else {
+				GB_ASSERT(id->import_name.pos.line != 0);
+				id->import_name.string = import_name;
+				Entity *e = make_entity_import_name(c->allocator, parent_scope, id->import_name, t_invalid,
+				                                    id->fullpath, id->import_name.string,
+				                                    scope);
+
+				add_entity(c, parent_scope, nullptr, e);
+			}
+		}
+	case_end;
+
+	case_ast_node(ed, ExportDecl, decl);
+		Token token = ed->relpath;
+		HashKey key = hash_string(ed->fullpath);
+		Scope **found = map_get(&c->file_scopes, key);
+		if (found == nullptr) {
+			for_array(scope_index, c->file_scopes.entries) {
+				Scope *scope = c->file_scopes.entries[scope_index].value;
+				gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath));
+			}
+			gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column);
+			GB_PANIC("Unable to find scope for file: %.*s", LIT(ed->fullpath));
+		}
+		Scope *scope = *found;
+
+		if (scope->is_global) {
+			error(token, "Exporting a #shared_global_scope is disallowed and unnecessary");
+			return;
+		}
+
+		if (ptr_set_exists(&parent_scope->imported, scope)) {
+			// error(token, "Multiple import of the same file within this scope");
+		} else {
+			ptr_set_add(&parent_scope->imported, scope);
+		}
+
+		scope->has_been_imported = true;
+		if (parent_scope->is_global) {
+			error(decl, "`export` cannot be used on #shared_global_scope");
+		} else {
+			// NOTE(bill): Add imported entities to this file's scope
+			for_array(elem_index, scope->elements.entries) {
+				Entity *e = scope->elements.entries[elem_index].value;
+				if (e->scope == parent_scope) return;
+
+				if (is_entity_kind_exported(e->kind)) {
+					add_entity(c, parent_scope, e->identifier, e);
+				}
+			}
+		}
+	case_end;
+
+	case_ast_node(fl, ForeignLibraryDecl, decl);
+		String file_str = fl->filepath.string;
+		String base_dir = fl->base_dir;
+
+		if (fl->token.kind == Token_foreign_library) {
+			gbAllocator a = heap_allocator(); // TODO(bill): Change this allocator
+
+			String rel_path = get_fullpath_relative(a, base_dir, file_str);
+			String import_file = rel_path;
+			if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
+				String abs_path = get_fullpath_core(a, file_str);
+				if (gb_file_exists(cast(char *)abs_path.text)) {
+					import_file = abs_path;
+				}
+			}
+			file_str = import_file;
+		}
+
+		String library_name = path_to_entity_name(fl->library_name.string, file_str);
+		if (is_blank_ident(library_name)) {
+			error(decl, "File name, %.*s, cannot be as a library name as it is not a valid identifier", LIT(fl->library_name.string));
+		} else {
+			GB_ASSERT(fl->library_name.pos.line != 0);
+			fl->library_name.string = library_name;
+			Entity *e = make_entity_library_name(c->allocator, parent_scope, fl->library_name, t_invalid,
+			                                     file_str, library_name);
+			add_entity(c, parent_scope, nullptr, e);
+		}
+	case_end;
+	}
+}
+
+void check_import_entities(Checker *c) {
+	Array<ImportGraphNode *> dep_graph = generate_import_dependency_graph(c);
 	defer ({
 		for_array(i, dep_graph) {
 			import_graph_node_destroy(dep_graph[i], heap_allocator());
@@ -2439,7 +2582,7 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
 		Scope *s = n->scope;
 
 		if (n->dep_count > 0) {
-			auto path = find_import_path(file_scopes, s, s);
+			auto path = find_import_path(&c->file_scopes, s, s);
 			defer (array_free(&path));
 
 			if (path.count > 0) {
@@ -2484,192 +2627,16 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
 
 	for_array(file_index, file_order) {
 		ImportGraphNode *node = file_order[file_index];
-		for_array(i, node->decls) {
-			AstNode *decl = node->decls[i];
-			Scope *parent_scope = decl->file->scope;
-			GB_ASSERT(parent_scope->is_file);
-
-			switch (decl->kind) {
-			case_ast_node(id, ImportDecl, decl);
-
-				Token token = id->relpath;
-				HashKey key = hash_string(id->fullpath);
-				Scope **found = map_get(file_scopes, key);
-				if (found == nullptr) {
-					for_array(scope_index, file_scopes->entries) {
-						Scope *scope = file_scopes->entries[scope_index].value;
-						gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath));
-					}
-					gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column);
-					GB_PANIC("Unable to find scope for file: %.*s", LIT(id->fullpath));
-				}
-				Scope *scope = *found;
-
-				if (scope->is_global) {
-					error(token, "Importing a #shared_global_scope is disallowed and unnecessary");
-					continue;
-				}
-
-				if (id->cond != nullptr) {
-					Operand operand = {Addressing_Invalid};
-					check_expr(c, &operand, id->cond);
-					if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
-						error(id->cond, "Non-constant boolean `when` condition");
-						continue;
-					}
-					if (operand.value.kind == ExactValue_Bool &&
-					    operand.value.value_bool == false) {
-						continue;
-					}
-				}
-
-				if (ptr_set_exists(&parent_scope->imported, scope)) {
-					// error(token, "Multiple import of the same file within this scope");
-				} else {
-					ptr_set_add(&parent_scope->imported, scope);
-				}
-
-				scope->has_been_imported = true;
+		AstFile *f = node->scope->file;
 
-				if (id->is_using) {
-					if (parent_scope->is_global) {
-						error(id->import_name, "#shared_global_scope imports cannot use using");
-					} else {
-						// NOTE(bill): Add imported entities to this file's scope
-						for_array(elem_index, scope->elements.entries) {
-							Entity *e = scope->elements.entries[elem_index].value;
-							if (e->scope == parent_scope) continue;
-
-							if (!is_entity_kind_exported(e->kind)) {
-								continue;
-							}
-							if (is_entity_exported(e)) {
-								// TODO(bill): Should these entities be imported but cause an error when used?
-								bool ok = add_entity(c, parent_scope, e->identifier, e);
-								if (ok) map_set(&parent_scope->implicit, hash_entity(e), true);
-							}
-						}
-					}
-				} else {
-					String import_name = path_to_entity_name(id->import_name.string, id->fullpath);
-					if (is_blank_ident(import_name)) {
-						error(token, "File name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
-					} else {
-						GB_ASSERT(id->import_name.pos.line != 0);
-						id->import_name.string = import_name;
-						Entity *e = make_entity_import_name(c->allocator, parent_scope, id->import_name, t_invalid,
-						                                    id->fullpath, id->import_name.string,
-						                                    scope);
-
-						add_entity(c, parent_scope, nullptr, e);
-					}
-				}
-			case_end;
-
-			case_ast_node(ed, ExportDecl, decl);
-				Token token = ed->relpath;
-				HashKey key = hash_string(ed->fullpath);
-				Scope **found = map_get(file_scopes, key);
-				if (found == nullptr) {
-					for_array(scope_index, file_scopes->entries) {
-						Scope *scope = file_scopes->entries[scope_index].value;
-						gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath));
-					}
-					gb_printf_err("%.*s(%td:%td)\n", LIT(token.pos.file), token.pos.line, token.pos.column);
-					GB_PANIC("Unable to find scope for file: %.*s", LIT(ed->fullpath));
-				}
-				Scope *scope = *found;
-
-				if (scope->is_global) {
-					error(token, "Exporting a #shared_global_scope is disallowed and unnecessary");
-					continue;
-				}
-
-				if (ed->cond != nullptr) {
-					Operand operand = {Addressing_Invalid};
-					check_expr(c, &operand, ed->cond);
-					if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
-						error(ed->cond, "Non-constant boolean `when` condition");
-						continue;
-					}
-					if (operand.value.kind == ExactValue_Bool &&
-					    operand.value.value_bool == false) {
-						continue;
-					}
-				}
-
-				if (ptr_set_exists(&parent_scope->imported, scope)) {
-					// error(token, "Multiple import of the same file within this scope");
-				} else {
-					ptr_set_add(&parent_scope->imported, scope);
-				}
-
-				scope->has_been_imported = true;
-				if (parent_scope->is_global) {
-					error(decl, "`export` cannot be used on #shared_global_scope");
-				} else {
-					// NOTE(bill): Add imported entities to this file's scope
-					for_array(elem_index, scope->elements.entries) {
-						Entity *e = scope->elements.entries[elem_index].value;
-						if (e->scope == parent_scope) continue;
-
-						if (is_entity_kind_exported(e->kind)) {
-							add_entity(c, parent_scope, e->identifier, e);
-						}
-					}
-				}
-			case_end;
-			}
-		}
-	}
-
-	for_array(i, c->delayed_foreign_libraries) {
-		AstNode *decl = c->delayed_foreign_libraries[i].decl;
-		ast_node(fl, ForeignLibraryDecl, decl);
-
-		// Scope *parent_scope = c->delayed_foreign_libraries[i].parent;
-		Scope *parent_scope = fl->parent->scope;
-		GB_ASSERT(parent_scope->is_file);
-
-		String file_str = fl->filepath.string;
-		String base_dir = fl->base_dir;
-
-		if (fl->token.kind == Token_foreign_library) {
-			gbAllocator a = heap_allocator(); // TODO(bill): Change this allocator
-
-			String rel_path = get_fullpath_relative(a, base_dir, file_str);
-			String import_file = rel_path;
-			if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
-				String abs_path = get_fullpath_core(a, file_str);
-				if (gb_file_exists(cast(char *)abs_path.text)) {
-					import_file = abs_path;
-				}
-			}
-			file_str = import_file;
-		}
+		CheckerContext prev_context = c->context;
+		defer (c->context = prev_context);
+		add_curr_ast_file(c, f);
 
-		if (fl->cond != nullptr) {
-			Operand operand = {Addressing_Invalid};
-			check_expr(c, &operand, fl->cond);
-			if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
-				error(fl->cond, "Non-constant boolean `when` condition");
-				continue;
-			}
-			if (operand.value.kind == ExactValue_Bool &&
-			    !operand.value.value_bool) {
-				continue;
-			}
-		}
+		c->context.allow_file_when_statement = true;
 
-		String library_name = path_to_entity_name(fl->library_name.string, file_str);
-		if (is_blank_ident(library_name)) {
-			error(decl, "File name, %.*s, cannot be as a library name as it is not a valid identifier", LIT(fl->library_name.string));
-		} else {
-			GB_ASSERT(fl->library_name.pos.line != 0);
-			fl->library_name.string = library_name;
-			Entity *e = make_entity_library_name(c->allocator, parent_scope, fl->library_name, t_invalid,
-			                                     file_str, library_name);
-			add_entity(c, parent_scope, nullptr, e);
+		for_array(i, f->decls) {
+			check_delayed_file_import_entities(c, f->decls[i]);
 		}
 	}
 }
@@ -2797,10 +2764,6 @@ void calculate_global_init_order(Checker *c) {
 
 
 void check_parsed_files(Checker *c) {
-	Map<Scope *> file_scopes; // Key: String (fullpath)
-	map_init(&file_scopes, heap_allocator());
-	defer (map_destroy(&file_scopes));
-
 	add_type_info_type(c, t_invalid);
 
 	// Map full filepaths to Scopes
@@ -2810,7 +2773,7 @@ void check_parsed_files(Checker *c) {
 
 		f->decl_info = make_declaration_info(c->allocator, f->scope, c->context.decl);
 		HashKey key = hash_string(f->tokenizer.fullpath);
-		map_set(&file_scopes, key, scope);
+		map_set(&c->file_scopes, key, scope);
 		map_set(&c->info.files, key, f);
 	}
 
@@ -2819,11 +2782,11 @@ void check_parsed_files(Checker *c) {
 		AstFile *f = c->parser->files[i];
 		CheckerContext prev_context = c->context;
 		add_curr_ast_file(c, f);
-		check_collect_entities(c, f->decls, true);
+		check_collect_entities(c, f->decls);
 		c->context = prev_context;
 	}
 
-	check_import_entities(c, &file_scopes);
+	check_import_entities(c);
 
 	check_all_global_entities(c);
 	init_preload(c); // NOTE(bill): This could be setup previously through the use of `type_info(_of_val)`
@@ -2935,8 +2898,8 @@ void check_parsed_files(Checker *c) {
 	// gb_printf_err("Count: %td\n", c->info.type_info_count++);
 
 	if (!build_context.is_dll) {
-		for_array(i, file_scopes.entries) {
-			Scope *s = file_scopes.entries[i].value;
+		for_array(i, c->file_scopes.entries) {
+			Scope *s = c->file_scopes.entries[i].value;
 			if (s->is_init) {
 				Entity *e = current_scope_lookup_entity(s, str_lit("main"));
 				if (e == nullptr) {

+ 2 - 9
src/map.cpp

@@ -101,8 +101,7 @@ struct Map {
 };
 
 
-template <typename T> void map_init             (Map<T> *h, gbAllocator a);
-template <typename T> void map_init_with_reserve(Map<T> *h, gbAllocator a, isize capacity);
+template <typename T> void map_init             (Map<T> *h, gbAllocator a, isize capacity = 16);
 template <typename T> void map_destroy          (Map<T> *h);
 template <typename T> T *  map_get              (Map<T> *h, HashKey key);
 template <typename T> void map_set              (Map<T> *h, HashKey key, T const &value);
@@ -123,13 +122,7 @@ template <typename T> void  multi_map_remove_all(Map<T> *h, HashKey key);
 
 
 template <typename T>
-gb_inline void map_init(Map<T> *h, gbAllocator a) {
-	array_init(&h->hashes,  a);
-	array_init(&h->entries, a);
-}
-
-template <typename T>
-gb_inline void map_init_with_reserve(Map<T> *h, gbAllocator a, isize capacity) {
+gb_inline void map_init(Map<T> *h, gbAllocator a, isize capacity) {
 	array_init(&h->hashes,  a, capacity);
 	array_init(&h->entries, a, capacity);
 }

+ 47 - 40
src/parser.cpp

@@ -36,30 +36,30 @@ struct ImportedFile {
 
 
 struct AstFile {
-	isize          id;
-	gbArena        arena;
-	Tokenizer      tokenizer;
-	Array<Token>   tokens;
-	isize          curr_token_index;
-	Token          curr_token;
-	Token          prev_token; // previous non-comment
+	isize               id;
+	gbArena             arena;
+	Tokenizer           tokenizer;
+	Array<Token>        tokens;
+	isize               curr_token_index;
+	Token               curr_token;
+	Token               prev_token; // previous non-comment
 
 	// >= 0: In Expression
 	// <  0: In Control Clause
 	// NOTE(bill): Used to prevent type literals in control clauses
-	isize          expr_level;
-	bool           allow_range; // NOTE(bill): Ranges are only allowed in certain cases
-	bool           in_foreign_block;
-	bool           allow_type;
+	isize               expr_level;
+	bool                allow_range; // NOTE(bill): Ranges are only allowed in certain cases
+	bool                in_foreign_block;
+	bool                allow_type;
 
-	Array<AstNode *> decls;
-	ImportedFileKind file_kind;
-	bool             is_global_scope;
+	Array<AstNode *>    decls;
+	ImportedFileKind    file_kind;
+	bool                is_global_scope;
 
-	AstNode *      curr_proc;
-	isize          scope_level;
-	Scope *        scope;       // NOTE(bill): Created in checker
-	DeclInfo *     decl_info;   // NOTE(bill): Created in checker
+	AstNode *           curr_proc;
+	isize               scope_level;
+	Scope *             scope;       // NOTE(bill): Created in checker
+	DeclInfo *          decl_info;   // NOTE(bill): Created in checker
 
 
 	CommentGroup        lead_comment; // Comment (block) before the decl
@@ -68,7 +68,6 @@ struct AstFile {
 	Array<CommentGroup> comments;     // All the comments!
 
 
-	// TODO(bill): Error recovery
 #define PARSER_MAX_FIX_COUNT 6
 	isize    fix_count;
 	TokenPos fix_prev_pos;
@@ -350,8 +349,6 @@ AST_NODE_KIND(_DeclBegin,      "", i32) \
 		Token    relpath;       \
 		String   fullpath;      \
 		Token    import_name;   \
-		AstNode *cond;          \
-		AstFile *parent;        \
 		CommentGroup docs;      \
 		CommentGroup comment;   \
 	}) \
@@ -359,8 +356,6 @@ AST_NODE_KIND(_DeclBegin,      "", i32) \
 		Token    token;         \
 		Token    relpath;       \
 		String   fullpath;      \
-		AstNode *cond;          \
-		AstFile *parent;        \
 		CommentGroup docs;      \
 		CommentGroup comment;   \
 	}) \
@@ -369,8 +364,6 @@ AST_NODE_KIND(_DeclBegin,      "", i32) \
 		Token    filepath;      \
 		Token    library_name;  \
 		String   base_dir;      \
-		AstNode *cond;          \
-		AstFile *parent;        \
 		CommentGroup docs;      \
 		CommentGroup comment;   \
 	}) \
@@ -831,9 +824,6 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
 		n->ValueDecl.type   = clone_ast_node(a, n->ValueDecl.type);
 		n->ValueDecl.values = clone_ast_node_array(a, n->ValueDecl.values);
 		break;
-	case AstNode_ForeignLibraryDecl:
-		n->ForeignLibraryDecl.cond = clone_ast_node(a, n->ForeignLibraryDecl.cond);
-		break;
 
 	case AstNode_Field:
 		n->Field.names = clone_ast_node_array(a, n->Field.names);
@@ -1547,8 +1537,6 @@ AstNode *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath,
 	result->ImportDecl.is_using    = is_using;
 	result->ImportDecl.relpath     = relpath;
 	result->ImportDecl.import_name = import_name;
-	result->ImportDecl.cond        = cond;
-	result->ImportDecl.parent      = f;
 	result->ImportDecl.docs        = docs;
 	result->ImportDecl.comment     = comment;
 	return result;
@@ -1559,8 +1547,6 @@ AstNode *ast_export_decl(AstFile *f, Token token, Token relpath, AstNode *cond,
 	AstNode *result = make_ast_node(f, AstNode_ExportDecl);
 	result->ExportDecl.token       = token;
 	result->ExportDecl.relpath     = relpath;
-	result->ExportDecl.cond        = cond;
-	result->ExportDecl.parent      = f;
 	result->ExportDecl.docs        = docs;
 	result->ExportDecl.comment     = comment;
 	return result;
@@ -1572,8 +1558,6 @@ AstNode *ast_foreign_library_decl(AstFile *f, Token token, Token filepath, Token
 	result->ForeignLibraryDecl.token        = token;
 	result->ForeignLibraryDecl.filepath     = filepath;
 	result->ForeignLibraryDecl.library_name = library_name;
-	result->ForeignLibraryDecl.cond         = cond;
-	result->ForeignLibraryDecl.parent       = f;
 	result->ForeignLibraryDecl.docs         = docs;
 	result->ForeignLibraryDecl.comment      = comment;
 	return result;
@@ -1765,7 +1749,6 @@ bool is_blank_ident(AstNode *node) {
 
 // NOTE(bill): Go to next statement to prevent numerous error messages popping up
 void fix_advance_to_next_stmt(AstFile *f) {
-	// TODO(bill): fix_advance_to_next_stmt
 #if 1
 	for (;;) {
 		Token t = f->curr_token;
@@ -1999,9 +1982,9 @@ void check_proc_add_tag(AstFile *f, AstNode *tag_expr, u64 *tags, ProcTag tag, S
 }
 
 bool is_foreign_name_valid(String name) {
-	// TODO(bill): is_foreign_name_valid
-	if (name.len == 0)
+	if (name.len == 0) {
 		return false;
+	}
 	isize offset = 0;
 	while (offset < name.len) {
 		Rune rune;
@@ -2047,7 +2030,6 @@ bool is_foreign_name_valid(String name) {
 }
 
 void parse_proc_tags(AstFile *f, u64 *tags, String *link_name, ProcCallingConvention *calling_convention) {
-	// TODO(bill): Add this to procedure literals too
 	GB_ASSERT(tags         != nullptr);
 	GB_ASSERT(link_name    != nullptr);
 
@@ -2067,7 +2049,6 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *link_name, ProcCallingConven
 			check_proc_add_tag(f, tag_expr, tags, ProcTag_link_name, tag_name);
 			if (f->curr_token.kind == Token_String) {
 				*link_name = f->curr_token.string;
-				// TODO(bill): Check if valid string
 				if (!is_foreign_name_valid(*link_name)) {
 					syntax_error(tag_expr, "Invalid alternative link procedure name `%.*s`", LIT(*link_name));
 				}
@@ -4852,12 +4833,35 @@ bool determine_path_from_string(Parser *p, AstNode *node, String base_dir, Strin
 	return true;
 }
 
+
+void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<AstNode *> decls);
+
+void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstNodeWhenStmt *ws) {
+	if (ws->body != nullptr) {
+		auto stmts = ws->body->BlockStmt.stmts;
+		parse_setup_file_decls(p, f, base_dir, stmts);
+	}
+
+	if (ws->else_stmt != nullptr) {
+		switch (ws->else_stmt->kind) {
+		case AstNode_BlockStmt: {
+			auto stmts = ws->else_stmt->BlockStmt.stmts;
+			parse_setup_file_decls(p, f, base_dir, stmts);
+		} break;
+		case AstNode_WhenStmt:
+			parse_setup_file_when_stmt(p, f, base_dir, &ws->else_stmt->WhenStmt);
+			break;
+		}
+	}
+}
+
 void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<AstNode *> decls) {
 	for_array(i, decls) {
 		AstNode *node = decls[i];
 		if (!is_ast_node_decl(node) &&
 		    node->kind != AstNode_BadStmt &&
-		    node->kind != AstNode_EmptyStmt) {
+		    node->kind != AstNode_EmptyStmt &&
+		    node->kind != AstNode_WhenStmt) {
 			// NOTE(bill): Sanity check
 			syntax_error(node, "Only declarations are allowed at file scope, got %.*s", LIT(ast_node_strings[node->kind]));
 		} else if (node->kind == AstNode_ImportDecl) {
@@ -4899,6 +4903,9 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<AstNod
 			} else {
 				fl->base_dir = base_dir;
 			}
+		} else if (node->kind == AstNode_WhenStmt) {
+			ast_node(ws, WhenStmt, node);
+			parse_setup_file_when_stmt(p, f, base_dir, ws);
 		}
 	}
 }