From 99080d41f3c02dbc179db8ebb0f215d199f49c89 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 25 Jul 2021 13:06:09 +0100 Subject: [PATCH] INTERNAL USE ONLY: `//+lazy` build flag --- src/build_settings.cpp | 2 + src/check_decl.cpp | 107 ++++++++++++++++++++--------------------- src/checker.cpp | 102 +++++++++++++++++++++++++++++++++++++-- src/checker.hpp | 2 + src/entity.cpp | 6 ++- src/main.cpp | 20 +++++--- src/parser.cpp | 15 +++++- src/parser.hpp | 11 +++-- 8 files changed, 191 insertions(+), 74 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 22eecd8b9..9f9844279 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -203,6 +203,8 @@ struct BuildContext { bool warnings_as_errors; bool show_error_line; + bool ignore_lazy; + bool use_subsystem_windows; bool ignore_microsoft_magic; bool linker_map_file; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index fb3ef8038..cc3ec6e5b 100644 --- a/src/check_decl.cpp +++ b/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) { return; } + if (e->flags & EntityFlag_Lazy) { + gb_mutex_lock(&ctx->info->lazy_mutex); + } + String name = e->token.string; if (e->type != nullptr || e->state != EntityState_Unresolved) { 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) { - // 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; + + 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; + + } +end:; + // NOTE(bill): Add it to the list of checked entities + if (e->flags & EntityFlag_Lazy) { + array_add(&ctx->info->entities, e); } - 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; - - 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; + if (e->flags & EntityFlag_Lazy) { + gb_mutex_unlock(&ctx->info->lazy_mutex); } - 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; - -#undef TIME_SECTION } diff --git a/src/checker.cpp b/src/checker.cpp index e251c2b61..0aafc4da4 100644 --- a/src/checker.cpp +++ b/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) { EntityGraphNode *x = data[i]; 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) { return -1; } @@ -867,6 +867,7 @@ void init_checker_info(CheckerInfo *i) { gb_mutex_init(&i->gen_procs_mutex); gb_mutex_init(&i->gen_types_mutex); + gb_mutex_init(&i->lazy_mutex); mutex_init(&i->type_info_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_types_mutex); + gb_mutex_destroy(&i->lazy_mutex); mutex_destroy(&i->type_info_mutex); mutex_destroy(&i->deps_mutex); mutex_destroy(&i->identifier_uses_mutex); @@ -1219,10 +1221,25 @@ bool redeclaration_error(String name, Entity *prev, Entity *found) { 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) { if (scope == nullptr) { return false; } + + if (!is_blank_ident(name)) { Entity *ie = scope_insert(scope, entity); 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) { GB_ASSERT(identifier->kind == Ast_Ident); GB_ASSERT(e != nullptr && d != nullptr); GB_ASSERT(identifier->Ident.token.string == e->token.string); + if (!could_entity_be_lazy(e, d)) { + e->flags &= ~EntityFlag_Lazy; + } + if (e->scope != nullptr) { Scope *scope = e->scope; @@ -1300,8 +1368,20 @@ void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, Dec d->entity = e; 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); EntityVisiblityKind entity_visibility_kind = c->foreign_context.visibility_kind; + bool is_test = false; for_array(i, vd->attributes) { Ast *attr = vd->attributes[i]; @@ -3050,6 +3131,8 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { } slice_unordered_remove(elems, j); 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->init_expr = init; + + if (is_test) { + e->flags |= EntityFlag_Test; + } } else if (init->kind == Ast_ProcGroup) { ast_node(pg, ProcGroup, init); 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) { e->flags |= EntityFlag_NotExported; } + add_entity_flags_from_file(c, e, c->scope); if (vd->is_using) { if (e->kind == Entity_TypeName && init->kind == Ast_EnumType) { @@ -3369,6 +3457,9 @@ void check_all_global_entities(Checker *c) { // Don't bother trying for_array(i, c->info.entities) { Entity *e = c->info.entities[i]; + if (e->flags & EntityFlag_Lazy) { + continue; + } DeclInfo *d = e->decl_info; check_single_global_entity(c, e, d); 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, fl->fullpaths, library_name); + add_entity_flags_from_file(ctx, e, parent_scope); 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) { DeclInfo *d = info->variable_init_order[i]; 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"); } diff --git a/src/checker.hpp b/src/checker.hpp index 6e111c5e4..7bd72c9d9 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -300,6 +300,8 @@ struct CheckerInfo { // too much of a problem in practice BlockingMutex deps_mutex; + gbMutex lazy_mutex; // Mutex required for lazy type checking of specific files + gbMutex gen_procs_mutex; // Possibly recursive gbMutex gen_types_mutex; // Possibly recursive Map > gen_procs; // Key: Ast * | Identifier -> Entity diff --git a/src/entity.cpp b/src/entity.cpp index 30bbef9dc..f875b9607 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -66,6 +66,8 @@ enum EntityFlag : u64 { EntityFlag_Disabled = 1ull<<24, EntityFlag_Cold = 1ull<<25, // procedure is rarely called + EntityFlag_Lazy = 1ull<<26, // Lazily type checked + EntityFlag_Test = 1ull<<30, EntityFlag_Overridden = 1ull<<63, @@ -132,7 +134,7 @@ struct Entity { lbModule * code_gen_module; lbProcedure *code_gen_procedure; - isize order_in_src; + u64 order_in_src; String deprecated_message; // 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) { return false; } - if (e->file != nullptr && e->file->is_private) { + if (e->file != nullptr && (e->file->flags & AstFile_IsPrivate) != 0) { return false; } diff --git a/src/main.cpp b/src/main.cpp index a10e19dea..805f20b38 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -464,12 +464,12 @@ i32 linker_stage(lbGenerator *gen) { " -e _main " #endif , 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.extra_linker_flags), link_settings); @@ -637,6 +637,7 @@ enum BuildFlagKind { BuildFlag_IgnoreWarnings, BuildFlag_WarningsAsErrors, BuildFlag_VerboseErrors, + BuildFlag_IgnoreLazy, // internal use only #if defined(GB_SYSTEM_WINDOWS) BuildFlag_IgnoreVsSearch, @@ -645,6 +646,7 @@ enum BuildFlagKind { BuildFlag_Subsystem, #endif + BuildFlag_COUNT, }; @@ -761,6 +763,7 @@ bool parse_build_flags(Array args) { 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_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) 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 args) { add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build); #endif + GB_ASSERT(args.count >= 3); Array flag_args = array_slice(args, 3, args.count); @@ -1364,6 +1368,10 @@ bool parse_build_flags(Array args) { build_context.show_error_line = true; break; + case BuildFlag_IgnoreLazy: + build_context.ignore_lazy = true; + break; + #if defined(GB_SYSTEM_WINDOWS) case BuildFlag_IgnoreVsSearch: GB_ASSERT(value.kind == ExactValue_Invalid); diff --git a/src/parser.cpp b/src/parser.cpp index e45195578..b22d063d2 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5312,7 +5312,18 @@ bool parse_file(Parser *p, AstFile *f) { return false; } } 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"); if (string_ends_with(name, test_suffix) && name != test_suffix) { - file->is_test = true; + file->flags |= AstFile_IsTest; } } diff --git a/src/parser.hpp b/src/parser.hpp index 390bfbba2..9fc4c95a3 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -77,8 +77,16 @@ struct ImportedFile { isize index; }; +enum AstFileFlag : u32 { + AstFile_IsPrivate = 1<<0, + AstFile_IsTest = 1<<1, + AstFile_IsLazy = 1<<2, +}; + + struct AstFile { i32 id; + u32 flags; AstPackage * pkg; Scope * scope; @@ -114,9 +122,6 @@ struct AstFile { f64 time_to_tokenize; // seconds f64 time_to_parse; // seconds - bool is_private; - bool is_test; - CommentGroup *lead_comment; // Comment (block) before the decl CommentGroup *line_comment; // Comment after the semicolon CommentGroup *docs; // current docs