aboutsummaryrefslogtreecommitdiff
path: root/src/checker.cpp
diff options
context:
space:
mode:
authorCourtney Strachan <courtney.strachan@gmail.com>2025-10-06 02:41:44 +0100
committerGitHub <noreply@github.com>2025-10-06 02:41:44 +0100
commit6de2d6e8ca687c989bbb7806e5cbe8d791e425bf (patch)
tree03a2e0a84c7c1530215f8e3f59a7f643b39b3677 /src/checker.cpp
parentdbbe96ae5c343f0e803de6ee508207a62571534f (diff)
parent0f97382fa3e46da80705c00dfe02f3deb9562e4f (diff)
Merge branch 'odin-lang:master' into master
Diffstat (limited to 'src/checker.cpp')
-rw-r--r--src/checker.cpp1233
1 files changed, 991 insertions, 242 deletions
diff --git a/src/checker.cpp b/src/checker.cpp
index bee3f1efe..0a8590570 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -380,16 +380,25 @@ gb_internal Entity *scope_lookup_current(Scope *s, String const &name) {
}
-gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_) {
+gb_global std::atomic<bool> in_single_threaded_checker_stage;
+
+gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_, u32 hash) {
+ bool is_single_threaded = in_single_threaded_checker_stage.load(std::memory_order_relaxed);
if (scope != nullptr) {
bool gone_thru_proc = false;
bool gone_thru_package = false;
- StringHashKey key = string_hash_string(name);
+ StringHashKey key = {};
+ if (hash) {
+ key.hash = hash;
+ key.string = name;
+ } else {
+ key = string_hash_string(name);
+ }
for (Scope *s = scope; s != nullptr; s = s->parent) {
Entity **found = nullptr;
- rw_mutex_shared_lock(&s->mutex);
+ if (!is_single_threaded) rw_mutex_shared_lock(&s->mutex);
found = string_map_get(&s->elements, key);
- rw_mutex_shared_unlock(&s->mutex);
+ if (!is_single_threaded) rw_mutex_shared_unlock(&s->mutex);
if (found) {
Entity *e = *found;
if (gone_thru_proc) {
@@ -424,9 +433,9 @@ gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **s
if (scope_) *scope_ = nullptr;
}
-gb_internal Entity *scope_lookup(Scope *s, String const &name) {
+gb_internal Entity *scope_lookup(Scope *s, String const &name, u32 hash) {
Entity *entity = nullptr;
- scope_lookup_parent(s, name, nullptr, &entity);
+ scope_lookup_parent(s, name, nullptr, &entity, hash);
return entity;
}
@@ -507,11 +516,9 @@ end:;
return result;
}
-gb_global bool in_single_threaded_checker_stage = false;
-
gb_internal Entity *scope_insert(Scope *s, Entity *entity) {
String name = entity->token.string;
- if (in_single_threaded_checker_stage) {
+ if (in_single_threaded_checker_stage.load(std::memory_order_relaxed)) {
return scope_insert_with_name_no_mutex(s, name, entity);
} else {
return scope_insert_with_name(s, name, entity);
@@ -565,6 +572,26 @@ gb_internal u64 check_feature_flags(CheckerContext *c, Ast *node) {
return 0;
}
+gb_internal u64 check_feature_flags(Entity *e) {
+ if (e == nullptr) {
+ return 0;
+ }
+ AstFile *file = nullptr;
+ if (e->file == nullptr) {
+ file = e->file;
+ }
+ if (file == nullptr) {
+ if (e->decl_info && e->decl_info->decl_node) {
+ file = e->decl_info->decl_node->file();
+ }
+ }
+ if (file != nullptr && file->feature_flags_set) {
+ return file->feature_flags;
+ }
+ return 0;
+}
+
+
enum VettedEntityKind {
VettedEntity_Invalid,
@@ -702,9 +729,10 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_flags, bool per_entity) {
u64 original_vet_flags = vet_flags;
+
+ TEMPORARY_ALLOCATOR_GUARD();
Array<VettedEntity> vetted_entities = {};
- array_init(&vetted_entities, heap_allocator());
- defer (array_free(&vetted_entities));
+ array_init(&vetted_entities, temporary_allocator());
rw_mutex_shared_lock(&scope->mutex);
for (auto const &entry : scope->elements) {
@@ -728,12 +756,17 @@ gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_fl
bool is_unused = false;
if (vet_unused && check_vet_unused(c, e, &ve_unused)) {
is_unused = true;
- } else if (vet_unused_procedures &&
- e->kind == Entity_Procedure) {
+ } else if (vet_unused_procedures && e->kind == Entity_Procedure) {
if (e->flags&EntityFlag_Used) {
is_unused = false;
} else if (e->flags & EntityFlag_Require) {
is_unused = false;
+ } else if (e->flags & EntityFlag_Init) {
+ is_unused = false;
+ } else if (e->flags & EntityFlag_Fini) {
+ is_unused = false;
+ } else if (e->Procedure.is_export) {
+ is_unused = false;
} else if (e->pkg && e->pkg->kind == Package_Init && e->token.string == "main") {
is_unused = false;
} else {
@@ -751,7 +784,7 @@ gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_fl
array_add(&vetted_entities, ve_unused);
} else if (is_shadowed) {
array_add(&vetted_entities, ve_shadowed);
- } else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using|EntityFlag_Static)) == 0 && !e->Variable.is_global) {
+ } else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using|EntityFlag_Static|EntityFlag_Field)) == 0 && !e->Variable.is_global) {
i64 sz = type_size_of(e->type);
// TODO(bill): When is a good size warn?
// Is >256 KiB good enough?
@@ -827,9 +860,13 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
gb_internal void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) {
- rw_mutex_lock(&d->deps_mutex);
- ptr_set_add(&d->deps, e);
- rw_mutex_unlock(&d->deps_mutex);
+ if (in_single_threaded_checker_stage.load(std::memory_order_relaxed)) {
+ ptr_set_add(&d->deps, e);
+ } else {
+ rw_mutex_lock(&d->deps_mutex);
+ ptr_set_add(&d->deps, e);
+ rw_mutex_unlock(&d->deps_mutex);
+ }
}
gb_internal void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type) {
if (d == nullptr || type == nullptr) {
@@ -1073,11 +1110,30 @@ gb_internal void init_universal(void) {
add_global_bool_constant("true", true);
add_global_bool_constant("false", false);
- add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
- add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
- add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT);
- add_global_string_constant("ODIN_BUILD_PROJECT_NAME", bc->ODIN_BUILD_PROJECT_NAME);
- add_global_string_constant("ODIN_WINDOWS_SUBSYSTEM", bc->ODIN_WINDOWS_SUBSYSTEM);
+ add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
+ add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
+ add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT);
+ add_global_string_constant("ODIN_BUILD_PROJECT_NAME", bc->ODIN_BUILD_PROJECT_NAME);
+
+ {
+ GlobalEnumValue values[Windows_Subsystem_COUNT] = {
+ {"Unknown", Windows_Subsystem_UNKNOWN},
+ {"Boot_Application", Windows_Subsystem_BOOT_APPLICATION},
+ {"Console", Windows_Subsystem_CONSOLE},
+ {"EFI_Application", Windows_Subsystem_EFI_APPLICATION},
+ {"EFI_Boot_Service_Driver", Windows_Subsystem_EFI_BOOT_SERVICE_DRIVER},
+ {"EFI_Rom", Windows_Subsystem_EFI_ROM},
+ {"EFI_Runtime_Driver", Windows_Subsystem_EFI_RUNTIME_DRIVER},
+ {"Native", Windows_Subsystem_NATIVE},
+ {"Posix", Windows_Subsystem_POSIX},
+ {"Windows", Windows_Subsystem_WINDOWS},
+ {"Windows_CE", Windows_Subsystem_WINDOWSCE},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Windows_Subsystem_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_WINDOWS_SUBSYSTEM", bc->ODIN_WINDOWS_SUBSYSTEM);
+ add_global_string_constant("ODIN_WINDOWS_SUBSYSTEM_STRING", windows_subsystem_names[bc->ODIN_WINDOWS_SUBSYSTEM]);
+ }
{
GlobalEnumValue values[TargetOs_COUNT] = {
@@ -1097,7 +1153,7 @@ gb_internal void init_universal(void) {
};
auto fields = add_global_enum_type(str_lit("Odin_OS_Type"), values, gb_count_of(values));
- add_global_enum_constant(fields, "ODIN_OS", bc->metrics.os);
+ add_global_enum_constant(fields, "ODIN_OS", bc->metrics.os);
add_global_string_constant("ODIN_OS_STRING", target_os_names[bc->metrics.os]);
}
@@ -1147,8 +1203,10 @@ gb_internal void init_universal(void) {
{
GlobalEnumValue values[Subtarget_COUNT] = {
- {"Default", Subtarget_Default},
- {"iOS", Subtarget_iOS},
+ {"Default", Subtarget_Default},
+ {"iPhone", Subtarget_iPhone},
+ {"iPhoneSimulator", Subtarget_iPhoneSimulator},
+ {"Android", Subtarget_Android},
};
auto fields = add_global_enum_type(str_lit("Odin_Platform_Subtarget_Type"), values, gb_count_of(values));
@@ -1337,23 +1395,29 @@ gb_internal void init_universal(void) {
}
- t_u8_ptr = alloc_type_pointer(t_u8);
- t_u8_multi_ptr = alloc_type_multi_pointer(t_u8);
- t_int_ptr = alloc_type_pointer(t_int);
- t_i64_ptr = alloc_type_pointer(t_i64);
- t_f64_ptr = alloc_type_pointer(t_f64);
- t_u8_slice = alloc_type_slice(t_u8);
- t_string_slice = alloc_type_slice(t_string);
+ t_u8_ptr = alloc_type_pointer(t_u8);
+ t_u8_multi_ptr = alloc_type_multi_pointer(t_u8);
+ t_u16_ptr = alloc_type_pointer(t_u16);
+ t_u16_multi_ptr = alloc_type_multi_pointer(t_u16);
+ t_int_ptr = alloc_type_pointer(t_int);
+ t_i64_ptr = alloc_type_pointer(t_i64);
+ t_f64_ptr = alloc_type_pointer(t_f64);
+ t_u8_slice = alloc_type_slice(t_u8);
+ t_string_slice = alloc_type_slice(t_string);
// intrinsics types for objective-c stuff
{
t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct_complete());
t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct_complete());
t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct_complete());
+ t_objc_ivar = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_ivar"), alloc_type_struct_complete());
t_objc_id = alloc_type_pointer(t_objc_object);
t_objc_SEL = alloc_type_pointer(t_objc_selector);
t_objc_Class = alloc_type_pointer(t_objc_class);
+ t_objc_Ivar = alloc_type_pointer(t_objc_ivar);
+
+ t_objc_instancetype = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_instancetype"), t_objc_id);
}
}
@@ -1369,13 +1433,10 @@ gb_internal void init_checker_info(CheckerInfo *i) {
array_init(&i->entities, a);
map_init(&i->global_untyped);
string_map_init(&i->foreigns);
- // map_init(&i->gen_procs);
- map_init(&i->gen_types);
type_set_init(&i->min_dep_type_info_set);
map_init(&i->min_dep_type_info_index_map);
- // map_init(&i->type_info_map);
string_map_init(&i->files);
string_map_init(&i->packages);
array_init(&i->variable_init_order, a);
@@ -1386,8 +1447,13 @@ gb_internal void init_checker_info(CheckerInfo *i) {
array_init(&i->defineables, a);
map_init(&i->objc_msgSend_types);
+ mpsc_init(&i->objc_class_implementations, a);
+ string_set_init(&i->obcj_class_name_set, 0);
+ map_init(&i->objc_method_implementations);
+
string_map_init(&i->load_file_cache);
- array_init(&i->all_procedures, heap_allocator());
+ array_init(&i->all_procedures, a);
+ mpsc_init(&i->all_procedures_queue, a);
mpsc_init(&i->entity_queue, a); // 1<<20);
mpsc_init(&i->definition_queue, a); //); // 1<<20);
@@ -1397,6 +1463,9 @@ gb_internal void init_checker_info(CheckerInfo *i) {
mpsc_init(&i->foreign_decls_to_check, a); // 1<<10);
mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used
+ mpsc_init(&i->raddbg_type_views_queue, a);
+ array_init(&i->raddbg_type_views, a);
+
string_map_init(&i->load_directory_cache);
map_init(&i->load_directory_map);
}
@@ -1406,8 +1475,6 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
array_free(&i->entities);
map_destroy(&i->global_untyped);
string_map_destroy(&i->foreigns);
- // map_destroy(&i->gen_procs);
- map_destroy(&i->gen_types);
type_set_destroy(&i->min_dep_type_info_set);
map_destroy(&i->min_dep_type_info_index_map);
@@ -1418,6 +1485,10 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
array_free(&i->required_foreign_imports_through_force);
array_free(&i->defineables);
+ array_free(&i->all_procedures);
+
+ mpsc_destroy(&i->all_procedures_queue);
+
mpsc_destroy(&i->entity_queue);
mpsc_destroy(&i->definition_queue);
mpsc_destroy(&i->required_global_variable_queue);
@@ -1425,7 +1496,14 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
mpsc_destroy(&i->foreign_imports_to_check_fullpaths);
mpsc_destroy(&i->foreign_decls_to_check);
+ mpsc_destroy(&i->raddbg_type_views_queue);
+ array_free(&i->raddbg_type_views);
+
map_destroy(&i->objc_msgSend_types);
+ string_set_destroy(&i->obcj_class_name_set);
+ mpsc_destroy(&i->objc_class_implementations);
+ map_destroy(&i->objc_method_implementations);
+
string_map_destroy(&i->load_file_cache);
string_map_destroy(&i->load_directory_cache);
map_destroy(&i->load_directory_map);
@@ -1438,12 +1516,12 @@ gb_internal CheckerContext make_checker_context(Checker *c) {
ctx.scope = builtin_pkg->scope;
ctx.pkg = builtin_pkg;
- ctx.type_path = new_checker_type_path();
+ ctx.type_path = new_checker_type_path(heap_allocator());
ctx.type_level = 0;
return ctx;
}
gb_internal void destroy_checker_context(CheckerContext *ctx) {
- destroy_checker_type_path(ctx->type_path);
+ destroy_checker_type_path(ctx->type_path, heap_allocator());
}
gb_internal bool add_curr_ast_file(CheckerContext *ctx, AstFile *file) {
@@ -1496,6 +1574,8 @@ gb_internal void init_checker(Checker *c) {
TIME_SECTION("init proc queues");
mpsc_init(&c->procs_with_deferred_to_check, a); //, 1<<10);
+ mpsc_init(&c->procs_with_objc_context_provider_to_check, a);
+
// NOTE(bill): 1 Mi elements should be enough on average
array_init(&c->procs_to_check, heap_allocator(), 0, 1<<20);
@@ -1646,7 +1726,7 @@ gb_internal void check_remove_expr_info(CheckerContext *c, Ast *e) {
}
gb_internal isize type_info_index(CheckerInfo *info, TypeInfoPair pair, bool error_on_failure) {
- mutex_lock(&info->minimum_dependency_type_info_mutex);
+ rw_mutex_shared_lock(&info->minimum_dependency_type_info_mutex);
isize entry_index = -1;
u64 hash = pair.hash;
@@ -1654,7 +1734,7 @@ gb_internal isize type_info_index(CheckerInfo *info, TypeInfoPair pair, bool err
if (found_entry_index) {
entry_index = *found_entry_index;
}
- mutex_unlock(&info->minimum_dependency_type_info_mutex);
+ rw_mutex_shared_unlock(&info->minimum_dependency_type_info_mutex);
if (error_on_failure && entry_index < 0) {
compiler_error("Type_Info for '%s' could not be found", type_to_string(pair.type));
@@ -1692,7 +1772,7 @@ gb_internal void add_untyped(CheckerContext *c, Ast *expr, AddressingMode mode,
check_set_expr_info(c, expr, mode, type, value);
}
-gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value) {
+gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value, bool use_mutex) {
if (expr == nullptr) {
return;
}
@@ -1710,7 +1790,7 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo
mutex = &ctx->pkg->type_and_value_mutex;
}
- mutex_lock(mutex);
+ if (use_mutex) mutex_lock(mutex);
Ast *prev_expr = nullptr;
while (prev_expr != expr) {
prev_expr = expr;
@@ -1731,8 +1811,11 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo
}
expr = unparen_expr(expr);
+ if (expr == nullptr) {
+ break;
+ };
}
- mutex_unlock(mutex);
+ if (use_mutex) mutex_unlock(mutex);
}
gb_internal void add_entity_definition(CheckerInfo *i, Ast *identifier, Entity *entity) {
@@ -1974,8 +2057,8 @@ gb_internal void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, En
add_entity_definition(info, identifier, e);
GB_ASSERT(e->decl_info == nullptr);
e->decl_info = d;
- d->entity = e;
e->pkg = c->pkg;
+ d->entity.store(e);
isize queue_count = -1;
bool is_lazy = false;
@@ -2021,7 +2104,7 @@ gb_internal void add_type_info_type(CheckerContext *c, Type *t) {
}
gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
- if (t == nullptr) {
+ if (t == nullptr || c == nullptr) {
return;
}
@@ -2276,11 +2359,9 @@ gb_internal void check_procedure_later(Checker *c, ProcInfo *info) {
}
if (DEBUG_CHECK_ALL_PROCEDURES) {
- MUTEX_GUARD_BLOCK(&c->info.all_procedures_mutex) {
- GB_ASSERT(info != nullptr);
- GB_ASSERT(info->decl != nullptr);
- array_add(&c->info.all_procedures, info);
- }
+ GB_ASSERT(info != nullptr);
+ GB_ASSERT(info->decl != nullptr);
+ mpsc_enqueue(&c->info.all_procedures_queue, info);
}
}
@@ -2308,7 +2389,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
return;
}
- if (type_set_update(&c->info.min_dep_type_info_set, t)) {
+ if (type_set_update_with_mutex(&c->info.min_dep_type_info_set, t, &c->info.min_dep_type_info_set_mutex)) {
return;
}
@@ -2483,15 +2564,15 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
}
}
+gb_internal void add_dependency_to_set_threaded(Checker *c, Entity *entity);
+
+gb_global std::atomic<Checker *> global_checker_ptr;
gb_internal void add_dependency_to_set(Checker *c, Entity *entity) {
if (entity == nullptr) {
return;
}
- CheckerInfo *info = &c->info;
- auto *set = &info->minimum_dependency_set;
-
if (entity->type != nullptr &&
is_type_polymorphic(entity->type)) {
DeclInfo *decl = decl_info_of_entity(entity);
@@ -2500,7 +2581,7 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) {
}
}
- if (ptr_set_update(set, entity)) {
+ if (entity->min_dep_count.fetch_add(1, std::memory_order_relaxed) > 0) {
return;
}
@@ -2511,29 +2592,108 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) {
for (TypeInfoPair const tt : decl->type_info_deps) {
add_min_dep_type_info(c, tt.type);
}
+ FOR_PTR_SET(e, decl->deps) {
+ switch (e->kind) {
+ case Entity_Procedure:
+ if (e->Procedure.is_foreign) {
+ Entity *fl = e->Procedure.foreign_library;
+ if (fl != nullptr) {
+ GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
+ (fl->flags&EntityFlag_Used),
+ "%.*s", LIT(entity->token.string));
+ add_dependency_to_set(c, fl);
+ }
+ }
+ break;
+ case Entity_Variable:
+ if (e->Variable.is_foreign) {
+ Entity *fl = e->Variable.foreign_library;
+ if (fl != nullptr) {
+ GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
+ (fl->flags&EntityFlag_Used),
+ "%.*s", LIT(entity->token.string));
+ add_dependency_to_set(c, fl);
+ }
+ }
+ break;
+ }
+ }
- for (Entity *e : decl->deps) {
+ FOR_PTR_SET(e, decl->deps) {
add_dependency_to_set(c, e);
- if (e->kind == Entity_Procedure && e->Procedure.is_foreign) {
- Entity *fl = e->Procedure.foreign_library;
- if (fl != nullptr) {
- GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
- (fl->flags&EntityFlag_Used),
- "%.*s", LIT(entity->token.string));
- add_dependency_to_set(c, fl);
+ }
+
+}
+gb_internal WORKER_TASK_PROC(add_dependency_to_set_worker) {
+ Checker *c = global_checker_ptr.load(std::memory_order_relaxed);
+ Entity *entity = cast(Entity *)data;
+ if (entity == nullptr) {
+ return 0;
+ }
+
+ if (entity->type != nullptr &&
+ is_type_polymorphic(entity->type)) {
+ DeclInfo *decl = decl_info_of_entity(entity);
+ if (decl != nullptr && decl->gen_proc_type == nullptr) {
+ return 0;
+ }
+ }
+
+ if (entity->min_dep_count.fetch_add(1, std::memory_order_relaxed) > 0) {
+ return 0;
+ }
+
+ DeclInfo *decl = decl_info_of_entity(entity);
+ if (decl == nullptr) {
+ return 0;
+ }
+ for (TypeInfoPair const tt : decl->type_info_deps) {
+ add_min_dep_type_info(c, tt.type);
+ }
+
+ FOR_PTR_SET(e, decl->deps) {
+ switch (e->kind) {
+ case Entity_Procedure:
+ if (e->Procedure.is_foreign) {
+ Entity *fl = e->Procedure.foreign_library;
+ if (fl != nullptr) {
+ GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
+ (fl->flags&EntityFlag_Used),
+ "%.*s", LIT(entity->token.string));
+ add_dependency_to_set_threaded(c, fl);
+ }
}
- } else if (e->kind == Entity_Variable && e->Variable.is_foreign) {
- Entity *fl = e->Variable.foreign_library;
- if (fl != nullptr) {
- GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
- (fl->flags&EntityFlag_Used),
- "%.*s", LIT(entity->token.string));
- add_dependency_to_set(c, fl);
+ break;
+ case Entity_Variable:
+ if (e->Variable.is_foreign) {
+ Entity *fl = e->Variable.foreign_library;
+ if (fl != nullptr) {
+ GB_ASSERT_MSG(fl->kind == Entity_LibraryName &&
+ (fl->flags&EntityFlag_Used),
+ "%.*s", LIT(entity->token.string));
+ add_dependency_to_set_threaded(c, fl);
+ }
}
+ break;
}
}
+
+ FOR_PTR_SET(e, decl->deps) {
+ add_dependency_to_set_threaded(c, e);
+ }
+
+ return 0;
+}
+
+
+gb_internal void add_dependency_to_set_threaded(Checker *c, Entity *entity) {
+ if (entity == nullptr) {
+ return;
+ }
+ thread_pool_add_task(add_dependency_to_set_worker, entity);
}
+
gb_internal void force_add_dependency_entity(Checker *c, Scope *scope, String const &name) {
Entity *e = scope_lookup(scope, name);
if (e == nullptr) {
@@ -2583,27 +2743,35 @@ gb_internal void collect_testing_procedures_of_package(Checker *c, AstPackage *p
}
gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *start) {
+ // auto const &add_to_set = add_dependency_to_set;
+ auto const &add_to_set = add_dependency_to_set_threaded;
+
+ Scope *builtin_scope = builtin_pkg->scope;
for_array(i, c->info.definitions) {
Entity *e = c->info.definitions[i];
- if (e->scope == builtin_pkg->scope) {
+ if (e->scope == builtin_scope) {
if (e->type == nullptr) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
+ }
+ } else if (e->kind == Entity_Procedure) {
+ if (e->Procedure.is_export) {
+ add_to_set(c, e);
+ }
+ } else if (e->kind == Entity_Variable) {
+ if (e->Variable.is_export) {
+ add_to_set(c, e);
}
- } else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
- add_dependency_to_set(c, e);
- } else if (e->kind == Entity_Variable && e->Variable.is_export) {
- add_dependency_to_set(c, e);
}
}
for (Entity *e; mpsc_dequeue(&c->info.required_foreign_imports_through_force_queue, &e); /**/) {
array_add(&c->info.required_foreign_imports_through_force, e);
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
}
for (Entity *e; mpsc_dequeue(&c->info.required_global_variable_queue, &e); /**/) {
e->flags |= EntityFlag_Used;
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
}
for_array(i, c->info.entities) {
@@ -2611,16 +2779,16 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
switch (e->kind) {
case Entity_Variable:
if (e->Variable.is_export) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
} else if (e->flags & EntityFlag_Require) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
}
break;
case Entity_Procedure:
if (e->Procedure.is_export) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
} else if (e->flags & EntityFlag_Require) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
}
if (e->flags & EntityFlag_Init) {
Type *t = base_type(e->type);
@@ -2635,6 +2803,15 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
is_init = false;
}
+ u64 feature_flags = check_feature_flags(e);
+ if ((feature_flags & OptInFeatureFlag_GlobalContext) == 0) {
+ if (t->Proc.calling_convention != ProcCC_Contextless) {
+ ERROR_BLOCK();
+ error(e->token, "@(init) procedures must be declared as \"contextless\"");
+ error_line("\tSuggestion: this can be bypassed, for the time being, with '#+feature global-context'");
+ }
+ }
+
if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
error(e->token, "@(init) procedures must be declared at the file scope");
is_init = false;
@@ -2645,8 +2822,13 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
is_init = false;
}
+ if (is_blank_ident(e->token)) {
+ error(e->token, "An @(init) procedure must not use a blank identifier as its name");
+ }
+
+
if (is_init) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
array_add(&c->info.init_procedures, e);
}
} else if (e->flags & EntityFlag_Fini) {
@@ -2662,13 +2844,26 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
is_fini = false;
}
+ u64 feature_flags = check_feature_flags(e);
+ if ((feature_flags & OptInFeatureFlag_GlobalContext) == 0) {
+ if (t->Proc.calling_convention != ProcCC_Contextless) {
+ ERROR_BLOCK();
+ error(e->token, "@(fini) procedures must be declared as \"contextless\"");
+ error_line("\tSuggestion: this can be bypassed, for the time being, with '#+feature global-context'");
+ }
+ }
+
if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
error(e->token, "@(fini) procedures must be declared at the file scope");
is_fini = false;
}
+ if (is_blank_ident(e->token)) {
+ error(e->token, "An @(fini) procedure must not use a blank identifier as its name");
+ }
+
if (is_fini) {
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
array_add(&c->info.fini_procedures, e);
}
}
@@ -2685,7 +2880,7 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
Entity *e = entry.value;
if (e != nullptr) {
e->flags |= EntityFlag_Used;
- add_dependency_to_set(c, e);
+ add_to_set(c, e);
}
}
@@ -2700,16 +2895,11 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
}
} else if (start != nullptr) {
start->flags |= EntityFlag_Used;
- add_dependency_to_set(c, start);
+ add_to_set(c, start);
}
}
gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
- isize entity_count = c->info.entities.count;
- isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor
-
- ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap);
-
#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \
if (condition) { \
String entities[] = {__VA_ARGS__}; \
@@ -2808,12 +2998,13 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
generate_minimum_dependency_set_internal(c, start);
+ thread_pool_wait();
+
#undef FORCE_ADD_RUNTIME_ENTITIES
}
-gb_internal bool is_entity_a_dependency(Entity *e) {
- if (e == nullptr) return false;
+gb_internal gb_inline bool is_entity_a_dependency(Entity *e) {
switch (e->kind) {
case Entity_Procedure:
return true;
@@ -2826,83 +3017,112 @@ gb_internal bool is_entity_a_dependency(Entity *e) {
return false;
}
-gb_internal Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info, gbAllocator allocator) {
- PtrMap<Entity *, EntityGraphNode *> M = {};
- map_init(&M, info->entities.count);
- defer (map_destroy(&M));
+gb_internal Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info, Arena *arena) {
+ PtrMap<Entity *, EntityGraphNode *> M_procs = {};
+ PtrMap<Entity *, EntityGraphNode *> M_vars = {};
+ PtrMap<Entity *, EntityGraphNode *> M_other = {};
+
+ map_init(&M_procs, info->entities.count);
+ defer (map_destroy(&M_procs));
+
+ map_init(&M_vars, info->entities.count);
+ defer (map_destroy(&M_vars));
+
+ map_init(&M_other, info->entities.count);
+ defer (map_destroy(&M_other));
+
for_array(i, info->entities) {
Entity *e = info->entities[i];
- if (is_entity_a_dependency(e)) {
- EntityGraphNode *n = gb_alloc_item(allocator, EntityGraphNode);
- n->entity = e;
- map_set(&M, e, n);
+ if (e == nullptr || !is_entity_a_dependency(e)) {
+ continue;
+ }
+ EntityGraphNode *n = arena_alloc_item<EntityGraphNode>(arena);
+ n->entity = e;
+ switch (e->kind) {
+ case Entity_Procedure: map_set(&M_procs, e, n); break;
+ case Entity_Variable: map_set(&M_vars, e, n); break;
+ default: map_set(&M_other, e, n); break;
}
}
TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 1");
// Calculate edges for graph M
- for (auto const &entry : M) {
+ for (auto const &entry : M_procs) {
EntityGraphNode *n = entry.value;
Entity *e = n->entity;
DeclInfo *decl = decl_info_of_entity(e);
GB_ASSERT(decl != nullptr);
- for (Entity *dep : decl->deps) {
+ FOR_PTR_SET(dep, decl->deps) {
+ GB_ASSERT(dep != nullptr);
if (dep->flags & EntityFlag_Field) {
continue;
}
- GB_ASSERT(dep != nullptr);
- if (is_entity_a_dependency(dep)) {
- EntityGraphNode *m = map_must_get(&M, dep);
- entity_graph_node_set_add(&n->succ, m);
- entity_graph_node_set_add(&m->pred, n);
+ if (!is_entity_a_dependency(dep)) {
+ continue;
+ }
+ EntityGraphNode *m = nullptr;
+
+ switch (dep->kind) {
+ case Entity_Procedure: m = map_must_get(&M_procs, dep); break;
+ case Entity_Variable: m = map_must_get(&M_vars, dep); break;
+ default: m = map_must_get(&M_other, dep); break;
}
+ entity_graph_node_set_add(&n->succ, m);
+ entity_graph_node_set_add(&m->pred, n);
}
}
- TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2");
- auto G = array_make<EntityGraphNode *>(allocator, 0, M.count);
+ TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2a (init)");
- for (auto const &m_entry : M) {
- auto *e = m_entry.key;
+ auto G = array_make<EntityGraphNode *>(arena_allocator(arena), 0, M_procs.count + M_vars.count + M_other.count);
+
+ TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2b (procs)");
+
+ for (auto const &m_entry : M_procs) {
EntityGraphNode *n = m_entry.value;
- if (e->kind == Entity_Procedure) {
- // Connect each pred 'p' of 'n' with each succ 's' and from
- // the procedure node
- for (EntityGraphNode *p : n->pred) {
+ // Connect each pred 'p' of 'n' with each succ 's' and from
+ // the procedure node
+ FOR_PTR_SET(p, n->pred) {
+ // Ignore self-cycles
+ if (p == n) {
+ continue;
+ }
+ // Each succ 's' of 'n' becomes a succ of 'p', and
+ // each pred 'p' of 'n' becomes a pred of 's'
+ FOR_PTR_SET(s, n->succ) {
// Ignore self-cycles
- if (p != n) {
- // Each succ 's' of 'n' becomes a succ of 'p', and
- // each pred 'p' of 'n' becomes a pred of 's'
- for (EntityGraphNode *s : n->succ) {
- // Ignore self-cycles
- if (s != n) {
- if (p->entity->kind == Entity_Procedure &&
- s->entity->kind == Entity_Procedure) {
- // NOTE(bill, 2020-11-15): Only care about variable initialization ordering
- // TODO(bill): This is probably wrong!!!!
- continue;
- }
- // IMPORTANT NOTE/TODO(bill, 2020-11-15): These three calls take the majority of the
- // the time to process
- entity_graph_node_set_add(&p->succ, s);
- entity_graph_node_set_add(&s->pred, p);
- // Remove edge to 'n'
- entity_graph_node_set_remove(&s->pred, n);
- }
- }
-
- // Remove edge to 'n'
- entity_graph_node_set_remove(&p->succ, n);
+ if (s == n) {
+ continue;
+ }
+ if (p->entity->kind == Entity_Procedure &&
+ s->entity->kind == Entity_Procedure) {
+ // NOTE(bill, 2020-11-15): Only care about variable initialization ordering
+ // TODO(bill): This is probably wrong!!!!
+ continue;
}
+ // IMPORTANT NOTE/TODO(bill, 2020-11-15): These three calls take the majority of the
+ // the time to process
+ entity_graph_node_set_add(&p->succ, s);
+ entity_graph_node_set_add(&s->pred, p);
+ // Remove edge to 'n'
+ entity_graph_node_set_remove(&s->pred, n);
}
- } else if (e->kind == Entity_Variable) {
- array_add(&G, n);
+
+ // Remove edge to 'n'
+ entity_graph_node_set_remove(&p->succ, n);
}
}
+ TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2c (vars)");
+
+ for (auto const &m_entry : M_vars) {
+ EntityGraphNode *n = m_entry.value;
+ array_add(&G, n);
+ }
+
TIME_SECTION("generate_entity_dependency_graph: Dependency Count Checker");
for_array(i, G) {
EntityGraphNode *n = G[i];
@@ -2986,19 +3206,17 @@ gb_internal Type *find_type_in_pkg(CheckerInfo *info, String const &pkg, String
return e->type;
}
-gb_internal CheckerTypePath *new_checker_type_path() {
- gbAllocator a = heap_allocator();
- auto *tp = gb_alloc_item(a, CheckerTypePath);
- array_init(tp, a, 0, 16);
+gb_internal CheckerTypePath *new_checker_type_path(gbAllocator allocator) {
+ auto *tp = gb_alloc_item(allocator, CheckerTypePath);
+ array_init(tp, allocator, 0, 16);
return tp;
}
-gb_internal void destroy_checker_type_path(CheckerTypePath *tp) {
+gb_internal void destroy_checker_type_path(CheckerTypePath *tp, gbAllocator allocator) {
array_free(tp);
- gb_free(heap_allocator(), tp);
+ gb_free(allocator, tp);
}
-
gb_internal void check_type_path_push(CheckerContext *c, Entity *e) {
GB_ASSERT(c->type_path != nullptr);
GB_ASSERT(e != nullptr);
@@ -3057,6 +3275,9 @@ gb_internal void init_core_type_info(Checker *c) {
GB_ASSERT(tis->fields.count == 5);
+ Entity *type_info_string_encoding_kind = find_core_entity(c, str_lit("Type_Info_String_Encoding_Kind"));
+ t_type_info_string_encoding_kind = type_info_string_encoding_kind->type;
+
Entity *type_info_variant = tis->fields[4];
Type *tiv_type = type_info_variant->type;
GB_ASSERT(is_type_union(tiv_type));
@@ -3167,12 +3388,20 @@ gb_internal void init_core_map_type(Checker *c) {
t_raw_map_ptr = alloc_type_pointer(t_raw_map);
}
+gb_internal void init_core_objc_c(Checker *c) {
+ if (build_context.metrics.os == TargetOs_darwin) {
+ t_objc_super = find_core_type(c, str_lit("objc_super"));
+ t_objc_super_ptr = alloc_type_pointer(t_objc_super);
+ }
+}
+
gb_internal void init_preload(Checker *c) {
init_core_type_info(c);
init_mem_allocator(c);
init_core_context(c);
init_core_source_code_location(c);
init_core_map_type(c);
+ init_core_objc_c(c);
}
gb_internal ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) {
@@ -3653,6 +3882,33 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
}
}
return true;
+ } else if (name == "objc_implement") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Bool) {
+ ac->objc_is_implementation = ev.value_bool;
+
+ if (!ac->objc_is_implementation) {
+ ac->objc_is_disabled_implement = true;
+ }
+ } else if (ev.kind == ExactValue_Invalid) {
+ ac->objc_is_implementation = true;
+ } else {
+ error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name));
+ }
+
+ return true;
+ } else if (name == "objc_selector") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_String) {
+ if (string_is_valid_identifier(ev.value_string)) {
+ ac->objc_selector = ev.value_string;
+ } else {
+ error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
+ }
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
} else if (name == "require_target_feature") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
@@ -3702,6 +3958,18 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
}
ac->instrumentation_exit = true;
return true;
+ } else if (name == "no_sanitize_address") {
+ if (value != nullptr) {
+ error(value, "'%.*s' expects no parameter", LIT(name));
+ }
+ ac->no_sanitize_address = true;
+ return true;
+ } else if (name == "no_sanitize_memory") {
+ if (value != nullptr) {
+ error(value, "'%.*s' expects no parameter", LIT(name));
+ }
+ ac->no_sanitize_memory = true;
+ return true;
}
return false;
}
@@ -3892,6 +4160,66 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) {
ac->objc_class = ev.value_string;
}
return true;
+ } else if (name == "objc_implement") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Bool) {
+ ac->objc_is_implementation = ev.value_bool;
+ } else if (ev.kind == ExactValue_Invalid) {
+ ac->objc_is_implementation = true;
+ } else {
+ error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_superclass") {
+ Type *objc_superclass = check_type(c, value);
+
+ if (objc_superclass != nullptr) {
+ ac->objc_superclass = objc_superclass;
+ } else {
+ error(value, "'%.*s' expected a named type", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_ivar") {
+ Type *objc_ivar = check_type(c, value);
+
+ if (objc_ivar != nullptr && objc_ivar->kind == Type_Named) {
+ ac->objc_ivar = objc_ivar;
+ } else {
+ error(value, "'%.*s' expected a named type", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_context_provider") {
+ Operand o = {};
+ check_expr(c, &o, value);
+ Entity *e = entity_of_node(o.expr);
+
+ if (e != nullptr) {
+ if (ac->objc_context_provider != nullptr) {
+ error(elem, "Previous usage of a 'objc_context_provider' attribute");
+ }
+ if (e->kind != Entity_Procedure) {
+ error(elem, "'objc_context_provider' must refer to a procedure");
+ } else {
+ ac->objc_context_provider = e;
+ }
+
+ return true;
+ }
+ } else if (name == "raddbg_type_view") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Invalid) {
+ ac->raddbg_type_view = true;
+ } else if (ev.kind == ExactValue_String) {
+ ac->raddbg_type_view = true;
+ ac->raddbg_type_view_string = ev.value_string;
+
+ if (ev.value_string.len == 0) {
+ error(elem, "Expected a non-empty string for '%.*s'", LIT(name));
+ }
+ } else {
+ error(elem, "Expected a string or no value for '%.*s'", LIT(name));
+ }
+ return true;
}
return false;
}
@@ -4651,7 +4979,7 @@ gb_internal void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d)
}
gb_internal void check_all_global_entities(Checker *c) {
- in_single_threaded_checker_stage = true;
+ in_single_threaded_checker_stage.store(true, std::memory_order_relaxed);
// NOTE(bill): This must be single threaded
// Don't bother trying
@@ -4673,7 +5001,7 @@ gb_internal void check_all_global_entities(Checker *c) {
}
}
- in_single_threaded_checker_stage = false;
+ in_single_threaded_checker_stage.store(false, std::memory_order_relaxed);
}
@@ -4811,14 +5139,14 @@ gb_internal void add_import_dependency_node(Checker *c, Ast *decl, PtrMap<AstPac
}
}
-gb_internal Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c) {
+gb_internal Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c, gbAllocator allocator) {
PtrMap<AstPackage *, ImportGraphNode *> M = {};
map_init(&M, 2*c->parser->packages.count);
defer (map_destroy(&M));
for_array(i, c->parser->packages) {
AstPackage *pkg = c->parser->packages[i];
- ImportGraphNode *n = import_graph_node_create(heap_allocator(), pkg);
+ ImportGraphNode *n = import_graph_node_create(allocator, pkg);
map_set(&M, pkg, n);
}
@@ -4835,7 +5163,7 @@ gb_internal Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c
}
Array<ImportGraphNode *> G = {};
- array_init(&G, heap_allocator(), 0, M.count);
+ array_init(&G, allocator, 0, M.count);
isize i = 0;
for (auto const &entry : M) {
@@ -4854,7 +5182,7 @@ struct ImportPathItem {
Ast * decl;
};
-gb_internal Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet<AstPackage *> *visited) {
+gb_internal Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start, AstPackage *end, PtrSet<AstPackage *> *visited, gbAllocator allocator) {
Array<ImportPathItem> empty_path = {};
if (ptr_set_update(visited, start)) {
@@ -4888,11 +5216,11 @@ gb_internal Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start
ImportPathItem item = {pkg, decl};
if (pkg == end) {
- auto path = array_make<ImportPathItem>(heap_allocator());
+ auto path = array_make<ImportPathItem>(allocator);
array_add(&path, item);
return path;
}
- auto next_path = find_import_path(c, pkg, end, visited);
+ auto next_path = find_import_path(c, pkg, end, visited, allocator);
if (next_path.count > 0) {
array_add(&next_path, item);
return next_path;
@@ -4972,9 +5300,10 @@ gb_internal void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
GB_ASSERT(scope->flags&ScopeFlag_Pkg);
- if (ptr_set_update(&parent_scope->imported, scope)) {
- // error(token, "Multiple import of the same file within this scope");
- }
+ ptr_set_add(&parent_scope->imported, scope);
+ // if (ptr_set_update(&parent_scope->imported, scope)) {
+ // // error(token, "Multiple import of the same file within this scope");
+ // }
String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false);
if (is_blank_ident(import_name)) {
@@ -5496,14 +5825,9 @@ gb_internal void check_export_entities(Checker *c) {
}
gb_internal 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());
- }
- array_free(&dep_graph);
- });
+ TEMPORARY_ALLOCATOR_GUARD();
+ Array<ImportGraphNode *> dep_graph = generate_import_dependency_graph(c, temporary_allocator());
TIME_SECTION("check_import_entities - sort packages");
// NOTE(bill): Priority queue
@@ -5513,8 +5837,7 @@ gb_internal void check_import_entities(Checker *c) {
defer (ptr_set_destroy(&emitted));
Array<ImportGraphNode *> package_order = {};
- array_init(&package_order, heap_allocator(), 0, c->parser->packages.count);
- defer (array_free(&package_order));
+ array_init(&package_order, temporary_allocator(), 0, c->parser->packages.count);
while (pq.queue.count > 0) {
ImportGraphNode *n = priority_queue_pop(&pq);
@@ -5522,11 +5845,12 @@ gb_internal void check_import_entities(Checker *c) {
AstPackage *pkg = n->pkg;
if (n->dep_count > 0) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
PtrSet<AstPackage *> visited = {};
defer (ptr_set_destroy(&visited));
- auto path = find_import_path(c, pkg, pkg, &visited);
- defer (array_free(&path));
+ auto path = find_import_path(c, pkg, pkg, &visited, temporary_allocator());
if (path.count > 1) {
ImportPathItem item = path[path.count-1];
@@ -5541,7 +5865,7 @@ gb_internal void check_import_entities(Checker *c) {
}
}
- for (ImportGraphNode *p : n->pred) {
+ FOR_PTR_SET(p, n->pred) {
p->dep_count = gb_max(p->dep_count-1, 0);
priority_queue_fix(&pq, p->index);
}
@@ -5644,9 +5968,9 @@ gb_internal void check_import_entities(Checker *c) {
}
-gb_internal Array<Entity *> find_entity_path(Entity *start, Entity *end, PtrSet<Entity *> *visited = nullptr);
+gb_internal Array<Entity *> find_entity_path(Entity *start, Entity *end, gbAllocator allocator, PtrSet<Entity *> *visited = nullptr);
-gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, PtrSet<Entity *> *visited, Array<Entity *> *path_) {
+gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, gbAllocator allocator, PtrSet<Entity *> *visited, Array<Entity *> *path_) {
GB_ASSERT(path_ != nullptr);
if (tuple == nullptr) {
return false;
@@ -5658,14 +5982,15 @@ gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, PtrSet<Entity
if (var_decl == nullptr) {
continue;
}
- for (Entity *dep : var_decl->deps) {
+
+ FOR_PTR_SET(dep, var_decl->deps) {
if (dep == end) {
- auto path = array_make<Entity *>(heap_allocator());
+ auto path = array_make<Entity *>(allocator);
array_add(&path, dep);
*path_ = path;
return true;
}
- auto next_path = find_entity_path(dep, end, visited);
+ auto next_path = find_entity_path(dep, end, allocator, visited);
if (next_path.count > 0) {
array_add(&next_path, dep);
*path_ = next_path;
@@ -5677,7 +6002,7 @@ gb_internal bool find_entity_path_tuple(Type *tuple, Entity *end, PtrSet<Entity
return false;
}
-gb_internal Array<Entity *> find_entity_path(Entity *start, Entity *end, PtrSet<Entity *> *visited) {
+gb_internal Array<Entity *> find_entity_path(Entity *start, Entity *end, gbAllocator allocator, PtrSet<Entity *> *visited) {
PtrSet<Entity *> visited_ = {};
bool made_visited = false;
if (visited == nullptr) {
@@ -5701,20 +6026,20 @@ gb_internal Array<Entity *> find_entity_path(Entity *start, Entity *end, PtrSet<
GB_ASSERT(t->kind == Type_Proc);
Array<Entity *> path = {};
- if (find_entity_path_tuple(t->Proc.params, end, visited, &path)) {
+ if (find_entity_path_tuple(t->Proc.params, end, allocator, visited, &path)) {
return path;
}
- if (find_entity_path_tuple(t->Proc.results, end, visited, &path)) {
+ if (find_entity_path_tuple(t->Proc.results, end, allocator, visited, &path)) {
return path;
}
} else {
- for (Entity *dep : decl->deps) {
+ FOR_PTR_SET(dep, decl->deps) {
if (dep == end) {
- auto path = array_make<Entity *>(heap_allocator());
+ auto path = array_make<Entity *>(allocator);
array_add(&path, dep);
return path;
}
- auto next_path = find_entity_path(dep, end, visited);
+ auto next_path = find_entity_path(dep, end, allocator, visited);
if (next_path.count > 0) {
array_add(&next_path, dep);
return next_path;
@@ -5730,13 +6055,10 @@ gb_internal void calculate_global_init_order(Checker *c) {
CheckerInfo *info = &c->info;
TIME_SECTION("calculate_global_init_order: generate entity dependency graph");
- Array<EntityGraphNode *> dep_graph = generate_entity_dependency_graph(info, heap_allocator());
- defer ({
- for_array(i, dep_graph) {
- entity_graph_node_destroy(dep_graph[i], heap_allocator());
- }
- array_free(&dep_graph);
- });
+ Arena *temporary_arena = get_arena(ThreadArena_Temporary);
+ ArenaTempGuard temporary_arena_guard(temporary_arena);
+
+ Array<EntityGraphNode *> dep_graph = generate_entity_dependency_graph(info, temporary_arena);
TIME_SECTION("calculate_global_init_order: priority queue create");
// NOTE(bill): Priority queue
@@ -5751,8 +6073,8 @@ gb_internal void calculate_global_init_order(Checker *c) {
Entity *e = n->entity;
if (n->dep_count > 0) {
- auto path = find_entity_path(e, e);
- defer (array_free(&path));
+ TEMPORARY_ALLOCATOR_GUARD();
+ auto path = find_entity_path(e, e, temporary_allocator());
if (path.count > 0) {
Entity *e = path[0];
@@ -5765,7 +6087,7 @@ gb_internal void calculate_global_init_order(Checker *c) {
}
}
- for (EntityGraphNode *p : n->pred) {
+ FOR_PTR_SET(p, n->pred) {
p->dep_count -= 1;
p->dep_count = gb_max(p->dep_count, 0);
priority_queue_fix(&pq, p->index);
@@ -5869,7 +6191,7 @@ gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *u
switch (pi->decl->proc_checked_state.load()) {
case ProcCheckedState_InProgress:
if (e) {
- GB_ASSERT(global_procedure_body_in_worker_queue.load());
+ GB_ASSERT(global_procedure_body_in_worker_queue.load() != 0);
}
return false;
case ProcCheckedState_Checked:
@@ -5958,7 +6280,7 @@ gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *u
add_untyped_expressions(&c->info, ctx.untyped);
rw_mutex_shared_lock(&ctx.decl->deps_mutex);
- for (Entity *dep : ctx.decl->deps) {
+ FOR_PTR_SET(dep, ctx.decl->deps) {
if (dep && dep->kind == Entity_Procedure &&
(dep->flags & EntityFlag_ProcBodyChecked) == 0) {
check_procedure_later_from_entity(c, dep, NULL);
@@ -5987,8 +6309,10 @@ gb_internal void check_unchecked_bodies(Checker *c) {
// use the `procs_to_check` array
global_procedure_body_in_worker_queue = false;
- for (Entity *e : c->info.minimum_dependency_set) {
- check_procedure_later_from_entity(c, e, "check_unchecked_bodies");
+ for (Entity *e : c->info.entities) {
+ if (e->min_dep_count.load(std::memory_order_relaxed) > 0) {
+ check_procedure_later_from_entity(c, e, "check_unchecked_bodies");
+ }
}
if (!global_procedure_body_in_worker_queue) {
@@ -6011,8 +6335,9 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) {
defer (map_destroy(&untyped));
- for_array(i, c->info.all_procedures) {
- ProcInfo *pi = c->info.all_procedures[i];
+ array_reserve(&c->info.all_procedures, c->info.all_procedures_queue.count.load());
+
+ for (ProcInfo *pi = nullptr; mpsc_dequeue(&c->info.all_procedures_queue, &pi); /**/) {
GB_ASSERT(pi != nullptr);
GB_ASSERT(pi->decl != nullptr);
Entity *e = pi->decl->entity;
@@ -6027,6 +6352,8 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) {
consume_proc_info(c, pi, &untyped);
}
}
+
+ array_add(&c->info.all_procedures, pi);
}
}
@@ -6121,7 +6448,7 @@ gb_internal WORKER_TASK_PROC(check_proc_info_worker_proc) {
gb_internal void check_init_worker_data(Checker *c) {
u32 thread_count = cast(u32)global_thread_pool.threads.count;
- check_procedure_bodies_worker_data = gb_alloc_array(permanent_allocator(), CheckProcedureBodyWorkerData, thread_count);
+ check_procedure_bodies_worker_data = permanent_alloc_array<CheckProcedureBodyWorkerData>(thread_count);
for (isize i = 0; i < thread_count; i++) {
check_procedure_bodies_worker_data[i].c = c;
@@ -6183,7 +6510,7 @@ gb_internal Type *tuple_to_pointers(Type *ot) {
Type *t = alloc_type_tuple();
- t->Tuple.variables = slice_make<Entity *>(heap_allocator(), ot->Tuple.variables.count);
+ t->Tuple.variables = permanent_slice_make<Entity *>(ot->Tuple.variables.count);
Scope *scope = nullptr;
for_array(i, t->Tuple.variables) {
@@ -6225,6 +6552,12 @@ gb_internal void check_deferred_procedures(Checker *c) {
continue;
}
+ if (dst->flags & EntityFlag_Disabled) {
+ // Prevent procedures that have been disabled from acting as deferrals.
+ src->Procedure.deferred_procedure = {};
+ continue;
+ }
+
GB_ASSERT(is_type_proc(src->type));
GB_ASSERT(is_type_proc(dst->type));
Type *src_params = base_type(src->type)->Proc.params;
@@ -6380,6 +6713,309 @@ gb_internal void check_deferred_procedures(Checker *c) {
}
+gb_internal void handle_raddbg_type_view(Checker *c, RaddbgTypeView const &type_view) {
+ auto const struct_tag_lookup = [](String tag, char const *key_c, String *value_) -> bool {
+ String t = tag;
+ String key = make_string_c(key_c);
+ while (t.len != 0) {
+ isize i = 0;
+ while (i < t.len && t[i] == ' ') { // Skip whitespace
+ i += 1;
+ }
+ t.text += i;
+ t.len -= i;
+ if (t.len == 0) {
+ break;
+ }
+
+ i = 0;
+
+ while (i < t.len) {
+ u8 c = t[i];
+ if (c == ':' || c == '"') {
+ break;
+ } else if ((0 <= c && c < ' ') || (0x7f <= c && c <= 0x9f)) {
+ // break if control character is found
+ break;
+ }
+ i += 1;
+ }
+
+ if (i == 0) {
+ break;
+ }
+ if (i+1 >= t.len) {
+ break;
+ }
+ if (t[i] != ':' || t[i+1] != '"') {
+ break;
+ }
+ String name = {t.text, i};
+ t = {t.text+i+1, t.len-(i+1)};
+
+ i = 1;
+ while (i < t.len && t[i] != '"') { // find closing quote
+ if (t[i] == '\\') {
+ i += 1; // Skip escaped characters
+ }
+ i += 1;
+ }
+ if (i >= t.len) {
+ break;
+ }
+
+ String value = {t.text, i+1};
+ t = {t.text+i+1, t.len-(i+1)};
+
+ if (key == name) {
+ value = {value.text+1, i-1};
+ value = string_trim_whitespace(value);
+ if (value_) *value_ = value;
+ return true;
+ }
+ }
+ return false;
+ };
+
+ auto const parse_int = [](String s, isize *offset_, u64 *result_) -> bool {
+ isize offset = *offset_;
+ isize new_offset = *offset_;
+
+ u64 result = 0;
+
+ while (new_offset < s.len) {
+ u8 c = s[new_offset];
+ if (!('0' <= c && c <= '9')) {
+ break;
+ }
+
+ new_offset += 1;
+ result *= 10;
+ result += u64(c)-'0';
+ }
+
+ *offset_ = new_offset;
+ *result_ = result;
+ return new_offset > offset;
+ };
+
+ Type *type = type_view.type;
+ if (type == nullptr || type == t_invalid) {
+ return;
+ }
+ String view = type_view.view;
+ if (view.len != 0) {
+ array_add(&c->info.raddbg_type_views, RaddbgTypeView{type, view});
+ return;
+ }
+
+ // NOTE(bill): Generate one automatically from the struct field tags if they exist
+ // If it cannot be generated, it'll be ignored/err
+
+ Type *bt = base_type(type);
+ if (is_type_struct(type)) {
+ GB_ASSERT(bt->kind == Type_Struct);
+ if (bt->Struct.tags != nullptr) {
+ bool found_any = false;
+
+ for (isize i = 0; i < bt->Struct.fields.count; i++) {
+ String tag = bt->Struct.tags[i];
+ String value = {};
+ if (struct_tag_lookup(tag, "raddbg", &value)) {
+ found_any = true;
+ } else if (struct_tag_lookup(tag, "fmt", &value)) {
+ found_any = true;
+ }
+ }
+
+ if (!found_any) {
+ return;
+ }
+
+ gbString s = gb_string_make(heap_allocator(), "");
+
+ s = gb_string_appendc(s, "rows($");
+
+ for (isize i = 0; i < bt->Struct.fields.count; i++) {
+ Entity *field = bt->Struct.fields[i];
+ GB_ASSERT(field != nullptr);
+ String name = field->token.string;
+ String tag = bt->Struct.tags[i];
+ String value = {};
+ bool custom_rule = false;
+
+ bool raddbg_seen = false;
+ if (struct_tag_lookup(tag, "raddbg", &value)) {
+ raddbg_seen = true;
+ if (value == "-") {
+ // Ignore this field entirely;
+ continue;
+ }
+ }
+
+ s = gb_string_appendc(s, ", ");
+
+ if (raddbg_seen) {
+ if (value == "") {
+ // ignore
+ } else {
+ s = gb_string_append_length(s, value.text, value.len);
+ custom_rule = true;
+ }
+ } else if (struct_tag_lookup(tag, "fmt", &value)) {
+ if (value == "" || value == "-") {
+ // ignore
+ } else {
+ auto p = string_partition(value, make_string_c(","));
+ String head = p.head;
+ String tail = p.tail;
+
+ isize i = 0;
+
+ for (bool ok = true; ok && i < head.len; i += 1) {
+ switch (head[i]) {
+ case '+':
+ case '-':
+ case ' ':
+ case '#':
+ case '0':
+ break;
+ default:
+ i -= 1;
+ ok = false;
+ break;
+ }
+ }
+
+ u64 prec = 0;
+ u64 width = 0;
+ bool width_ok = parse_int(head, &i, &width);
+ bool prec_ok = false;
+ if (i < head.len && head[i] == '.') {
+ i += 1;
+ prec_ok = parse_int(head, &i, &prec);
+ }
+
+
+ Rune verb = 0;
+ if (i >= head.len || head[i] == ' ') {
+ verb = 'v';
+ } else {
+ utf8_decode(head.text+i, head.len-i, &verb);
+ }
+
+ isize paren_count = 0;
+
+
+ if (width_ok) {
+ s = gb_string_appendc(s, "digits(");
+ custom_rule = true;
+ }
+
+ switch (verb) {
+ case 'b':
+ s = gb_string_appendc(s, "bin(");
+ paren_count += 1;
+ break;
+ case 'd':
+ s = gb_string_appendc(s, "dec(");
+ paren_count += 1;
+ break;
+ case 'x':
+ case 'X':
+ s = gb_string_appendc(s, "hex(");
+ paren_count += 1;
+ break;
+ case 'o':
+ s = gb_string_appendc(s, "oct(");
+ paren_count += 1;
+ break;
+ }
+
+
+ if (tail.len != 0 && tail != "0") {
+ s = gb_string_appendc(s, "array(");
+ s = gb_string_append_length(s, name.text, name.len);
+ if (is_type_slice(field->type) || is_type_dynamic_array(field->type)) {
+ s = gb_string_appendc(s, ".data");
+ }
+ s = gb_string_appendc(s, ", ");
+ s = gb_string_append_length(s, tail.text, tail.len);
+ s = gb_string_appendc(s, ")");
+ custom_rule = true;
+ } else {
+ s = gb_string_append_length(s, name.text, name.len);
+ custom_rule = true;
+ }
+
+
+ for (isize j = 0; j < paren_count; j++) {
+ s = gb_string_appendc(s, ")");
+ custom_rule = true;
+ }
+ if (width_ok) {
+ s = gb_string_append_fmt(s, ", %llu)", cast(unsigned long long)width);
+ }
+ }
+ }
+
+ if (!custom_rule) {
+ s = gb_string_append_length(s, name.text, name.len);
+ }
+ }
+
+ s = gb_string_appendc(s, ")");
+
+ view = make_string((u8 const *)s, gb_string_length(s));
+ }
+ }
+
+ if (view.len == 0) {
+ // Ignore the type, it didn't anything custom
+ return;
+ }
+
+ array_add(&c->info.raddbg_type_views, RaddbgTypeView{type, view});
+}
+
+gb_internal void check_objc_context_provider_procedures(Checker *c) {
+ for (Entity *e = nullptr; mpsc_dequeue(&c->procs_with_objc_context_provider_to_check, &e); /**/) {
+ GB_ASSERT(e->kind == Entity_TypeName);
+
+ Entity *proc_entity = e->TypeName.objc_context_provider;
+ GB_ASSERT(proc_entity->kind == Entity_Procedure);
+
+ auto &proc = proc_entity->type->Proc;
+
+ Type *return_type = proc.result_count != 1 ? t_untyped_nil : base_named_type(proc.results->Tuple.variables[0]->type);
+ if (return_type != t_context) {
+ error(proc_entity->token, "The @(objc_context_provider) procedure must only return a context.");
+ }
+
+ const char *self_param_err = "The @(objc_context_provider) procedure must take as a parameter a single pointer to the @(objc_type) value.";
+ if (proc.param_count != 1) {
+ error(proc_entity->token, self_param_err);
+ }
+
+ Type *self_param = base_type(proc.params->Tuple.variables[0]->type);
+ if (self_param->kind != Type_Pointer) {
+ error(proc_entity->token, self_param_err);
+ }
+
+ Type *self_type = base_named_type(self_param->Pointer.elem);
+ if (!internal_check_is_assignable_to(self_type, e->type) &&
+ !(e->TypeName.objc_ivar && internal_check_is_assignable_to(self_type, e->TypeName.objc_ivar))) {
+ error(proc_entity->token, self_param_err);
+ }
+ if (proc.calling_convention != ProcCC_CDecl && proc.calling_convention != ProcCC_Contextless) {
+ error(e->token, self_param_err);
+ }
+ if (proc.is_polymorphic) {
+ error(e->token, self_param_err);
+ }
+ }
+}
+
gb_internal void check_unique_package_names(Checker *c) {
ERROR_BLOCK();
@@ -6414,6 +7050,19 @@ gb_internal void check_unique_package_names(Checker *c) {
"\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n"
"\tA package name is required for link name prefixing to have a consistent ABI\n");
error_line("%s found at previous location\n", token_pos_to_string(ast_token(prev).pos));
+
+ // NOTE(Jeroen): Check if the conflicting imports are the same case-folded directory
+ // See https://github.com/odin-lang/Odin/issues/5080
+ #if defined(GB_SYSTEM_WINDOWS)
+ String dir_a = pkg->files[0]->directory;
+ String dir_b = (*found)->files[0]->directory;
+
+ if (str_eq_ignore_case(dir_a, dir_b)) {
+ error_line("\tRemember that Windows case-folds paths, and so %.*s and %.*s are the same directory.\n", LIT(dir_a), LIT(dir_b));
+ // Could also perform a FS lookup to check which of the two is the actual directory and suggest it, but this should be enough.
+ }
+ #endif
+
end_error_block();
}
}
@@ -6440,6 +7089,7 @@ gb_internal void check_merge_queues_into_arrays(Checker *c) {
}
check_add_entities_from_queues(c);
check_add_definitions_from_queues(c);
+ thread_pool_wait();
}
gb_internal GB_COMPARE_PROC(init_procedures_cmp) {
@@ -6494,17 +7144,23 @@ gb_internal void check_sort_init_and_fini_procedures(Checker *c) {
}
gb_internal void add_type_info_for_type_definitions(Checker *c) {
- for_array(i, c->info.definitions) {
- Entity *e = c->info.definitions[i];
- if (e->kind == Entity_TypeName && e->type != nullptr) {
+ for (Entity *e : c->info.definitions) {
+ if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) {
+ #if 0
i64 align = type_align_of(e->type);
- if (align > 0 && ptr_set_exists(&c->info.minimum_dependency_set, e)) {
+ if (align > 0 && e->min_dep_count.load(std::memory_order_relaxed) > 0) {
add_type_info_type(&c->builtin_ctx, e->type);
}
+ #else
+ if (e->min_dep_count.load(std::memory_order_relaxed) > 0) {
+ add_type_info_type(&c->builtin_ctx, e->type);
+ }
+ #endif
}
}
}
+#if 0
gb_internal void check_walk_all_dependencies(DeclInfo *decl) {
if (decl == nullptr) {
return;
@@ -6526,8 +7182,116 @@ gb_internal void check_update_dependency_tree_for_procedures(Checker *c) {
check_walk_all_dependencies(decl);
}
}
+#else
+gb_internal void check_walk_all_dependencies(DeclInfo *decl);
+
+gb_internal WORKER_TASK_PROC(check_walk_all_dependencies_worker_proc) {
+ if (data == nullptr) {
+ return 0;
+ }
+ DeclInfo *decl = cast(DeclInfo *)data;
+
+ for (DeclInfo *child = decl->next_child; child != nullptr; child = child->next_sibling) {
+ thread_pool_add_task(check_walk_all_dependencies_worker_proc, child);
+ check_walk_all_dependencies(child);
+ }
+
+ add_deps_from_child_to_parent(decl);
+ return 0;
+}
+
+gb_internal void check_walk_all_dependencies(DeclInfo *decl) {
+ if (decl != nullptr) {
+ thread_pool_add_task(check_walk_all_dependencies_worker_proc, decl);
+ }
+}
+
+gb_internal void check_update_dependency_tree_for_procedures(Checker *c) {
+ mutex_lock(&c->nested_proc_lits_mutex);
+ for (DeclInfo *decl : c->nested_proc_lits) {
+ check_walk_all_dependencies(decl);
+ }
+ mutex_unlock(&c->nested_proc_lits_mutex);
+ for (Entity *e : c->info.entities) {
+ DeclInfo *decl = e->decl_info;
+ check_walk_all_dependencies(decl);
+ }
+
+ thread_pool_wait();
+}
+#endif
+
+gb_internal WORKER_TASK_PROC(check_scope_usage_file_worker) {
+ Checker *c = global_checker_ptr.load(std::memory_order_relaxed);
+ AstFile *f = cast(AstFile *)data;
+ u64 vet_flags = ast_file_vet_flags(f);
+ check_scope_usage(c, f->scope, vet_flags);
+ return 0;
+}
+
+gb_internal WORKER_TASK_PROC(check_scope_usage_pkg_worker) {
+ Checker *c = global_checker_ptr.load(std::memory_order_relaxed);
+ AstPackage *pkg = cast(AstPackage *)data;
+ check_scope_usage_internal(c, pkg->scope, 0, true);
+ return 0;
+}
+
+
+
+gb_internal void check_all_scope_usages(Checker *c) {
+ for (auto const &entry : c->info.files) {
+ AstFile *f = entry.value;
+ thread_pool_add_task(check_scope_usage_file_worker, f);
+ }
+ for (auto const &entry : c->info.packages) {
+ AstPackage *pkg = entry.value;
+ thread_pool_add_task(check_scope_usage_pkg_worker, pkg);
+ }
+
+ thread_pool_wait();
+}
+
+
+gb_internal void check_for_type_cycles(Checker *c) {
+ // NOTE(bill): Check for illegal cyclic type declarations
+ for_array(i, c->info.definitions) {
+ Entity *e = c->info.definitions[i];
+ if (e->kind != Entity_TypeName) {
+ continue;
+ }
+ if (e->type != nullptr && is_type_typed(e->type)) {
+ if (e->TypeName.is_type_alias) {
+ // Ignore for the time being
+ } else {
+ (void)type_align_of(e->type);
+ }
+ }
+ }
+}
+
+gb_internal void check_for_inline_cycles(Checker *c) {
+ for_array(i, c->info.definitions) {
+ Entity *e = c->info.definitions[i];
+ if (e->kind != Entity_Procedure) {
+ continue;
+ }
+ DeclInfo *decl = e->decl_info;
+ ast_node(pl, ProcLit, decl->proc_lit);
+ if (pl->inlining == ProcInlining_inline) {
+ FOR_PTR_SET(dep, decl->deps) {
+ if (dep == e) {
+ error(e->token, "Cannot inline recursive procedure '%.*s'", LIT(e->token.string));
+ break;
+ }
+ }
+ }
+ }
+}
+
gb_internal void check_parsed_files(Checker *c) {
+ global_checker_ptr.store(c, std::memory_order_relaxed);
+
TIME_SECTION("map full filepaths to scope");
add_type_info_type(&c->builtin_ctx, t_invalid);
@@ -6591,16 +7355,9 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("add entities from procedure bodies");
check_merge_queues_into_arrays(c);
- TIME_SECTION("check scope usage");
- for (auto const &entry : c->info.files) {
- AstFile *f = entry.value;
- u64 vet_flags = ast_file_vet_flags(f);
- check_scope_usage(c, f->scope, vet_flags);
- }
- for (auto const &entry : c->info.packages) {
- AstPackage *pkg = entry.value;
- check_scope_usage_internal(c, pkg->scope, 0, true);
- }
+ TIME_SECTION("check all scope usages");
+ check_all_scope_usages(c);
+
TIME_SECTION("add basic type information");
// Add "Basic" type information
@@ -6613,29 +7370,18 @@ gb_internal void check_parsed_files(Checker *c) {
}
check_merge_queues_into_arrays(c);
- TIME_SECTION("check for type cycles and inline cycles");
- // NOTE(bill): Check for illegal cyclic type declarations
- for_array(i, c->info.definitions) {
- Entity *e = c->info.definitions[i];
- if (e->kind == Entity_TypeName && e->type != nullptr) {
- (void)type_align_of(e->type);
- } else if (e->kind == Entity_Procedure) {
- DeclInfo *decl = e->decl_info;
- ast_node(pl, ProcLit, decl->proc_lit);
- if (pl->inlining == ProcInlining_inline) {
- for (Entity *dep : decl->deps) {
- if (dep == e) {
- error(e->token, "Cannot inline recursive procedure '%.*s'", LIT(e->token.string));
- break;
- }
- }
- }
- }
- }
+ TIME_SECTION("check for type cycles");
+ check_for_type_cycles(c);
+
+ TIME_SECTION("check for inline cycles");
+ check_for_inline_cycles(c);
TIME_SECTION("check deferred procedures");
check_deferred_procedures(c);
+ TIME_SECTION("check objc context provider procedures");
+ check_objc_context_provider_procedures(c);
+
TIME_SECTION("calculate global init order");
calculate_global_init_order(c);
@@ -6653,11 +7399,9 @@ gb_internal void check_parsed_files(Checker *c) {
check_unchecked_bodies(c);
TIME_SECTION("check #soa types");
-
check_merge_queues_into_arrays(c);
- thread_pool_wait();
- TIME_SECTION("update minimum dependency set");
+ TIME_SECTION("update minimum dependency set again");
generate_minimum_dependency_set_internal(c, c->info.entry_point);
// NOTE(laytan): has to be ran after generate_minimum_dependency_set,
@@ -6722,7 +7466,6 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("add untyped expression values");
- // Add untyped expression values
for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) {
GB_ASSERT(u.expr != nullptr && u.info != nullptr);
if (is_type_typed(u.info->type)) {
@@ -6733,9 +7476,10 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("initialize and check for collisions in type info array");
{
+ TEMPORARY_ALLOCATOR_GUARD();
+
Array<TypeInfoPair> type_info_types; // sorted after filled
- array_init(&type_info_types, heap_allocator());
- defer (array_free(&type_info_types));
+ array_init(&type_info_types, temporary_allocator());
for (auto const &tt : c->info.min_dep_type_info_set) {
array_add(&type_info_types, tt);
@@ -6765,7 +7509,7 @@ gb_internal void check_parsed_files(Checker *c) {
if (entry.key != tt.hash) {
continue;
}
- auto const &other = type_info_types[entry.value];
+ auto const &other = c->info.type_info_types_hash_map[entry.value];
if (are_types_identical_unique_tuples(tt.type, other.type)) {
continue;
}
@@ -6798,6 +7542,11 @@ gb_internal void check_parsed_files(Checker *c) {
}
}
+ TIME_SECTION("collate type info stuff");
+ for (RaddbgTypeView type_view; mpsc_dequeue(&c->info.raddbg_type_views_queue, &type_view); /**/) {
+ handle_raddbg_type_view(c, type_view);
+ }
+
TIME_SECTION("type check finish");
}