Browse Source

Big simplification and improvement of the entity collection system, reducing unneeded steps for packages

gingerBill 4 years ago
parent
commit
9cd5ea59dd
7 changed files with 331 additions and 274 deletions
  1. 2 0
      core/sys/windows/types.odin
  2. 148 165
      src/checker.cpp
  3. 2 2
      src/checker.hpp
  4. 148 105
      src/llvm_backend.cpp
  5. 7 0
      src/parser.cpp
  6. 16 1
      src/parser.hpp
  7. 8 1
      src/queue.cpp

+ 2 - 0
core/sys/windows/types.odin

@@ -397,6 +397,8 @@ when size_of(uintptr) == 4 {
 		szDescription: [WSADESCRIPTION_LEN + 1]u8,
 		szSystemStatus: [WSASYS_STATUS_LEN + 1]u8,
 	}
+} else {
+	#panic("unknown word size");
 }
 
 WSABUF :: struct {

+ 148 - 165
src/checker.cpp

@@ -228,9 +228,6 @@ Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capaci
 	string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
 	ptr_set_init(&s->imported, heap_allocator(), 0);
 
-	s->delayed_imports.allocator = heap_allocator();
-	s->delayed_directives.allocator = heap_allocator();
-
 	if (parent != nullptr && parent != builtin_pkg->scope) {
 		Scope *prev_head_child = parent->head_child.exchange(s, std::memory_order_acq_rel);
 		if (prev_head_child) {
@@ -253,8 +250,6 @@ Scope *create_scope_from_file(CheckerInfo *info, AstFile *f) {
 	isize init_elements_capacity = gb_max(DEFAULT_SCOPE_CAPACITY, 2*f->total_file_decl_count);
 	Scope *s = create_scope(info, f->pkg->scope, init_elements_capacity);
 
-	array_reserve(&s->delayed_imports, f->imports.count);
-	array_reserve(&s->delayed_directives, f->directive_count);
 
 	s->flags |= ScopeFlag_File;
 	s->file = f;
@@ -311,8 +306,6 @@ void destroy_scope(Scope *scope) {
 	}
 
 	string_map_destroy(&scope->elements);
-	array_free(&scope->delayed_imports);
-	array_free(&scope->delayed_directives);
 	ptr_set_destroy(&scope->imported);
 
 	// NOTE(bill): No need to free scope as it "should" be allocated in an arena (except for the global scope)
@@ -872,6 +865,7 @@ void init_checker_info(CheckerInfo *i) {
 	gb_mutex_init(&i->lazy_mutex);
 
 	mutex_init(&i->type_info_mutex);
+	mutex_init(&i->scope_mutex);
 	mutex_init(&i->deps_mutex);
 	mutex_init(&i->identifier_uses_mutex);
 	mutex_init(&i->foreign_mutex);
@@ -903,6 +897,7 @@ void destroy_checker_info(CheckerInfo *i) {
 	gb_mutex_destroy(&i->gen_types_mutex);
 	gb_mutex_destroy(&i->lazy_mutex);
 	mutex_destroy(&i->type_info_mutex);
+	mutex_destroy(&i->scope_mutex);
 	mutex_destroy(&i->deps_mutex);
 	mutex_destroy(&i->identifier_uses_mutex);
 	mutex_destroy(&i->foreign_mutex);
@@ -1352,15 +1347,22 @@ void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, Dec
 	if (e->scope != nullptr) {
 		Scope *scope = e->scope;
 
-		if (scope->flags & ScopeFlag_File) {
-			if (is_entity_kind_exported(e->kind) && is_exported) {
-				AstPackage *pkg = scope->file->pkg;
-				GB_ASSERT(pkg->scope == scope->parent);
-				GB_ASSERT(c->pkg == pkg);
-				scope = pkg->scope;
-			}
+		if (scope->flags & ScopeFlag_File && is_entity_kind_exported(e->kind) && is_exported) {
+			AstPackage *pkg = scope->file->pkg;
+			GB_ASSERT(pkg->scope == scope->parent);
+			GB_ASSERT(c->pkg == pkg);
+
+			// NOTE(bill): as multiple threads could be accessing this, it needs to be wrapped
+			// The current hash map for scopes is not thread safe
+			AstPackageExportedEntity ee = {identifier, e};
+			mpmc_enqueue(&pkg->exported_entity_queue, ee);
+
+			// mutex_lock(&c->info->scope_mutex);
+			// add_entity(c, pkg->scope, identifier, e);
+			// mutex_unlock(&c->info->scope_mutex);
+		} else {
+			add_entity(c, scope, identifier, e);
 		}
-		add_entity(c, scope, identifier, e);
 	}
 
 	CheckerInfo *info = c->info;
@@ -3330,14 +3332,23 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
 
 // NOTE(bill): If file_scopes == nullptr, this will act like a local scope
 void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
+	AstFile *curr_file = nullptr;
+	if ((c->scope->flags&ScopeFlag_File) != 0) {
+		curr_file = c->scope->file;
+		GB_ASSERT(curr_file != nullptr);
+	}
+
+
 	for_array(decl_index, nodes) {
 		Ast *decl = nodes[decl_index];
 		if (!is_ast_decl(decl) && !is_ast_when_stmt(decl)) {
-			if ((c->scope->flags&ScopeFlag_File) != 0 && decl->kind == Ast_ExprStmt) {
+			if (curr_file && decl->kind == Ast_ExprStmt) {
 				Ast *expr = decl->ExprStmt.expr;
 				if (expr->kind == Ast_CallExpr && expr->CallExpr.proc->kind == Ast_BasicDirective) {
 					if (c->collect_delayed_decls) {
-						array_add(&c->scope->delayed_directives, expr);
+						if (decl->state_flags & StateFlag_BeenHandled) return;
+						decl->state_flags |= StateFlag_BeenHandled;
+						mpmc_enqueue(&curr_file->delayed_decls_queues[AstDelayQueue_Expr], expr);
 					}
 					continue;
 				}
@@ -3358,15 +3369,14 @@ void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
 		case_end;
 
 		case_ast_node(id, ImportDecl, decl);
-			if ((c->scope->flags&ScopeFlag_File) == 0) {
+			if (curr_file == nullptr) {
 				error(decl, "import declarations are only allowed in the file scope");
 				// NOTE(bill): _Should_ be caught by the parser
 				// TODO(bill): Better error handling if it isn't
 				continue;
 			}
-			if (c->collect_delayed_decls) {
-				array_add(&c->scope->delayed_imports, decl);
-			}
+			// Will be handled later
+			mpmc_enqueue(&curr_file->delayed_decls_queues[AstDelayQueue_Import], decl);
 		case_end;
 
 		case_ast_node(fl, ForeignImportDecl, decl);
@@ -3391,15 +3401,14 @@ void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
 		}
 	}
 
+
 	// 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->scope->flags&ScopeFlag_File) == 0 || c->collect_delayed_decls) {
-		for_array(i, nodes) {
-			Ast *node = nodes[i];
-			switch (node->kind) {
-			case_ast_node(ws, WhenStmt, node);
-				check_collect_entities_from_when_stmt(c, ws);
-			case_end;
+	if (curr_file == nullptr) {
+		for_array(decl_index, nodes) {
+			Ast *decl = nodes[decl_index];
+			if (decl->kind == Ast_WhenStmt) {
+				check_collect_entities_from_when_stmt(c, &decl->WhenStmt);
 			}
 		}
 	}
@@ -3434,11 +3443,6 @@ void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d) {
 	ctx->decl = d;
 	ctx->scope = d->scope;
 
-	if (!e->pkg->used) {
-		return;
-	}
-
-
 	if (pkg->kind == Package_Init) {
 		if (e->kind != Entity_Procedure && e->token.string == "main") {
 			error(e->token, "'main' is reserved as the entry point procedure in the initial scope");
@@ -3733,11 +3737,9 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
 
 	if (id->fullpath == "builtin") {
 		scope = builtin_pkg->scope;
-		builtin_pkg->used = true;
 		force_use = true;
 	} else if (id->fullpath == "intrinsics") {
 		scope = intrinsics_pkg->scope;
-		intrinsics_pkg->used = true;
 		force_use = true;
 	} else {
 		AstPackage **found = string_map_get(pkgs, id->fullpath);
@@ -3750,7 +3752,6 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
 			GB_PANIC("Unable to find scope for package: %.*s", LIT(id->fullpath));
 		} else {
 			AstPackage *pkg = *found;
-			pkg->used = true;
 			scope = pkg->scope;
 		}
 	}
@@ -3889,27 +3890,6 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
 	}
 }
 
-bool collect_checked_packages_from_decl_list(Checker *c, Slice<Ast *> const &decls) {
-	bool new_files = false;
-	for_array(i, decls) {
-		Ast *decl = decls[i];
-		switch (decl->kind) {
-		case_ast_node(id, ImportDecl, decl);
-			AstPackage **found = string_map_get(&c->info.packages, id->fullpath);
-			if (found == nullptr) {
-				continue;
-			}
-			AstPackage *pkg = *found;
-			if (!pkg->used) {
-				new_files = true;
-				pkg->used = true;
-			}
-		case_end;
-		}
-	}
-	return new_files;
-}
-
 // Returns true if a new package is present
 bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls);
 bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws);
@@ -3933,11 +3913,11 @@ bool collect_when_stmt_from_file(CheckerContext *ctx, AstWhenStmt *ws) {
 		error(ws->cond, "Invalid body for 'when' statement");
 	} else {
 		if (ws->determined_cond) {
-			return collect_checked_packages_from_decl_list(ctx->checker, ws->body->BlockStmt.stmts);
+			//
 		} else if (ws->else_stmt) {
 			switch (ws->else_stmt->kind) {
 			case Ast_BlockStmt:
-				return collect_checked_packages_from_decl_list(ctx->checker, ws->else_stmt->BlockStmt.stmts);
+				return false;
 			case Ast_WhenStmt:
 				return collect_when_stmt_from_file(ctx, &ws->else_stmt->WhenStmt);
 			default:
@@ -3986,68 +3966,77 @@ bool collect_file_decls_from_when_stmt(CheckerContext *ctx, AstWhenStmt *ws) {
 	return false;
 }
 
-bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls) {
+
+bool collect_file_decl(CheckerContext *ctx, Ast *decl) {
 	GB_ASSERT(ctx->scope->flags&ScopeFlag_File);
 
-	if (collect_checked_packages_from_decl_list(ctx->checker, decls)) {
-		return true;
-	}
+	AstFile *curr_file = ctx->scope->file;
+	GB_ASSERT(curr_file != nullptr);
 
-	for_array(i, decls) {
-		Ast *decl = decls[i];
-		if (decl->state_flags & StateFlag_BeenHandled) {
-			continue;
-		}
+	if (decl->state_flags & StateFlag_BeenHandled) {
+		return false;
+	}
 
-		switch (decl->kind) {
-		case_ast_node(vd, ValueDecl, decl);
-			check_collect_value_decl(ctx, decl);
-		case_end;
+	switch (decl->kind) {
+	case_ast_node(vd, ValueDecl, decl);
+		check_collect_value_decl(ctx, decl);
+	case_end;
 
-		case_ast_node(id, ImportDecl, decl);
-			check_add_import_decl(ctx, decl);
-		case_end;
+	case_ast_node(id, ImportDecl, decl);
+		check_add_import_decl(ctx, decl);
+	case_end;
 
-		case_ast_node(fl, ForeignImportDecl, decl);
-			check_add_foreign_import_decl(ctx, decl);
-		case_end;
+	case_ast_node(fl, ForeignImportDecl, decl);
+		check_add_foreign_import_decl(ctx, decl);
+	case_end;
 
-		case_ast_node(fb, ForeignBlockDecl, decl);
-			check_add_foreign_block_decl(ctx, decl);
-		case_end;
+	case_ast_node(fb, ForeignBlockDecl, decl);
+		check_add_foreign_block_decl(ctx, decl);
+	case_end;
 
-		case_ast_node(ws, WhenStmt, decl);
-			if (!ws->is_cond_determined) {
-				if (collect_when_stmt_from_file(ctx, ws)) {
-					return true;
-				}
+	case_ast_node(ws, WhenStmt, decl);
+		if (!ws->is_cond_determined) {
+			if (collect_when_stmt_from_file(ctx, ws)) {
+				return true;
+			}
 
-				CheckerContext nctx = *ctx;
-				nctx.collect_delayed_decls = true;
+			CheckerContext nctx = *ctx;
+			nctx.collect_delayed_decls = true;
 
-				if (collect_file_decls_from_when_stmt(&nctx, ws)) {
-					return true;
-				}
-			} else {
-				CheckerContext nctx = *ctx;
-				nctx.collect_delayed_decls = true;
+			if (collect_file_decls_from_when_stmt(&nctx, ws)) {
+				return true;
+			}
+		} else {
+			CheckerContext nctx = *ctx;
+			nctx.collect_delayed_decls = true;
 
-				if (collect_file_decls_from_when_stmt(&nctx, ws)) {
-					return true;
-				}
+			if (collect_file_decls_from_when_stmt(&nctx, ws)) {
+				return true;
 			}
-		case_end;
+		}
+	case_end;
 
-		case_ast_node(es, ExprStmt, decl);
-			if (es->expr->kind == Ast_CallExpr) {
-				ast_node(ce, CallExpr, es->expr);
-				if (ce->proc->kind == Ast_BasicDirective) {
-					if (ctx->collect_delayed_decls) {
-						array_add(&ctx->scope->delayed_directives, es->expr);
-					}
-				}
+	case_ast_node(es, ExprStmt, decl);
+		GB_ASSERT(ctx->collect_delayed_decls);
+		decl->state_flags |= StateFlag_BeenHandled;
+		if (es->expr->kind == Ast_CallExpr) {
+			ast_node(ce, CallExpr, es->expr);
+			if (ce->proc->kind == Ast_BasicDirective) {
+				mpmc_enqueue(&curr_file->delayed_decls_queues[AstDelayQueue_Expr], es->expr);
 			}
-		case_end;
+		}
+	case_end;
+	}
+
+	return false;
+}
+
+bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls) {
+	GB_ASSERT(ctx->scope->flags&ScopeFlag_File);
+
+	for_array(i, decls) {
+		if (collect_file_decl(ctx, decls[i])) {
+			return true;
 		}
 	}
 
@@ -4064,9 +4053,10 @@ void check_create_file_scopes(Checker *c) {
 			string_map_set(&c->info.files, f->fullpath, f);
 
 			create_scope_from_file(nullptr, f);
+			total_pkg_decl_count += f->total_file_decl_count;
 		}
 
-		pkg->used = true;
+		mpmc_init(&pkg->exported_entity_queue, heap_allocator(), total_pkg_decl_count);
 	}
 }
 
@@ -4086,7 +4076,10 @@ GB_THREAD_PROC(thread_proc_collect_entities) {
 
 	CheckerContext *ctx = &collect_entity_ctx;
 
-	for (isize i = data->file_offset; i < data->file_count; i++) {
+	isize file_offset = data->file_offset;
+	isize file_end = gb_min(file_offset+data->file_count, c->info.files.entries.count);
+
+	for (isize i = file_offset; i < file_end; i++) {
 		AstFile *f = c->info.files.entries[i].value;
 		reset_checker_context(ctx, f);
 		check_collect_entities(ctx, f->decls);
@@ -4126,6 +4119,7 @@ void check_collect_entities_all(Checker *c) {
 	isize file_load_count = (total_file_count+thread_count-1)/thread_count;
 	isize remaining_file_count = c->info.files.entries.count;
 
+
 	ThreadProcCollectEntities *thread_data = gb_alloc_array(permanent_allocator(), ThreadProcCollectEntities, thread_count);
 	for (isize i = 0; i < thread_count; i++) {
 		ThreadProcCollectEntities *data = thread_data + i;
@@ -4155,6 +4149,27 @@ void check_collect_entities_all(Checker *c) {
 	}
 }
 
+void check_export_entites(Checker *c) {
+	CheckerContext ctx = make_checker_context(c);
+
+	for_array(i, c->info.packages.entries) {
+		AstPackage *pkg = c->info.packages.entries[i].value;
+		if (pkg->files.count == 0) {
+			continue; // Sanity check
+		}
+
+
+		AstPackageExportedEntity item = {};
+		while (mpmc_dequeue(&pkg->exported_entity_queue, &item)) {
+			AstFile *f = item.entity->file;
+			if (ctx.file != f) {
+				reset_checker_context(&ctx, f);
+			}
+			add_entity(&ctx, pkg->scope, item.identifier, item.entity);
+		}
+	}
+}
+
 void check_import_entities(Checker *c) {
 #define TIME_SECTION(str) do { debugf("[Section] %s\n", str); if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
 
@@ -4167,7 +4182,7 @@ void check_import_entities(Checker *c) {
 	});
 
 
-	TIME_SECTION("check_import_entities - cycles");
+	TIME_SECTION("check_import_entities - sort packages");
 	// NOTE(bill): Priority queue
 	auto pq = priority_queue_create(dep_graph, import_graph_node_cmp, import_graph_node_swap);
 
@@ -4221,68 +4236,27 @@ void check_import_entities(Checker *c) {
 		array_add(&package_order, n);
 	}
 
-	TIME_SECTION("check_import_entities - used");
-	for_array(i, c->parser->packages) {
-		AstPackage *pkg = c->parser->packages[i];
-		switch (pkg->kind) {
-		case Package_Init:
-		case Package_Runtime:
-			pkg->used = true;
-			break;
-		}
-	}
-
-	TIME_SECTION("check_import_entities - collect checked packages from decl list");
+	TIME_SECTION("check_import_entities - collect file decls");
 	CheckerContext ctx = make_checker_context(c);
 
-	for (isize loop_count = 0; ; loop_count++) {
-		bool new_files = false;
-		for_array(i, package_order) {
-			ImportGraphNode *node = package_order[i];
-			GB_ASSERT(node->scope->flags&ScopeFlag_Pkg);
-			AstPackage *pkg = node->scope->pkg;
-			if (!pkg->used) {
-				continue;
-			}
-
-			for_array(i, pkg->files) {
-				AstFile *f = pkg->files[i];
-				reset_checker_context(&ctx, f);
-				new_files |= collect_checked_packages_from_decl_list(c, f->decls);
-			}
-		}
-
-		if (!new_files) {
-			break;
-		}
-	}
-
-	TIME_SECTION("check_import_entities - collect file decls");
-	for (isize pkg_index = 0; pkg_index < package_order.count; pkg_index++) {
+	for_array(pkg_index, package_order) {
 		ImportGraphNode *node = package_order[pkg_index];
 		AstPackage *pkg = node->pkg;
 
-		if (!pkg->used) {
-			continue;
-		}
-
-		bool new_packages = false;
-
 		for_array(i, pkg->files) {
 			AstFile *f = pkg->files[i];
 
 			reset_checker_context(&ctx, f);
 			ctx.collect_delayed_decls = true;
 
-			if (collect_file_decls(&ctx, f->decls)) {
-				new_packages = true;
-				break;
+			MPMCQueue<Ast *> *q = nullptr;
+
+			// Check import declarations first to simplify things
+			for (Ast *id = nullptr; mpmc_dequeue(&f->delayed_decls_queues[AstDelayQueue_Import], &id); /**/) {
+				check_add_import_decl(&ctx, id);
 			}
-		}
 
-		if (new_packages) {
-			pkg_index = -1;
-			continue;
+			collect_file_decls(&ctx, f->decls);
 		}
 	}
 
@@ -4296,8 +4270,8 @@ void check_import_entities(Checker *c) {
 			AstFile *f = pkg->files[i];
 			reset_checker_context(&ctx, f);
 
-			for_array(j, f->scope->delayed_imports) {
-				Ast *decl = f->scope->delayed_imports[j];
+			auto *q = &f->delayed_decls_queues[AstDelayQueue_Import];
+			for (Ast *decl = nullptr; mpmc_dequeue(q, &decl); /**/) {
 				check_add_import_decl(&ctx, decl);
 			}
 		}
@@ -4305,8 +4279,8 @@ void check_import_entities(Checker *c) {
 			AstFile *f = pkg->files[i];
 			reset_checker_context(&ctx, f);
 
-			for_array(j, f->scope->delayed_directives) {
-				Ast *expr = f->scope->delayed_directives[j];
+			auto *q = &f->delayed_decls_queues[AstDelayQueue_Expr];
+			for (Ast *expr = nullptr; mpmc_dequeue(q, &expr); /**/) {
 				Operand o = {};
 				check_expr(&ctx, &o, expr);
 			}
@@ -5014,12 +4988,21 @@ void check_parsed_files(Checker *c) {
 	check_create_file_scopes(c);
 
 	TIME_SECTION("collect entities");
-	// Collect Entities
 	check_collect_entities_all(c);
 
-	TIME_SECTION("import entities");
+	TIME_SECTION("export entities - pre");
+	check_export_entites(c);
+
+	// TIME_SECTION("import entities");
 	check_import_entities(c);
 
+	TIME_SECTION("export entities - post");
+	check_export_entites(c);
+
+	// if (true) {
+	// 	return;
+	// }
+
 	TIME_SECTION("add entities from packages");
 	check_add_entities_from_queues(c);
 

+ 2 - 2
src/checker.hpp

@@ -191,8 +191,6 @@ struct Scope {
 	std::atomic<Scope *> head_child;
 
 	StringMap<Entity *> elements;
-	Array<Ast *>    delayed_directives;
-	Array<Ast *>    delayed_imports;
 	PtrSet<Scope *> imported;
 
 	i32             flags; // ScopeFlag
@@ -295,6 +293,8 @@ struct CheckerInfo {
 	// NOTE(bill): If the semantic checker (check_proc_body) is to ever to be multithreaded,
 	// these variables will be of contention
 
+	BlockingMutex scope_mutex;
+
 	// NOT recursive & Only used at the end of `check_proc_body`
 	// This is a possible source of contention but probably not
 	// too much of a problem in practice

+ 148 - 105
src/llvm_backend.cpp

@@ -7778,6 +7778,18 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
 
 	case Token_CmpEq:
 	case Token_NotEq:
+		if (is_type_untyped_nil(be->right->tav.type)) {
+			lbValue left = lb_build_expr(p, be->left);
+			lbValue cmp = lb_emit_comp_against_nil(p, be->op.kind, left);
+			Type *type = default_type(tv.type);
+			return lb_emit_conv(p, cmp, type);
+		} else if (is_type_untyped_nil(be->left->tav.type)) {
+			lbValue right = lb_build_expr(p, be->right);
+			lbValue cmp = lb_emit_comp_against_nil(p, be->op.kind, right);
+			Type *type = default_type(tv.type);
+			return lb_emit_conv(p, cmp, type);
+		}
+		/*fallthrough*/
 	case Token_Lt:
 	case Token_LtEq:
 	case Token_Gt:
@@ -11337,133 +11349,164 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
 	lbValue res = {};
 	res.type = t_llvm_bool;
 	Type *t = x.type;
-	if (is_type_enum(t)) {
-		if (op_kind == Token_CmpEq) {
-			res.value = LLVMBuildIsNull(p->builder, x.value, "");
-		} else if (op_kind == Token_NotEq) {
-			res.value = LLVMBuildIsNotNull(p->builder, x.value, "");
-		}
-		return res;
-	} else if (is_type_pointer(t)) {
-		if (op_kind == Token_CmpEq) {
-			res.value = LLVMBuildIsNull(p->builder, x.value, "");
-		} else if (op_kind == Token_NotEq) {
-			res.value = LLVMBuildIsNotNull(p->builder, x.value, "");
-		}
-		return res;
-	} else if (is_type_cstring(t)) {
-		lbValue ptr = lb_emit_conv(p, x, t_u8_ptr);
-		if (op_kind == Token_CmpEq) {
-			res.value = LLVMBuildIsNull(p->builder, ptr.value, "");
-		} else if (op_kind == Token_NotEq) {
-			res.value = LLVMBuildIsNotNull(p->builder, ptr.value, "");
+	Type *bt = base_type(t);
+	TypeKind type_kind = bt->kind;
+
+	switch (type_kind) {
+	case Type_Basic:
+		switch (bt->Basic.kind) {
+		case Basic_rawptr:
+		case Basic_cstring:
+			if (op_kind == Token_CmpEq) {
+				res.value = LLVMBuildIsNull(p->builder, x.value, "");
+			} else if (op_kind == Token_NotEq) {
+				res.value = LLVMBuildIsNotNull(p->builder, x.value, "");
+			}
+			return res;
+		case Basic_any:
+			{
+				// TODO(bill): is this correct behaviour for nil comparison for any?
+				lbValue data = lb_emit_struct_ev(p, x, 0);
+				lbValue ti   = lb_emit_struct_ev(p, x, 1);
+				if (op_kind == Token_CmpEq) {
+					LLVMValueRef a =  LLVMBuildIsNull(p->builder, data.value, "");
+					LLVMValueRef b =  LLVMBuildIsNull(p->builder, ti.value, "");
+					res.value = LLVMBuildOr(p->builder, a, b, "");
+					return res;
+				} else if (op_kind == Token_NotEq) {
+					LLVMValueRef a =  LLVMBuildIsNotNull(p->builder, data.value, "");
+					LLVMValueRef b =  LLVMBuildIsNotNull(p->builder, ti.value, "");
+					res.value = LLVMBuildAnd(p->builder, a, b, "");
+					return res;
+				}
+			}
+			break;
+		case Basic_typeid:
+			lbValue invalid_typeid = lb_const_value(p->module, t_typeid, exact_value_i64(0));
+			return lb_emit_comp(p, op_kind, x, invalid_typeid);
 		}
-		return res;
-	} else if (is_type_proc(t)) {
+		break;
+
+	case Type_Enum:
+	case Type_Pointer:
+	case Type_Proc:
+	case Type_BitSet:
 		if (op_kind == Token_CmpEq) {
 			res.value = LLVMBuildIsNull(p->builder, x.value, "");
 		} else if (op_kind == Token_NotEq) {
 			res.value = LLVMBuildIsNotNull(p->builder, x.value, "");
 		}
 		return res;
-	} else if (is_type_any(t)) {
-		// TODO(bill): is this correct behaviour for nil comparison for any?
-		lbValue data = lb_emit_struct_ev(p, x, 0);
-		lbValue ti   = lb_emit_struct_ev(p, x, 1);
-		if (op_kind == Token_CmpEq) {
-			LLVMValueRef a =  LLVMBuildIsNull(p->builder, data.value, "");
-			LLVMValueRef b =  LLVMBuildIsNull(p->builder, ti.value, "");
-			res.value = LLVMBuildOr(p->builder, a, b, "");
-			return res;
-		} else if (op_kind == Token_NotEq) {
-			LLVMValueRef a =  LLVMBuildIsNotNull(p->builder, data.value, "");
-			LLVMValueRef b =  LLVMBuildIsNotNull(p->builder, ti.value, "");
-			res.value = LLVMBuildAnd(p->builder, a, b, "");
-			return res;
-		}
-	} else if (is_type_slice(t)) {
-		lbValue data = lb_emit_struct_ev(p, x, 0);
-		if (op_kind == Token_CmpEq) {
-			res.value = LLVMBuildIsNull(p->builder, data.value, "");
-			return res;
-		} else if (op_kind == Token_NotEq) {
-			res.value = LLVMBuildIsNotNull(p->builder, data.value, "");
-			return res;
-		}
-	} else if (is_type_dynamic_array(t)) {
-		lbValue data = lb_emit_struct_ev(p, x, 0);
-		if (op_kind == Token_CmpEq) {
-			res.value = LLVMBuildIsNull(p->builder, data.value, "");
-			return res;
-		} else if (op_kind == Token_NotEq) {
-			res.value = LLVMBuildIsNotNull(p->builder, data.value, "");
-			return res;
-		}
-	} else if (is_type_map(t)) {
-		lbValue hashes = lb_emit_struct_ev(p, x, 0);
-		lbValue data = lb_emit_struct_ev(p, hashes, 0);
-		return lb_emit_comp(p, op_kind, data, lb_zero(p->module, data.type));
-	} else if (is_type_union(t)) {
-		if (type_size_of(t) == 0) {
+
+	case Type_Slice:
+		{
+			lbValue data = lb_emit_struct_ev(p, x, 0);
 			if (op_kind == Token_CmpEq) {
-				return lb_const_bool(p->module, t_llvm_bool, true);
+				res.value = LLVMBuildIsNull(p->builder, data.value, "");
+				return res;
 			} else if (op_kind == Token_NotEq) {
-				return lb_const_bool(p->module, t_llvm_bool, false);
+				res.value = LLVMBuildIsNotNull(p->builder, data.value, "");
+				return res;
 			}
-		} else if (is_type_union_maybe_pointer(t)) {
-			lbValue tag = lb_emit_transmute(p, x, t_rawptr);
-			return lb_emit_comp_against_nil(p, op_kind, tag);
-		} else {
-			lbValue tag = lb_emit_union_tag_value(p, x);
-			return lb_emit_comp(p, op_kind, tag, lb_zero(p->module, tag.type));
 		}
-	} else if (is_type_typeid(t)) {
-		lbValue invalid_typeid = lb_const_value(p->module, t_typeid, exact_value_i64(0));
-		return lb_emit_comp(p, op_kind, x, invalid_typeid);
-	} else if (is_type_soa_struct(t)) {
-		Type *bt = base_type(t);
-		if (bt->Struct.soa_kind == StructSoa_Slice) {
-			LLVMValueRef the_value = {};
-			if (bt->Struct.fields.count == 0) {
-				lbValue len = lb_soa_struct_len(p, x);
-				the_value = len.value;
-			} else {
-				lbValue first_field = lb_emit_struct_ev(p, x, 0);
-				the_value = first_field.value;
-			}
+		break;
+
+	case Type_DynamicArray:
+		{
+			lbValue data = lb_emit_struct_ev(p, x, 0);
 			if (op_kind == Token_CmpEq) {
-				res.value = LLVMBuildIsNull(p->builder, the_value, "");
+				res.value = LLVMBuildIsNull(p->builder, data.value, "");
 				return res;
 			} else if (op_kind == Token_NotEq) {
-				res.value = LLVMBuildIsNotNull(p->builder, the_value, "");
+				res.value = LLVMBuildIsNotNull(p->builder, data.value, "");
 				return res;
 			}
-		} else if (bt->Struct.soa_kind == StructSoa_Dynamic) {
-			LLVMValueRef the_value = {};
-			if (bt->Struct.fields.count == 0) {
-				lbValue cap = lb_soa_struct_cap(p, x);
-				the_value = cap.value;
-			} else {
-				lbValue first_field = lb_emit_struct_ev(p, x, 0);
-				the_value = first_field.value;
-			}
+		}
+		break;
+
+	case Type_Map:
+		{
+			lbValue map_ptr = lb_address_from_load_or_generate_local(p, x);
+
+			unsigned indices[2] = {0, 0};
+			LLVMValueRef hashes_data = LLVMBuildStructGEP(p->builder, map_ptr.value, 0, "");
+			LLVMValueRef hashes_data_ptr_ptr = LLVMBuildStructGEP(p->builder, hashes_data, 0, "");
+			LLVMValueRef hashes_data_ptr = LLVMBuildLoad(p->builder, hashes_data_ptr_ptr, "");
+
 			if (op_kind == Token_CmpEq) {
-				res.value = LLVMBuildIsNull(p->builder, the_value, "");
+				res.value = LLVMBuildIsNull(p->builder, hashes_data_ptr, "");
 				return res;
-			} else if (op_kind == Token_NotEq) {
-				res.value = LLVMBuildIsNotNull(p->builder, the_value, "");
+			} else {
+				res.value = LLVMBuildIsNotNull(p->builder, hashes_data_ptr, "");
 				return res;
 			}
 		}
-	} else if (is_type_struct(t) && type_has_nil(t)) {
-		auto args = array_make<lbValue>(permanent_allocator(), 2);
-		lbValue lhs = lb_address_from_load_or_generate_local(p, x);
-		args[0] = lb_emit_conv(p, lhs, t_rawptr);
-		args[1] = lb_const_int(p->module, t_int, type_size_of(t));
-		lbValue val = lb_emit_runtime_call(p, "memory_compare_zero", args);
-		lbValue res = lb_emit_comp(p, op_kind, val, lb_const_int(p->module, t_int, 0));
-		return res;
+		break;
+
+	case Type_Union:
+		{
+			if (type_size_of(t) == 0) {
+				if (op_kind == Token_CmpEq) {
+					return lb_const_bool(p->module, t_llvm_bool, true);
+				} else if (op_kind == Token_NotEq) {
+					return lb_const_bool(p->module, t_llvm_bool, false);
+				}
+			} else if (is_type_union_maybe_pointer(t)) {
+				lbValue tag = lb_emit_transmute(p, x, t_rawptr);
+				return lb_emit_comp_against_nil(p, op_kind, tag);
+			} else {
+				lbValue tag = lb_emit_union_tag_value(p, x);
+				return lb_emit_comp(p, op_kind, tag, lb_zero(p->module, tag.type));
+			}
+		}
+	case Type_Struct:
+		if (is_type_soa_struct(t)) {
+			Type *bt = base_type(t);
+			if (bt->Struct.soa_kind == StructSoa_Slice) {
+				LLVMValueRef the_value = {};
+				if (bt->Struct.fields.count == 0) {
+					lbValue len = lb_soa_struct_len(p, x);
+					the_value = len.value;
+				} else {
+					lbValue first_field = lb_emit_struct_ev(p, x, 0);
+					the_value = first_field.value;
+				}
+				if (op_kind == Token_CmpEq) {
+					res.value = LLVMBuildIsNull(p->builder, the_value, "");
+					return res;
+				} else if (op_kind == Token_NotEq) {
+					res.value = LLVMBuildIsNotNull(p->builder, the_value, "");
+					return res;
+				}
+			} else if (bt->Struct.soa_kind == StructSoa_Dynamic) {
+				LLVMValueRef the_value = {};
+				if (bt->Struct.fields.count == 0) {
+					lbValue cap = lb_soa_struct_cap(p, x);
+					the_value = cap.value;
+				} else {
+					lbValue first_field = lb_emit_struct_ev(p, x, 0);
+					the_value = first_field.value;
+				}
+				if (op_kind == Token_CmpEq) {
+					res.value = LLVMBuildIsNull(p->builder, the_value, "");
+					return res;
+				} else if (op_kind == Token_NotEq) {
+					res.value = LLVMBuildIsNotNull(p->builder, the_value, "");
+					return res;
+				}
+			}
+		} else if (is_type_struct(t) && type_has_nil(t)) {
+			auto args = array_make<lbValue>(permanent_allocator(), 2);
+			lbValue lhs = lb_address_from_load_or_generate_local(p, x);
+			args[0] = lb_emit_conv(p, lhs, t_rawptr);
+			args[1] = lb_const_int(p->module, t_int, type_size_of(t));
+			lbValue val = lb_emit_runtime_call(p, "memory_compare_zero", args);
+			lbValue res = lb_emit_comp(p, op_kind, val, lb_const_int(p->module, t_int, 0));
+			return res;
+		}
+		break;
 	}
+	GB_PANIC("Unknown handled type: %s -> %s", type_to_string(t), type_to_string(bt));
 	return {};
 }
 

+ 7 - 0
src/parser.cpp

@@ -5370,6 +5370,9 @@ bool parse_file(Parser *p, AstFile *f) {
 				}
 
 				f->total_file_decl_count += calc_decl_count(stmt);
+				if (stmt->kind == Ast_WhenStmt || stmt->kind == Ast_ExprStmt || stmt->kind == Ast_ImportDecl) {
+					f->delayed_decl_count += 1;
+				}
 			}
 		}
 
@@ -5381,6 +5384,10 @@ bool parse_file(Parser *p, AstFile *f) {
 	u64 end = time_stamp_time_now();
 	f->time_to_parse = cast(f64)(end-start)/cast(f64)time_stamp__freq();
 
+	for (int i = 0; i < AstDelayQueue_COUNT; i++) {
+		mpmc_init(f->delayed_decls_queues+i, heap_allocator(), f->delayed_decl_count);
+	}
+
 
 	return f->error_count == 0;
 }

+ 16 - 1
src/parser.hpp

@@ -83,6 +83,11 @@ enum AstFileFlag : u32 {
 	AstFile_IsLazy    = 1<<2,
 };
 
+enum AstDelayQueueKind {
+	AstDelayQueue_Import,
+	AstDelayQueue_Expr,
+	AstDelayQueue_COUNT,
+};
 
 struct AstFile {
 	i32          id;
@@ -113,6 +118,7 @@ struct AstFile {
 	bool         allow_type;
 
 	isize total_file_decl_count;
+	isize delayed_decl_count;
 	Slice<Ast *> decls;
 	Array<Ast *> imports; // 'import'
 	isize        directive_count;
@@ -128,6 +134,9 @@ struct AstFile {
 	CommentGroup *docs;             // current docs
 	Array<CommentGroup *> comments; // All the comments!
 
+	// TODO(bill): make this a basic queue as it does not require
+	// any multiple thread capabilities
+	MPMCQueue<Ast *> delayed_decls_queues[AstDelayQueue_COUNT];
 
 #define PARSER_MAX_FIX_COUNT 6
 	isize    fix_count;
@@ -151,6 +160,11 @@ struct AstForeignFile {
 };
 
 
+struct AstPackageExportedEntity {
+	Ast *identifier;
+	Entity *entity;
+};
+
 struct AstPackage {
 	PackageKind           kind;
 	isize                 id;
@@ -160,10 +174,11 @@ struct AstPackage {
 	Array<AstForeignFile> foreign_files;
 	bool                  is_single_file;
 
+	MPMCQueue<AstPackageExportedEntity> exported_entity_queue;
+
 	// NOTE(bill): Created/set in checker
 	Scope *   scope;
 	DeclInfo *decl_info;
-	bool      used;
 	bool      is_extra;
 };
 

+ 8 - 1
src/queue.cpp

@@ -64,6 +64,10 @@ void mpmc_destroy(MPMCQueue<T> *q) {
 
 template <typename T>
 isize mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
+	if (q->mask == 0) {
+		return -1;
+	}
+
 	isize head_idx = q->head_idx.load(std::memory_order_relaxed);
 
 	for (;;) {
@@ -101,9 +105,12 @@ isize mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
 	}
 }
 
-
 template <typename T>
 bool mpmc_dequeue(MPMCQueue<T> *q, T *data_) {
+	if (q->mask == 0) {
+		return false;
+	}
+
 	isize tail_idx = q->tail_idx.load(std::memory_order_relaxed);
 
 	for (;;) {