Browse Source

INTERNAL USE ONLY: `//+lazy` build flag

gingerBill 4 years ago
parent
commit
99080d41f3
8 changed files with 188 additions and 71 deletions
  1. 2 0
      src/build_settings.cpp
  2. 48 53
      src/check_decl.cpp
  3. 97 5
      src/checker.cpp
  4. 2 0
      src/checker.hpp
  5. 4 2
      src/entity.cpp
  6. 14 6
      src/main.cpp
  7. 13 2
      src/parser.cpp
  8. 8 3
      src/parser.hpp

+ 2 - 0
src/build_settings.cpp

@@ -203,6 +203,8 @@ struct BuildContext {
 	bool   warnings_as_errors;
 	bool   warnings_as_errors;
 	bool   show_error_line;
 	bool   show_error_line;
 
 
+	bool   ignore_lazy;
+
 	bool   use_subsystem_windows;
 	bool   use_subsystem_windows;
 	bool   ignore_microsoft_magic;
 	bool   ignore_microsoft_magic;
 	bool   linker_map_file;
 	bool   linker_map_file;

+ 48 - 53
src/check_decl.cpp

@@ -1115,71 +1115,66 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_
 	if (e->state == EntityState_Resolved)  {
 	if (e->state == EntityState_Resolved)  {
 		return;
 		return;
 	}
 	}
+	if (e->flags & EntityFlag_Lazy) {
+		gb_mutex_lock(&ctx->info->lazy_mutex);
+	}
+
 	String name = e->token.string;
 	String name = e->token.string;
 
 
 	if (e->type != nullptr || e->state != EntityState_Unresolved) {
 	if (e->type != nullptr || e->state != EntityState_Unresolved) {
 		error(e->token, "Illegal declaration cycle of `%.*s`", LIT(name));
 		error(e->token, "Illegal declaration cycle of `%.*s`", LIT(name));
-		return;
-	}
-
-	GB_ASSERT(e->state == EntityState_Unresolved);
-
-#if 0
-	char buf[256] = {};
-	isize n = gb_snprintf(buf, 256, "%.*s %d", LIT(name), e->kind);
-	Timings timings = {};
-	timings_init(&timings, make_string(cast(u8 *)buf, n-1), 16);
-	defer ({
-		timings_print_all(&timings);
-		timings_destroy(&timings);
-	});
-#define TIME_SECTION(str) timings_start_section(&timings, str_lit(str))
-#else
-#define TIME_SECTION(str)
-#endif
-
-	if (d == nullptr) {
-		d = decl_info_of_entity(e);
+	} else {
+		GB_ASSERT(e->state == EntityState_Unresolved);
 		if (d == nullptr) {
 		if (d == nullptr) {
-			// TODO(bill): Err here?
-			e->type = t_invalid;
-			e->state = EntityState_Resolved;
-			set_base_type(named_type, t_invalid);
-			return;
-			// GB_PANIC("'%.*s' should been declared!", LIT(name));
+			d = decl_info_of_entity(e);
+			if (d == nullptr) {
+				// TODO(bill): Err here?
+				e->type = t_invalid;
+				e->state = EntityState_Resolved;
+				set_base_type(named_type, t_invalid);
+				goto end;
+			}
 		}
 		}
-	}
 
 
-	CheckerContext c = *ctx;
-	c.scope = d->scope;
-	c.decl  = d;
-	c.type_level = 0;
+		CheckerContext c = *ctx;
+		c.scope = d->scope;
+		c.decl  = d;
+		c.type_level = 0;
 
 
-	e->parent_proc_decl = c.curr_proc_decl;
-	e->state = EntityState_InProgress;
+		e->parent_proc_decl = c.curr_proc_decl;
+		e->state = EntityState_InProgress;
+
+		switch (e->kind) {
+		case Entity_Variable:
+			check_global_variable_decl(&c, e, d->type_expr, d->init_expr);
+			break;
+		case Entity_Constant:
+			check_const_decl(&c, e, d->type_expr, d->init_expr, named_type);
+			break;
+		case Entity_TypeName: {
+			check_type_decl(&c, e, d->init_expr, named_type);
+			break;
+		}
+		case Entity_Procedure:
+			check_proc_decl(&c, e, d);
+			break;
+		case Entity_ProcGroup:
+			check_proc_group_decl(&c, e, d);
+			break;
+		}
+
+		e->state = EntityState_Resolved;
 
 
-	switch (e->kind) {
-	case Entity_Variable:
-		check_global_variable_decl(&c, e, d->type_expr, d->init_expr);
-		break;
-	case Entity_Constant:
-		check_const_decl(&c, e, d->type_expr, d->init_expr, named_type);
-		break;
-	case Entity_TypeName: {
-		check_type_decl(&c, e, d->init_expr, named_type);
-		break;
 	}
 	}
-	case Entity_Procedure:
-		check_proc_decl(&c, e, d);
-		break;
-	case Entity_ProcGroup:
-		check_proc_group_decl(&c, e, d);
-		break;
+end:;
+	// NOTE(bill): Add it to the list of checked entities
+	if (e->flags & EntityFlag_Lazy) {
+		array_add(&ctx->info->entities, e);
 	}
 	}
 
 
-	e->state = EntityState_Resolved;
-
-#undef TIME_SECTION
+	if (e->flags & EntityFlag_Lazy) {
+		gb_mutex_unlock(&ctx->info->lazy_mutex);
+	}
 }
 }
 
 
 
 

+ 97 - 5
src/checker.cpp

@@ -87,8 +87,8 @@ void entity_graph_node_destroy(EntityGraphNode *n, gbAllocator a) {
 int entity_graph_node_cmp(EntityGraphNode **data, isize i, isize j) {
 int entity_graph_node_cmp(EntityGraphNode **data, isize i, isize j) {
 	EntityGraphNode *x = data[i];
 	EntityGraphNode *x = data[i];
 	EntityGraphNode *y = data[j];
 	EntityGraphNode *y = data[j];
-	isize a = x->entity->order_in_src;
-	isize b = y->entity->order_in_src;
+	u64 a = x->entity->order_in_src;
+	u64 b = y->entity->order_in_src;
 	if (x->dep_count < y->dep_count) {
 	if (x->dep_count < y->dep_count) {
 		return -1;
 		return -1;
 	}
 	}
@@ -867,6 +867,7 @@ void init_checker_info(CheckerInfo *i) {
 
 
 	gb_mutex_init(&i->gen_procs_mutex);
 	gb_mutex_init(&i->gen_procs_mutex);
 	gb_mutex_init(&i->gen_types_mutex);
 	gb_mutex_init(&i->gen_types_mutex);
+	gb_mutex_init(&i->lazy_mutex);
 
 
 	mutex_init(&i->type_info_mutex);
 	mutex_init(&i->type_info_mutex);
 	mutex_init(&i->deps_mutex);
 	mutex_init(&i->deps_mutex);
@@ -898,6 +899,7 @@ void destroy_checker_info(CheckerInfo *i) {
 
 
 	gb_mutex_destroy(&i->gen_procs_mutex);
 	gb_mutex_destroy(&i->gen_procs_mutex);
 	gb_mutex_destroy(&i->gen_types_mutex);
 	gb_mutex_destroy(&i->gen_types_mutex);
+	gb_mutex_destroy(&i->lazy_mutex);
 	mutex_destroy(&i->type_info_mutex);
 	mutex_destroy(&i->type_info_mutex);
 	mutex_destroy(&i->deps_mutex);
 	mutex_destroy(&i->deps_mutex);
 	mutex_destroy(&i->identifier_uses_mutex);
 	mutex_destroy(&i->identifier_uses_mutex);
@@ -1219,10 +1221,25 @@ bool redeclaration_error(String name, Entity *prev, Entity *found) {
 	return false;
 	return false;
 }
 }
 
 
+void add_entity_flags_from_file(CheckerContext *c, Entity *e, Scope *scope) {
+	if (c->file != nullptr && (c->file->flags & AstFile_IsLazy) != 0 && scope->flags & ScopeFlag_File) {
+		AstPackage *pkg = c->file->pkg;
+		if (pkg->kind == Package_Init && e->kind == Entity_Procedure && e->token.string == "main") {
+			// Do nothing
+		} else if (e->flags & EntityFlag_Test) {
+			// Do nothing
+		} else {
+			e->flags |= EntityFlag_Lazy;
+		}
+	}
+}
+
 bool add_entity_with_name(CheckerContext *c, Scope *scope, Ast *identifier, Entity *entity, String name) {
 bool add_entity_with_name(CheckerContext *c, Scope *scope, Ast *identifier, Entity *entity, String name) {
 	if (scope == nullptr) {
 	if (scope == nullptr) {
 		return false;
 		return false;
 	}
 	}
+
+
 	if (!is_blank_ident(name)) {
 	if (!is_blank_ident(name)) {
 		Entity *ie = scope_insert(scope, entity);
 		Entity *ie = scope_insert(scope, entity);
 		if (ie != nullptr) {
 		if (ie != nullptr) {
@@ -1274,11 +1291,62 @@ void add_entity_use(CheckerContext *c, Ast *identifier, Entity *entity) {
 }
 }
 
 
 
 
+bool could_entity_be_lazy(Entity *e, DeclInfo *d) {
+	if ((e->flags & EntityFlag_Lazy) == 0) {
+		return false;
+	}
+
+	if (e->flags & EntityFlag_Test) {
+		return false;
+	} else if (e->kind == Entity_Variable && e->Variable.is_export) {
+		return false;
+	} else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
+		return false;
+	}
+
+	for_array(i, d->attributes) {
+		Ast *attr = d->attributes[i];
+		if (attr->kind != Ast_Attribute) continue;
+		for_array(j, attr->Attribute.elems) {
+			Ast *elem = attr->Attribute.elems[j];
+			String name = {};
+
+			switch (elem->kind) {
+			case_ast_node(i, Ident, elem);
+				name = i->token.string;
+			case_end;
+			case_ast_node(i, Implicit, elem);
+				name = i->string;
+			case_end;
+			case_ast_node(fv, FieldValue, elem);
+				if (fv->field->kind == Ast_Ident) {
+					name = fv->field->Ident.token.string;
+				}
+			case_end;
+			}
+
+			if (name.len != 0) {
+				if (name == "test") {
+					return false;
+				} else if (name == "export") {
+					return false;
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
 void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d, bool is_exported) {
 void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d, bool is_exported) {
 	GB_ASSERT(identifier->kind == Ast_Ident);
 	GB_ASSERT(identifier->kind == Ast_Ident);
 	GB_ASSERT(e != nullptr && d != nullptr);
 	GB_ASSERT(e != nullptr && d != nullptr);
 	GB_ASSERT(identifier->Ident.token.string == e->token.string);
 	GB_ASSERT(identifier->Ident.token.string == e->token.string);
 
 
+	if (!could_entity_be_lazy(e, d)) {
+		e->flags &= ~EntityFlag_Lazy;
+	}
+
 	if (e->scope != nullptr) {
 	if (e->scope != nullptr) {
 		Scope *scope = e->scope;
 		Scope *scope = e->scope;
 
 
@@ -1300,8 +1368,20 @@ void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, Dec
 	d->entity = e;
 	d->entity = e;
 	e->pkg = c->pkg;
 	e->pkg = c->pkg;
 
 
-	// Is this even correct?
-	e->order_in_src = 1+mpmc_enqueue(&info->entity_queue, e);
+	isize queue_count = -1;
+	bool is_lazy = false;
+
+	// is_lazy = (e->flags & EntityFlag_Lazy) == EntityFlag_Lazy;
+	// if (!is_lazy) {
+		queue_count = mpmc_enqueue(&info->entity_queue, e);
+	// }
+
+	if (e->token.pos.file_id != 0) {
+		e->order_in_src = cast(u64)(e->token.pos.file_id)<<32 | u32(e->token.pos.offset);
+	} else {
+		GB_ASSERT(!is_lazy);
+		e->order_in_src = cast(u64)(1+queue_count);
+	}
 }
 }
 
 
 
 
@@ -2998,6 +3078,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 	ast_node(vd, ValueDecl, decl);
 	ast_node(vd, ValueDecl, decl);
 
 
 	EntityVisiblityKind entity_visibility_kind = c->foreign_context.visibility_kind;
 	EntityVisiblityKind entity_visibility_kind = c->foreign_context.visibility_kind;
+	bool is_test = false;
 
 
 	for_array(i, vd->attributes) {
 	for_array(i, vd->attributes) {
 		Ast *attr = vd->attributes[i];
 		Ast *attr = vd->attributes[i];
@@ -3050,6 +3131,8 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 				}
 				}
 				slice_unordered_remove(elems, j);
 				slice_unordered_remove(elems, j);
 				j -= 1;
 				j -= 1;
+			} else if (name == "test") {
+				is_test = true;
 			}
 			}
 		}
 		}
 	}
 	}
@@ -3171,6 +3254,10 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 				}
 				}
 				d->proc_lit = init;
 				d->proc_lit = init;
 				d->init_expr = init;
 				d->init_expr = init;
+
+				if (is_test) {
+					e->flags |= EntityFlag_Test;
+				}
 			} else if (init->kind == Ast_ProcGroup) {
 			} else if (init->kind == Ast_ProcGroup) {
 				ast_node(pg, ProcGroup, init);
 				ast_node(pg, ProcGroup, init);
 				e = alloc_entity_proc_group(d->scope, token, nullptr);
 				e = alloc_entity_proc_group(d->scope, token, nullptr);
@@ -3185,6 +3272,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 			if (entity_visibility_kind != EntityVisiblity_Public) {
 			if (entity_visibility_kind != EntityVisiblity_Public) {
 				e->flags |= EntityFlag_NotExported;
 				e->flags |= EntityFlag_NotExported;
 			}
 			}
+			add_entity_flags_from_file(c, e, c->scope);
 
 
 			if (vd->is_using) {
 			if (vd->is_using) {
 				if (e->kind == Entity_TypeName && init->kind == Ast_EnumType) {
 				if (e->kind == Entity_TypeName && init->kind == Ast_EnumType) {
@@ -3369,6 +3457,9 @@ void check_all_global_entities(Checker *c) {
 	// Don't bother trying
 	// Don't bother trying
 	for_array(i, c->info.entities) {
 	for_array(i, c->info.entities) {
 		Entity *e = c->info.entities[i];
 		Entity *e = c->info.entities[i];
+		if (e->flags & EntityFlag_Lazy) {
+			continue;
+		}
 		DeclInfo *d = e->decl_info;
 		DeclInfo *d = e->decl_info;
 		check_single_global_entity(c, e, d);
 		check_single_global_entity(c, e, d);
 		if (e->type != nullptr && is_type_typed(e->type)) {
 		if (e->type != nullptr && is_type_typed(e->type)) {
@@ -3784,6 +3875,7 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
 
 
 	Entity *e = alloc_entity_library_name(parent_scope, fl->library_name, t_invalid,
 	Entity *e = alloc_entity_library_name(parent_scope, fl->library_name, t_invalid,
 	                                      fl->fullpaths, library_name);
 	                                      fl->fullpaths, library_name);
+	add_entity_flags_from_file(ctx, e, parent_scope);
 	add_entity(ctx, parent_scope, nullptr, e);
 	add_entity(ctx, parent_scope, nullptr, e);
 
 
 
 
@@ -4366,7 +4458,7 @@ void calculate_global_init_order(Checker *c) {
 		for_array(i, info->variable_init_order) {
 		for_array(i, info->variable_init_order) {
 			DeclInfo *d = info->variable_init_order[i];
 			DeclInfo *d = info->variable_init_order[i];
 			Entity *e = d->entity;
 			Entity *e = d->entity;
-			gb_printf("\t'%.*s' %td\n", LIT(e->token.string), e->order_in_src);
+			gb_printf("\t'%.*s' %llu\n", LIT(e->token.string), cast(unsigned long long)e->order_in_src);
 		}
 		}
 		gb_printf("\n");
 		gb_printf("\n");
 	}
 	}

+ 2 - 0
src/checker.hpp

@@ -300,6 +300,8 @@ struct CheckerInfo {
 	// too much of a problem in practice
 	// too much of a problem in practice
 	BlockingMutex deps_mutex;
 	BlockingMutex deps_mutex;
 
 
+	gbMutex lazy_mutex; // Mutex required for lazy type checking of specific files
+
 	gbMutex gen_procs_mutex;         // Possibly recursive
 	gbMutex gen_procs_mutex;         // Possibly recursive
 	gbMutex gen_types_mutex;         // Possibly recursive
 	gbMutex gen_types_mutex;         // Possibly recursive
 	Map<Array<Entity *> > gen_procs; // Key: Ast * | Identifier -> Entity
 	Map<Array<Entity *> > gen_procs; // Key: Ast * | Identifier -> Entity

+ 4 - 2
src/entity.cpp

@@ -66,6 +66,8 @@ enum EntityFlag : u64 {
 	EntityFlag_Disabled      = 1ull<<24,
 	EntityFlag_Disabled      = 1ull<<24,
 	EntityFlag_Cold          = 1ull<<25, // procedure is rarely called
 	EntityFlag_Cold          = 1ull<<25, // procedure is rarely called
 
 
+	EntityFlag_Lazy          = 1ull<<26, // Lazily type checked
+
 	EntityFlag_Test          = 1ull<<30,
 	EntityFlag_Test          = 1ull<<30,
 
 
 	EntityFlag_Overridden    = 1ull<<63,
 	EntityFlag_Overridden    = 1ull<<63,
@@ -132,7 +134,7 @@ struct Entity {
 	lbModule *   code_gen_module;
 	lbModule *   code_gen_module;
 	lbProcedure *code_gen_procedure;
 	lbProcedure *code_gen_procedure;
 
 
-	isize       order_in_src;
+	u64         order_in_src;
 	String      deprecated_message;
 	String      deprecated_message;
 
 
 	// IMPORTANT NOTE(bill): This must be a discriminated union because of patching
 	// IMPORTANT NOTE(bill): This must be a discriminated union because of patching
@@ -225,7 +227,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) {
 	if (e->flags & EntityFlag_NotExported) {
 	if (e->flags & EntityFlag_NotExported) {
 		return false;
 		return false;
 	}
 	}
-	if (e->file != nullptr && e->file->is_private) {
+	if (e->file != nullptr && (e->file->flags & AstFile_IsPrivate) != 0) {
 		return false;
 		return false;
 	}
 	}
 
 

+ 14 - 6
src/main.cpp

@@ -464,12 +464,12 @@ i32 linker_stage(lbGenerator *gen) {
 				" -e _main "
 				" -e _main "
 			#endif
 			#endif
 			, linker, object_files, LIT(output_base), LIT(output_ext),
 			, linker, object_files, LIT(output_base), LIT(output_ext),
-      			#if defined(GB_SYSTEM_OSX)
-        			"-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib",
-      			#else
-        			"-lc -lm",
-      			#endif
-      			lib_str,
+			#if defined(GB_SYSTEM_OSX)
+			  "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib",
+			#else
+			  "-lc -lm",
+			#endif
+			lib_str,
 			LIT(build_context.link_flags),
 			LIT(build_context.link_flags),
 			LIT(build_context.extra_linker_flags),
 			LIT(build_context.extra_linker_flags),
 			link_settings);
 			link_settings);
@@ -637,6 +637,7 @@ enum BuildFlagKind {
 	BuildFlag_IgnoreWarnings,
 	BuildFlag_IgnoreWarnings,
 	BuildFlag_WarningsAsErrors,
 	BuildFlag_WarningsAsErrors,
 	BuildFlag_VerboseErrors,
 	BuildFlag_VerboseErrors,
+	BuildFlag_IgnoreLazy, // internal use only
 
 
 #if defined(GB_SYSTEM_WINDOWS)
 #if defined(GB_SYSTEM_WINDOWS)
 	BuildFlag_IgnoreVsSearch,
 	BuildFlag_IgnoreVsSearch,
@@ -645,6 +646,7 @@ enum BuildFlagKind {
 	BuildFlag_Subsystem,
 	BuildFlag_Subsystem,
 #endif
 #endif
 
 
+
 	BuildFlag_COUNT,
 	BuildFlag_COUNT,
 };
 };
 
 
@@ -761,6 +763,7 @@ bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_IgnoreWarnings,   str_lit("ignore-warnings"),    BuildFlagParam_None, Command_all);
 	add_flag(&build_flags, BuildFlag_IgnoreWarnings,   str_lit("ignore-warnings"),    BuildFlagParam_None, Command_all);
 	add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
 	add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
 	add_flag(&build_flags, BuildFlag_VerboseErrors,    str_lit("verbose-errors"),     BuildFlagParam_None, Command_all);
 	add_flag(&build_flags, BuildFlag_VerboseErrors,    str_lit("verbose-errors"),     BuildFlagParam_None, Command_all);
+	add_flag(&build_flags, BuildFlag_IgnoreLazy,       str_lit("ignore_lazy"),        BuildFlagParam_None, Command_all);
 
 
 #if defined(GB_SYSTEM_WINDOWS)
 #if defined(GB_SYSTEM_WINDOWS)
 	add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"),  BuildFlagParam_None, Command__does_build);
 	add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"),  BuildFlagParam_None, Command__does_build);
@@ -769,6 +772,7 @@ bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_Subsystem,      str_lit("subsystem"),         BuildFlagParam_String, Command__does_build);
 	add_flag(&build_flags, BuildFlag_Subsystem,      str_lit("subsystem"),         BuildFlagParam_String, Command__does_build);
 #endif
 #endif
 
 
+
 	GB_ASSERT(args.count >= 3);
 	GB_ASSERT(args.count >= 3);
 	Array<String> flag_args = array_slice(args, 3, args.count);
 	Array<String> flag_args = array_slice(args, 3, args.count);
 
 
@@ -1364,6 +1368,10 @@ bool parse_build_flags(Array<String> args) {
 							build_context.show_error_line = true;
 							build_context.show_error_line = true;
 							break;
 							break;
 
 
+						case BuildFlag_IgnoreLazy:
+							build_context.ignore_lazy = true;
+							break;
+
 					#if defined(GB_SYSTEM_WINDOWS)
 					#if defined(GB_SYSTEM_WINDOWS)
 						case BuildFlag_IgnoreVsSearch:
 						case BuildFlag_IgnoreVsSearch:
 							GB_ASSERT(value.kind == ExactValue_Invalid);
 							GB_ASSERT(value.kind == ExactValue_Invalid);

+ 13 - 2
src/parser.cpp

@@ -5312,7 +5312,18 @@ bool parse_file(Parser *p, AstFile *f) {
 							return false;
 							return false;
 						}
 						}
 					} else if (lc == "+private") {
 					} else if (lc == "+private") {
-						f->is_private = true;
+						f->flags |= AstFile_IsPrivate;
+					} else if (lc == "+lazy") {
+						if (build_context.ignore_lazy) {
+							// Ignore
+						} else if (f->flags & AstFile_IsTest) {
+							// Ignore
+						} else if (build_context.command_kind == Command_doc &&
+						           f->pkg->kind == Package_Init) {
+							// Ignore
+						} else {
+							f->flags |= AstFile_IsLazy;
+						}
 					}
 					}
 				}
 				}
 			}
 			}
@@ -5405,7 +5416,7 @@ ParseFileError process_imported_file(Parser *p, ImportedFile const &imported_fil
 
 
 		String test_suffix = str_lit("_test");
 		String test_suffix = str_lit("_test");
 		if (string_ends_with(name, test_suffix) && name != test_suffix) {
 		if (string_ends_with(name, test_suffix) && name != test_suffix) {
-			file->is_test = true;
+			file->flags |= AstFile_IsTest;
 		}
 		}
 	}
 	}
 
 

+ 8 - 3
src/parser.hpp

@@ -77,8 +77,16 @@ struct ImportedFile {
 	isize       index;
 	isize       index;
 };
 };
 
 
+enum AstFileFlag : u32 {
+	AstFile_IsPrivate = 1<<0,
+	AstFile_IsTest    = 1<<1,
+	AstFile_IsLazy    = 1<<2,
+};
+
+
 struct AstFile {
 struct AstFile {
 	i32          id;
 	i32          id;
+	u32          flags;
 	AstPackage * pkg;
 	AstPackage * pkg;
 	Scope *      scope;
 	Scope *      scope;
 
 
@@ -114,9 +122,6 @@ struct AstFile {
 	f64            time_to_tokenize; // seconds
 	f64            time_to_tokenize; // seconds
 	f64            time_to_parse;    // seconds
 	f64            time_to_parse;    // seconds
 
 
-	bool is_private;
-	bool is_test;
-
 	CommentGroup *lead_comment;     // Comment (block) before the decl
 	CommentGroup *lead_comment;     // Comment (block) before the decl
 	CommentGroup *line_comment;     // Comment after the semicolon
 	CommentGroup *line_comment;     // Comment after the semicolon
 	CommentGroup *docs;             // current docs
 	CommentGroup *docs;             // current docs