diff options
Diffstat (limited to 'src/checker.cpp')
| -rw-r--r-- | src/checker.cpp | 1263 |
1 files changed, 899 insertions, 364 deletions
diff --git a/src/checker.cpp b/src/checker.cpp index 29f22bd9c..5d3263789 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3,6 +3,8 @@ #include "entity.cpp" #include "types.cpp" +String get_final_microarchitecture(); + gb_internal void check_expr(CheckerContext *c, Operand *operand, Ast *expression); gb_internal void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr); gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t); @@ -48,15 +50,6 @@ gb_internal bool check_rtti_type_disallowed(Ast *expr, Type *type, char const *f return check_rtti_type_disallowed(ast_token(expr), type, format); } -gb_internal void scope_reset(Scope *scope) { - if (scope == nullptr) return; - - rw_mutex_lock(&scope->mutex); - scope->head_child.store(nullptr, std::memory_order_relaxed); - string_map_clear(&scope->elements); - ptr_set_clear(&scope->imported); - rw_mutex_unlock(&scope->mutex); -} gb_internal void scope_reserve(Scope *scope, isize count) { string_map_reserve(&scope->elements, 2*count); @@ -166,9 +159,6 @@ gb_internal void import_graph_node_swap(ImportGraphNode **data, isize i, isize j } - - - gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { gb_zero_item(d); if (parent) { @@ -182,6 +172,9 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { ptr_set_init(&d->deps, 0); ptr_set_init(&d->type_info_deps, 0); d->labels.allocator = heap_allocator(); + d->variadic_reuses.allocator = heap_allocator(); + d->variadic_reuse_max_bytes = 0; + d->variadic_reuse_max_align = 1; } gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) { @@ -224,7 +217,7 @@ gb_internal Scope *create_scope(CheckerInfo *info, Scope *parent) { if (parent != nullptr && parent != builtin_pkg->scope) { Scope *prev_head_child = parent->head_child.exchange(s, std::memory_order_acq_rel); if (prev_head_child) { - prev_head_child->next.store(s, std::memory_order_release); + s->next.store(prev_head_child, std::memory_order_release); } } @@ -313,6 +306,7 @@ gb_internal void add_scope(CheckerContext *c, Ast *node, Scope *scope) { case Ast_StructType: node->StructType.scope = scope; break; case Ast_UnionType: node->UnionType.scope = scope; break; case Ast_EnumType: node->EnumType.scope = scope; break; + case Ast_BitFieldType: node->BitFieldType.scope = scope; break; default: GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind])); } } @@ -334,6 +328,7 @@ gb_internal Scope *scope_of_node(Ast *node) { case Ast_StructType: return node->StructType.scope; case Ast_UnionType: return node->UnionType.scope; case Ast_EnumType: return node->EnumType.scope; + case Ast_BitFieldType: return node->BitFieldType.scope; } GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind])); return nullptr; @@ -342,6 +337,7 @@ gb_internal Scope *scope_of_node(Ast *node) { gb_internal void check_open_scope(CheckerContext *c, Ast *node) { node = unparen_expr(node); + GB_ASSERT(node != nullptr); GB_ASSERT(node->kind == Ast_Invalid || is_ast_stmt(node) || is_ast_type(node)); @@ -355,6 +351,7 @@ gb_internal void check_open_scope(CheckerContext *c, Ast *node) { case Ast_EnumType: case Ast_UnionType: case Ast_BitSetType: + case Ast_BitFieldType: scope->flags |= ScopeFlag_Type; break; } @@ -375,6 +372,7 @@ gb_internal Entity *scope_lookup_current(Scope *s, String const &name) { return nullptr; } + gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_) { if (scope != nullptr) { bool gone_thru_proc = false; @@ -502,9 +500,15 @@ 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; - return scope_insert_with_name(s, name, entity); + if (in_single_threaded_checker_stage) { + return scope_insert_with_name_no_mutex(s, name, entity); + } else { + return scope_insert_with_name(s, name, entity); + } } gb_internal Entity *scope_insert_no_mutex(Scope *s, Entity *entity) { @@ -513,7 +517,7 @@ gb_internal Entity *scope_insert_no_mutex(Scope *s, Entity *entity) { } -GB_COMPARE_PROC(entity_variable_pos_cmp) { +gb_internal GB_COMPARE_PROC(entity_variable_pos_cmp) { Entity *x = *cast(Entity **)a; Entity *y = *cast(Entity **)b; @@ -529,20 +533,32 @@ gb_internal u64 check_vet_flags(CheckerContext *c) { c->curr_proc_decl->proc_lit) { file = c->curr_proc_decl->proc_lit->file(); } - if (file && file->vet_flags_set) { - return file->vet_flags; - } - return build_context.vet_flags; + + return ast_file_vet_flags(file); } gb_internal u64 check_vet_flags(Ast *node) { AstFile *file = node->file(); - if (file && file->vet_flags_set) { - return file->vet_flags; + return ast_file_vet_flags(file); +} + +gb_internal u64 check_feature_flags(CheckerContext *c, Ast *node) { + AstFile *file = c->file; + if (file == nullptr && + c->curr_proc_decl && + c->curr_proc_decl->proc_lit) { + file = c->curr_proc_decl->proc_lit->file(); + } + if (file == nullptr) { + file = node->file(); } - return build_context.vet_flags; + if (file != nullptr && file->feature_flags_set) { + return file->feature_flags; + } + return 0; } + enum VettedEntityKind { VettedEntity_Invalid, @@ -649,7 +665,7 @@ gb_internal bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) { } } - zero_item(ve); + gb_zero_item(ve); ve->kind = VettedEntity_Shadowed; ve->entity = e; ve->other = shadowed; @@ -668,7 +684,7 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) { } case Entity_ImportName: case Entity_LibraryName: - zero_item(ve); + gb_zero_item(ve); ve->kind = VettedEntity_Unused; ve->entity = e; return true; @@ -677,20 +693,48 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) { return false; } -gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { - bool vet_unused = (vet_flags & VetFlag_Unused) != 0; - bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0; - +gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_flags, bool per_entity) { + u64 original_vet_flags = vet_flags; Array<VettedEntity> vetted_entities = {}; array_init(&vetted_entities, heap_allocator()); + defer (array_free(&vetted_entities)); rw_mutex_shared_lock(&scope->mutex); for (auto const &entry : scope->elements) { Entity *e = entry.value; if (e == nullptr) continue; + + vet_flags = original_vet_flags; + if (per_entity) { + vet_flags = ast_file_vet_flags(e->file); + } + + bool vet_unused = (vet_flags & VetFlag_Unused) != 0; + bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0; + bool vet_unused_procedures = (vet_flags & VetFlag_UnusedProcedures) != 0; + if (vet_unused_procedures && e->pkg && e->pkg->kind == Package_Runtime) { + vet_unused_procedures = false; + } + VettedEntity ve_unused = {}; VettedEntity ve_shadowed = {}; - bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused); + 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) { + if (e->flags&EntityFlag_Used) { + is_unused = false; + } else if (e->flags & EntityFlag_Require) { + is_unused = false; + } else if (e->pkg && e->pkg->kind == Package_Init && e->token.string == "main") { + is_unused = false; + } else { + is_unused = true; + ve_unused.kind = VettedEntity_Unused; + ve_unused.entity = e; + } + } bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed); if (is_unused && is_shadowed) { VettedEntity ve_both = ve_shadowed; @@ -700,23 +744,43 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { 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) { + i64 sz = type_size_of(e->type); + // TODO(bill): When is a good size warn? + // Is >256 KiB good enough? + if (sz > 1ll<<18) { + gbString type_str = type_to_string(e->type); + warning(e->token, "Declaration of '%.*s' may cause a stack overflow due to its type '%s' having a size of %lld bytes", LIT(e->token.string), type_str, cast(long long)sz); + gb_string_free(type_str); + } } } rw_mutex_shared_unlock(&scope->mutex); - gb_sort(vetted_entities.data, vetted_entities.count, gb_size_of(VettedEntity), vetted_entity_variable_pos_cmp); + array_sort(vetted_entities, vetted_entity_variable_pos_cmp); for (auto const &ve : vetted_entities) { Entity *e = ve.entity; Entity *other = ve.other; String name = e->token.string; + vet_flags = original_vet_flags; + if (per_entity) { + vet_flags = ast_file_vet_flags(e->file); + } + if (ve.kind == VettedEntity_Shadowed_And_Unused) { error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line); } else if (vet_flags) { switch (ve.kind) { case VettedEntity_Unused: - if (vet_flags & VetFlag_Unused) { + if (e->kind == Entity_Variable && (vet_flags & VetFlag_UnusedVariables) != 0) { + error(e->token, "'%.*s' declared but not used", LIT(name)); + } + if (e->kind == Entity_Procedure && (vet_flags & VetFlag_UnusedProcedures) != 0) { + error(e->token, "'%.*s' declared but not used", LIT(name)); + } + if ((e->kind == Entity_ImportName || e->kind == Entity_LibraryName) && (vet_flags & VetFlag_UnusedImports) != 0) { error(e->token, "'%.*s' declared but not used", LIT(name)); } break; @@ -731,20 +795,13 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { break; } } - - if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using)) == 0) { - i64 sz = type_size_of(e->type); - // TODO(bill): When is a good size warn? - // Is 128 KiB good enough? - if (sz >= 1ll<<17) { - gbString type_str = type_to_string(e->type); - warning(e->token, "Declaration of '%.*s' may cause a stack overflow due to its type '%s' having a size of %lld bytes", LIT(name), type_str, cast(long long)sz); - gb_string_free(type_str); - } - } } - array_free(&vetted_entities); +} + + +gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { + check_scope_usage_internal(c, scope, vet_flags, false); for (Scope *child = scope->head_child; child != nullptr; child = child->next) { if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) { @@ -770,15 +827,17 @@ gb_internal void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type * rw_mutex_unlock(&d->type_info_deps_mutex); } -gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) { + +gb_internal AstPackage *get_runtime_package(CheckerInfo *info) { + String name = str_lit("runtime"); gbAllocator a = heap_allocator(); - String path = get_fullpath_core(a, name); + String path = get_fullpath_base_collection(a, name, nullptr); defer (gb_free(a, path.text)); auto found = string_map_get(&info->packages, path); if (found == nullptr) { gb_printf_err("Name: %.*s\n", LIT(name)); gb_printf_err("Fullpath: %.*s\n", LIT(path)); - + for (auto const &entry : info->packages) { gb_printf_err("%.*s\n", LIT(entry.key)); } @@ -787,14 +846,37 @@ gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) { return *found; } +gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) { + if (name == "runtime") { + return get_runtime_package(info); + } -gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name) { + gbAllocator a = heap_allocator(); + String path = get_fullpath_core_collection(a, name, nullptr); + defer (gb_free(a, path.text)); + auto found = string_map_get(&info->packages, path); + if (found == nullptr) { + gb_printf_err("Name: %.*s\n", LIT(name)); + gb_printf_err("Fullpath: %.*s\n", LIT(path)); + + for (auto const &entry : info->packages) { + gb_printf_err("%.*s\n", LIT(entry.key)); + } + GB_ASSERT_MSG(found != nullptr, "Missing core package %.*s", LIT(name)); + } + return *found; +} + +gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name, bool required=false) { String n = make_string_c(name); AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name)); Entity *e = scope_lookup(p->scope, n); GB_ASSERT_MSG(e != nullptr, "%s", name); GB_ASSERT(c->decl != nullptr); e->flags |= EntityFlag_Used; + if (required) { + e->flags |= EntityFlag_Require; + } add_dependency(c->info, c->decl, e); } @@ -815,6 +897,10 @@ gb_internal void add_declaration_dependency(CheckerContext *c, Entity *e) { if (e == nullptr) { return; } + if (e->flags & EntityFlag_Disabled) { + // ignore the dependencies if it has been `@(disabled=true)` + return; + } if (c->decl != nullptr) { add_dependency(c->info, c->decl, e); } @@ -968,10 +1054,11 @@ 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_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); { GlobalEnumValue values[TargetOs_COUNT] = { @@ -981,9 +1068,12 @@ gb_internal void init_universal(void) { {"Linux", TargetOs_linux}, {"Essence", TargetOs_essence}, {"FreeBSD", TargetOs_freebsd}, + {"Haiku", TargetOs_haiku}, {"OpenBSD", TargetOs_openbsd}, + {"NetBSD", TargetOs_netbsd}, {"WASI", TargetOs_wasi}, {"JS", TargetOs_js}, + {"Orca", TargetOs_orca}, {"Freestanding", TargetOs_freestanding}, }; @@ -1001,17 +1091,21 @@ gb_internal void init_universal(void) { {"arm64", TargetArch_arm64}, {"wasm32", TargetArch_wasm32}, {"wasm64p32", TargetArch_wasm64p32}, + {"riscv64", TargetArch_riscv64}, }; auto fields = add_global_enum_type(str_lit("Odin_Arch_Type"), values, gb_count_of(values)); add_global_enum_constant(fields, "ODIN_ARCH", bc->metrics.arch); add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]); } + + add_global_string_constant("ODIN_MICROARCH_STRING", get_final_microarchitecture()); { GlobalEnumValue values[BuildMode_COUNT] = { {"Executable", BuildMode_Executable}, {"Dynamic", BuildMode_DynamicLibrary}, + {"Static", BuildMode_StaticLibrary}, {"Object", BuildMode_Object}, {"Assembly", BuildMode_Assembly}, {"LLVM_IR", BuildMode_LLVM_IR}, @@ -1067,24 +1161,59 @@ gb_internal void init_universal(void) { scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name); } + { + int minimum_os_version = 0; + if (build_context.minimum_os_version_string != "") { + int major, minor, revision = 0; + #if defined(GB_SYSTEM_WINDOWS) + sscanf_s(cast(const char *)(build_context.minimum_os_version_string.text), "%d.%d.%d", &major, &minor, &revision); + #else + sscanf(cast(const char *)(build_context.minimum_os_version_string.text), "%d.%d.%d", &major, &minor, &revision); + #endif + minimum_os_version = (major*10000)+(minor*100)+revision; + } + add_global_constant("ODIN_MINIMUM_OS_VERSION", t_untyped_integer, exact_value_i64(minimum_os_version)); + } + + add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG); + add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT); + add_global_bool_constant("ODIN_DEFAULT_TO_NIL_ALLOCATOR", bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR); + add_global_bool_constant("ODIN_NO_BOUNDS_CHECK", build_context.no_bounds_check); + add_global_bool_constant("ODIN_NO_TYPE_ASSERT", build_context.no_type_assert); + add_global_bool_constant("ODIN_DEFAULT_TO_PANIC_ALLOCATOR", bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR); + add_global_bool_constant("ODIN_NO_CRT", bc->no_crt); + add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules); + add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test); + add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point); + add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES); + add_global_bool_constant("ODIN_NO_RTTI", bc->no_rtti); + + add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT); + add_global_bool_constant("ODIN_TILDE", bc->tilde_backend); - add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG); - add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT); - add_global_bool_constant("ODIN_DEFAULT_TO_NIL_ALLOCATOR", bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR); - add_global_bool_constant("ODIN_NO_DYNAMIC_LITERALS", bc->no_dynamic_literals); - add_global_bool_constant("ODIN_NO_CRT", bc->no_crt); - add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules); - add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test); - add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point); - add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES); - add_global_bool_constant("ODIN_NO_RTTI", bc->no_rtti); + add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp())); - add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT); - add_global_bool_constant("ODIN_TILDE", bc->tilde_backend); + { + String version = {}; - add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp())); + #ifdef GIT_SHA + version.text = cast(u8 *)GIT_SHA; + version.len = gb_strlen(GIT_SHA); + #endif - add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", lb_use_new_pass_system()); + add_global_string_constant("ODIN_VERSION_HASH", version); + } + + { + bool f16_supported = lb_use_new_pass_system(); + if (is_arch_wasm()) { + f16_supported = false; + } else if (build_context.metrics.os == TargetOs_darwin && build_context.metrics.arch == TargetArch_amd64) { + // NOTE(laytan): See #3222 for my ramblings on this. + f16_supported = false; + } + add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", f16_supported); + } { GlobalEnumValue values[3] = { @@ -1112,6 +1241,18 @@ gb_internal void init_universal(void) { add_global_constant("ODIN_SANITIZER_FLAGS", named_type, exact_value_u64(bc->sanitizer_flags)); } + { + GlobalEnumValue values[5] = { + {"None", -1}, + {"Minimal", 0}, + {"Size", 1}, + {"Speed", 2}, + {"Aggressive", 3}, + }; + + auto fields = add_global_enum_type(str_lit("Odin_Optimization_Mode"), values, gb_count_of(values)); + add_global_enum_constant(fields, "ODIN_OPTIMIZATION_MODE", bc->optimization_level); + } // Builtin Procedures @@ -1173,7 +1314,7 @@ gb_internal void init_universal(void) { } if (defined_values_double_declaration) { - gb_exit(1); + exit_with_errors(); } @@ -1187,9 +1328,9 @@ gb_internal void init_universal(void) { // intrinsics types for objective-c stuff { - t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct()); - t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct()); - t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct()); + 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_id = alloc_type_pointer(t_objc_object); t_objc_SEL = alloc_type_pointer(t_objc_selector); @@ -1220,6 +1361,7 @@ gb_internal void init_checker_info(CheckerInfo *i) { array_init(&i->init_procedures, a, 0, 0); array_init(&i->fini_procedures, a, 0, 0); array_init(&i->required_foreign_imports_through_force, a, 0, 0); + array_init(&i->defineables, a); map_init(&i->objc_msgSend_types); string_map_init(&i->load_file_cache); @@ -1229,7 +1371,12 @@ gb_internal void init_checker_info(CheckerInfo *i) { mpsc_init(&i->definition_queue, a); //); // 1<<20); mpsc_init(&i->required_global_variable_queue, a); // 1<<10); mpsc_init(&i->required_foreign_imports_through_force_queue, a); // 1<<10); + mpsc_init(&i->foreign_imports_to_check_fullpaths, a); // 1<<10); + 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 + + string_map_init(&i->load_directory_cache); + map_init(&i->load_directory_map); } gb_internal void destroy_checker_info(CheckerInfo *i) { @@ -1245,14 +1392,19 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { string_map_destroy(&i->packages); array_free(&i->variable_init_order); array_free(&i->required_foreign_imports_through_force); + array_free(&i->defineables); mpsc_destroy(&i->entity_queue); mpsc_destroy(&i->definition_queue); mpsc_destroy(&i->required_global_variable_queue); mpsc_destroy(&i->required_foreign_imports_through_force_queue); + mpsc_destroy(&i->foreign_imports_to_check_fullpaths); + mpsc_destroy(&i->foreign_decls_to_check); map_destroy(&i->objc_msgSend_types); string_map_destroy(&i->load_file_cache); + string_map_destroy(&i->load_directory_cache); + map_destroy(&i->load_directory_map); } gb_internal CheckerContext make_checker_context(Checker *c) { @@ -1290,7 +1442,7 @@ gb_internal void reset_checker_context(CheckerContext *ctx, AstFile *file, Untyp auto type_path = ctx->type_path; array_clear(type_path); - zero_size(&ctx->pkg, gb_size_of(CheckerContext) - gb_offset_of(CheckerContext, pkg)); + gb_zero_size(&ctx->pkg, gb_size_of(CheckerContext) - gb_offset_of(CheckerContext, pkg)); ctx->file = nullptr; ctx->scope = builtin_pkg->scope; @@ -1326,6 +1478,7 @@ gb_internal void init_checker(Checker *c) { array_init(&c->nested_proc_lits, heap_allocator(), 0, 1<<20); mpsc_init(&c->global_untyped_queue, a); // , 1<<20); + mpsc_init(&c->soa_types_to_complete, a); // , 1<<20); c->builtin_ctx = make_checker_context(c); } @@ -1338,6 +1491,7 @@ gb_internal void destroy_checker(Checker *c) { array_free(&c->nested_proc_lits); array_free(&c->procs_to_check); mpsc_destroy(&c->global_untyped_queue); + mpsc_destroy(&c->soa_types_to_complete); } @@ -1372,6 +1526,7 @@ gb_internal Entity *implicit_entity_of_node(Ast *clause) { } gb_internal Entity *entity_of_node(Ast *expr) { +retry:; expr = unparen_expr(expr); switch (expr->kind) { case_ast_node(ident, Ident, expr); @@ -1388,6 +1543,21 @@ gb_internal Entity *entity_of_node(Ast *expr) { case_ast_node(cc, CaseClause, expr); return cc->implicit_entity; case_end; + + case_ast_node(ce, CallExpr, expr); + return ce->entity_procedure_of; + case_end; + + case_ast_node(we, TernaryWhenExpr, expr); + if (we->cond == nullptr) { + break; + } + if (we->cond->tav.value.kind != ExactValue_Bool) { + break; + } + expr = we->cond->tav.value.value_bool ? we->x : we->y; + goto retry; + case_end; } return nullptr; } @@ -1535,9 +1705,9 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo if (mode == Addressing_Constant || mode == Addressing_Invalid) { expr->tav.value = value; - } else if (mode == Addressing_Value && is_type_typeid(type)) { + } else if (mode == Addressing_Value && type != nullptr && is_type_typeid(type)) { expr->tav.value = value; - } else if (mode == Addressing_Value && is_type_proc(type)) { + } else if (mode == Addressing_Value && type != nullptr && is_type_proc(type)) { expr->tav.value = value; } @@ -1637,6 +1807,26 @@ gb_internal bool add_entity_with_name(CheckerContext *c, Scope *scope, Ast *iden } return true; } + +gb_internal bool add_entity_with_name(CheckerInfo *info, 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) { + return redeclaration_error(name, entity, ie); + } + } + if (identifier != nullptr) { + GB_ASSERT(entity->file != nullptr); + add_entity_definition(info, identifier, entity); + } + return true; +} + gb_internal bool add_entity(CheckerContext *c, Scope *scope, Ast *identifier, Entity *entity) { return add_entity_with_name(c, scope, identifier, entity, entity->token.string); } @@ -1649,13 +1839,14 @@ gb_internal void add_entity_use(CheckerContext *c, Ast *identifier, Entity *enti entity->flags |= EntityFlag_Used; if (entity_has_deferred_procedure(entity)) { Entity *deferred = entity->Procedure.deferred_procedure.entity; - add_entity_use(c, nullptr, deferred); + if (deferred != entity) { + add_entity_use(c, nullptr, deferred); + } } if (identifier == nullptr || identifier->kind != Ast_Ident) { return; } - Ast *empty_ident = nullptr; - entity->identifier.compare_exchange_strong(empty_ident, identifier); + entity->identifier.store(identifier); identifier->Ident.entity = entity; @@ -1818,8 +2009,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { add_type_info_dependency(c->info, c->decl, t); MUTEX_GUARD_BLOCK(&c->info->type_info_mutex) { - MapFindResult fr; - auto found = map_try_get(&c->info->type_info_map, t, &fr); + auto found = map_get(&c->info->type_info_map, t); if (found != nullptr) { // Types have already been added return; @@ -1843,7 +2033,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { ti_index = c->info->type_info_types.count; array_add(&c->info->type_info_types, t); } - map_set_internal_from_try_get(&c->checker->info.type_info_map, t, ti_index, fr); + map_set(&c->checker->info.type_info_map, t, ti_index); if (prev) { // NOTE(bill): If a previous one exists already, no need to continue @@ -1961,6 +2151,8 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { break; case Type_Struct: + if (bt->Struct.fields_wait_signal.futex.load() == 0) + return; if (bt->Struct.scope != nullptr) { for (auto const &entry : bt->Struct.scope->elements) { Entity *e = entry.value; @@ -1981,7 +2173,9 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { add_type_info_type_internal(c, bt->Struct.polymorphic_params); for_array(i, bt->Struct.fields) { Entity *f = bt->Struct.fields[i]; - add_type_info_type_internal(c, f->type); + if (f && f->type) { + add_type_info_type_internal(c, f->type); + } } add_comparison_procedures_for_fields(c, bt); break; @@ -2010,16 +2204,6 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { add_type_info_type_internal(c, bt->SimdVector.elem); break; - case Type_RelativePointer: - add_type_info_type_internal(c, bt->RelativePointer.pointer_type); - add_type_info_type_internal(c, bt->RelativePointer.base_integer); - break; - - case Type_RelativeMultiPointer: - add_type_info_type_internal(c, bt->RelativeMultiPointer.pointer_type); - add_type_info_type_internal(c, bt->RelativeMultiPointer.base_integer); - break; - case Type_Matrix: add_type_info_type_internal(c, bt->Matrix.elem); break; @@ -2028,6 +2212,12 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { add_type_info_type_internal(c, bt->SoaPointer.elem); break; + case Type_BitField: + add_type_info_type_internal(c, bt->BitField.backing_type); + for (Entity *f : bt->BitField.fields) { + add_type_info_type_internal(c, f->type); + } + break; case Type_Generic: break; @@ -2104,7 +2294,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { // IMPORTANT NOTE(bill): this must be copied as `map_set` takes a const ref // and effectively assigns the `+1` of the value isize const count = set->count; - if (map_set_if_not_previously_exists(set, ti_index, count)) { + if (map_set_if_not_previously_exists(set, ti_index+1, count)) { // Type already exists; return; } @@ -2211,9 +2401,14 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { Entity *e = entry.value; switch (bt->Struct.soa_kind) { case StructSoa_Dynamic: + add_min_dep_type_info(c, t_type_info_ptr); // append_soa + add_min_dep_type_info(c, t_allocator); /*fallthrough*/ case StructSoa_Slice: + add_min_dep_type_info(c, t_int); + add_min_dep_type_info(c, t_uint); + /*fallthrough*/ case StructSoa_Fixed: add_min_dep_type_info(c, alloc_type_pointer(e->type)); break; @@ -2254,16 +2449,6 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { add_min_dep_type_info(c, bt->SimdVector.elem); break; - case Type_RelativePointer: - add_min_dep_type_info(c, bt->RelativePointer.pointer_type); - add_min_dep_type_info(c, bt->RelativePointer.base_integer); - break; - - case Type_RelativeMultiPointer: - add_min_dep_type_info(c, bt->RelativeMultiPointer.pointer_type); - add_min_dep_type_info(c, bt->RelativeMultiPointer.base_integer); - break; - case Type_Matrix: add_min_dep_type_info(c, bt->Matrix.elem); break; @@ -2272,6 +2457,13 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { add_min_dep_type_info(c, bt->SoaPointer.elem); break; + case Type_BitField: + add_min_dep_type_info(c, bt->BitField.backing_type); + for (Entity *f : bt->BitField.fields) { + add_min_dep_type_info(c, f->type); + } + break; + default: GB_PANIC("Unhandled type: %*.s", LIT(type_strings[bt->kind])); break; @@ -2289,7 +2481,6 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) { 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; @@ -2340,99 +2531,45 @@ gb_internal void force_add_dependency_entity(Checker *c, Scope *scope, String co add_dependency_to_set(c, e); } +gb_internal void collect_testing_procedures_of_package(Checker *c, AstPackage *pkg) { + AstPackage *testing_package = get_core_package(&c->info, str_lit("testing")); + Scope *testing_scope = testing_package->scope; + Entity *test_signature = scope_lookup_current(testing_scope, str_lit("Test_Signature")); + Scope *s = pkg->scope; + for (auto const &entry : s->elements) { + Entity *e = entry.value; + if (e->kind != Entity_Procedure) { + continue; + } -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); - map_init(&c->info.minimum_dependency_type_info_set); - -#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \ - if (condition) { \ - String entities[] = {__VA_ARGS__}; \ - for (isize i = 0; i < gb_count_of(entities); i++) { \ - force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \ - } \ - } \ -} while (0) - - // required runtime entities - FORCE_ADD_RUNTIME_ENTITIES(true, - // Odin types - str_lit("Source_Code_Location"), - str_lit("Context"), - str_lit("Allocator"), - str_lit("Logger"), - - // Odin internal procedures - str_lit("__init_context"), - str_lit("cstring_to_string"), - str_lit("_cleanup_runtime"), - - // Pseudo-CRT required procedures - str_lit("memset"), - str_lit("memcpy"), - str_lit("memmove"), - - // Utility procedures - str_lit("memory_equal"), - str_lit("memory_compare"), - str_lit("memory_compare_zero"), - ); - - FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend, - // Extended data type internal procedures - str_lit("umodti3"), - str_lit("udivti3"), - str_lit("modti3"), - str_lit("divti3"), - str_lit("fixdfti"), - str_lit("fixunsdfti"), - str_lit("fixunsdfdi"), - str_lit("floattidf"), - str_lit("floattidf_unsigned"), - str_lit("truncsfhf2"), - str_lit("truncdfhf2"), - str_lit("gnu_h2f_ieee"), - str_lit("gnu_f2h_ieee"), - str_lit("extendhfsf2"), - - // WASM Specific - str_lit("__ashlti3"), - str_lit("__multi3"), - ); - - FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti, - // Odin types - str_lit("Type_Info"), + if ((e->flags & EntityFlag_Test) == 0) { + continue; + } - // Global variables - str_lit("type_table"), - str_lit("__type_info_of"), - ); + String name = e->token.string; - FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point, - // Global variables - str_lit("args__"), - ); + bool is_tester = true; - FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()), - // NOTE(bill): Only if these exist - str_lit("_tls_index"), - str_lit("_fltused"), - ); + Type *t = base_type(e->type); + GB_ASSERT(t->kind == Type_Proc); + if (are_types_identical(t, base_type(test_signature->type))) { + // Good + } else { + gbString str = type_to_string(t); + error(e->token, "Testing procedures must have a signature type of proc(^testing.T), got %s", str); + gb_string_free(str); + is_tester = false; + } - FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check, - // Bounds checking related procedures - str_lit("bounds_check_error"), - str_lit("matrix_bounds_check_error"), - str_lit("slice_expr_error_hi"), - str_lit("slice_expr_error_lo_hi"), - str_lit("multi_pointer_slice_expr_error"), - ); + if (is_tester) { + add_dependency_to_set(c, e); + array_add(&c->info.testing_procedures, e); + } + } +} +gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *start) { for_array(i, c->info.definitions) { Entity *e = c->info.definitions[i]; if (e->scope == builtin_pkg->scope) { @@ -2490,6 +2627,11 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { is_init = false; } + if ((e->flags & EntityFlag_Disabled) != 0) { + warning(e->token, "This @(init) procedure is disabled; you must call it manually"); + is_init = false; + } + if (is_init) { add_dependency_to_set(c, e); array_add(&c->info.init_procedures, e); @@ -2534,47 +2676,126 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { } } + AstPackage *pkg = c->info.init_package; + collect_testing_procedures_of_package(c, pkg); - Entity *test_signature = scope_lookup_current(testing_scope, str_lit("Test_Signature")); + if (build_context.test_all_packages) { + for (auto const &entry : c->info.packages) { + AstPackage *pkg = entry.value; + collect_testing_procedures_of_package(c, pkg); + } + } + } else if (start != nullptr) { + start->flags |= EntityFlag_Used; + add_dependency_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 - AstPackage *pkg = c->info.init_package; - Scope *s = pkg->scope; + ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap); + map_init(&c->info.minimum_dependency_type_info_set); - for (auto const &entry : s->elements) { - Entity *e = entry.value; - if (e->kind != Entity_Procedure) { - continue; - } +#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \ + if (condition) { \ + String entities[] = {__VA_ARGS__}; \ + for (isize i = 0; i < gb_count_of(entities); i++) { \ + force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \ + } \ + } \ +} while (0) - if ((e->flags & EntityFlag_Test) == 0) { - continue; - } + // required runtime entities + FORCE_ADD_RUNTIME_ENTITIES(true, + // Odin types + str_lit("Source_Code_Location"), + str_lit("Context"), + str_lit("Allocator"), + str_lit("Logger"), - String name = e->token.string; + // Odin internal procedures + str_lit("__init_context"), + // str_lit("cstring_to_string"), + str_lit("_cleanup_runtime"), - bool is_tester = true; + // Pseudo-CRT required procedures + str_lit("memset"), - Type *t = base_type(e->type); - GB_ASSERT(t->kind == Type_Proc); - if (are_types_identical(t, base_type(test_signature->type))) { - // Good - } else { - gbString str = type_to_string(t); - error(e->token, "Testing procedures must have a signature type of proc(^testing.T), got %s", str); - gb_string_free(str); - is_tester = false; - } + // Utility procedures + str_lit("memory_equal"), + str_lit("memory_compare"), + str_lit("memory_compare_zero"), + ); + + // Only required if no CRT is present + FORCE_ADD_RUNTIME_ENTITIES(build_context.no_crt, + str_lit("memcpy"), + str_lit("memmove"), + ); + + FORCE_ADD_RUNTIME_ENTITIES(build_context.metrics.arch == TargetArch_arm32, + str_lit("aeabi_d2h") + ); + + FORCE_ADD_RUNTIME_ENTITIES(is_arch_wasm() && !build_context.tilde_backend, + // // Extended data type internal procedures + // str_lit("umodti3"), + // str_lit("udivti3"), + // str_lit("modti3"), + // str_lit("divti3"), + // str_lit("fixdfti"), + // str_lit("fixunsdfti"), + // str_lit("fixunsdfdi"), + // str_lit("floattidf"), + // str_lit("floattidf_unsigned"), + // str_lit("truncsfhf2"), + // str_lit("truncdfhf2"), + // str_lit("gnu_h2f_ieee"), + // str_lit("gnu_f2h_ieee"), + // str_lit("extendhfsf2"), + + // WASM Specific + str_lit("__ashlti3"), + str_lit("__multi3"), + str_lit("__lshrti3"), + ); + + FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti, + // Odin types + str_lit("Type_Info"), + + // Global variables + str_lit("type_table"), + str_lit("__type_info_of"), + ); + + FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point, + // Global variables + str_lit("args__"), + ); + + FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()), + // NOTE(bill): Only if these exist + str_lit("_tls_index"), + str_lit("_fltused"), + ); + + FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check, + // Bounds checking related procedures + str_lit("bounds_check_error"), + str_lit("matrix_bounds_check_error"), + str_lit("slice_expr_error_hi"), + str_lit("slice_expr_error_lo_hi"), + str_lit("multi_pointer_slice_expr_error"), + ); + + add_dependency_to_set(c, c->info.instrumentation_enter_entity); + add_dependency_to_set(c, c->info.instrumentation_exit_entity); + + generate_minimum_dependency_set_internal(c, start); - if (is_tester) { - add_dependency_to_set(c, e); - array_add(&c->info.testing_procedures, e); - } - } - } else if (start != nullptr) { - start->flags |= EntityFlag_Used; - add_dependency_to_set(c, start); - } #undef FORCE_ADD_RUNTIME_ENTITIES } @@ -2806,6 +3027,11 @@ gb_internal void init_core_type_info(Checker *c) { return; } Entity *type_info_entity = find_core_entity(c, str_lit("Type_Info")); + GB_ASSERT(type_info_entity != nullptr); + if (type_info_entity->type == nullptr) { + check_single_global_entity(c, type_info_entity, type_info_entity->decl_info); + } + GB_ASSERT(type_info_entity->type != nullptr); t_type_info = type_info_entity->type; t_type_info_ptr = alloc_type_pointer(t_type_info); @@ -2847,10 +3073,9 @@ gb_internal void init_core_type_info(Checker *c) { t_type_info_map = find_core_type(c, str_lit("Type_Info_Map")); t_type_info_bit_set = find_core_type(c, str_lit("Type_Info_Bit_Set")); t_type_info_simd_vector = find_core_type(c, str_lit("Type_Info_Simd_Vector")); - t_type_info_relative_pointer = find_core_type(c, str_lit("Type_Info_Relative_Pointer")); - t_type_info_relative_multi_pointer = find_core_type(c, str_lit("Type_Info_Relative_Multi_Pointer")); t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix")); t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer")); + t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field")); t_type_info_named_ptr = alloc_type_pointer(t_type_info_named); t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer); @@ -2876,10 +3101,9 @@ gb_internal void init_core_type_info(Checker *c) { t_type_info_map_ptr = alloc_type_pointer(t_type_info_map); t_type_info_bit_set_ptr = alloc_type_pointer(t_type_info_bit_set); t_type_info_simd_vector_ptr = alloc_type_pointer(t_type_info_simd_vector); - t_type_info_relative_pointer_ptr = alloc_type_pointer(t_type_info_relative_pointer); - t_type_info_relative_multi_pointer_ptr = alloc_type_pointer(t_type_info_relative_multi_pointer); t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix); t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer); + t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field); } gb_internal void init_mem_allocator(Checker *c) { @@ -2907,6 +3131,16 @@ gb_internal void init_core_source_code_location(Checker *c) { t_source_code_location_ptr = alloc_type_pointer(t_source_code_location); } +gb_internal void init_core_load_directory_file(Checker *c) { + if (t_load_directory_file != nullptr) { + return; + } + t_load_directory_file = find_core_type(c, str_lit("Load_Directory_File")); + t_load_directory_file_ptr = alloc_type_pointer(t_load_directory_file); + t_load_directory_file_slice = alloc_type_slice(t_load_directory_file); +} + + gb_internal void init_core_map_type(Checker *c) { if (t_map_info != nullptr) { return; @@ -2971,8 +3205,8 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) { return true; } else if (name == "link_prefix") { if (ev.kind == ExactValue_String) { - String link_prefix = ev.value_string; - if (!is_foreign_name_valid(link_prefix)) { + String link_prefix = string_trim_whitespace(ev.value_string); + if (link_prefix.len != 0 && !is_foreign_name_valid(link_prefix)) { error(elem, "Invalid link prefix: '%.*s'", LIT(link_prefix)); } else { c->foreign_context.link_prefix = link_prefix; @@ -2981,6 +3215,18 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + if (ev.kind == ExactValue_String) { + String link_suffix = string_trim_whitespace(ev.value_string); + if (link_suffix.len != 0 && !is_foreign_name_valid(link_suffix)) { + error(elem, "Invalid link suffix: '%.*s'", LIT(link_suffix)); + } else { + c->foreign_context.link_suffix = link_suffix; + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "private") { EntityVisiblityKind kind = EntityVisiblity_PrivateToPackage; if (ev.kind == ExactValue_Invalid) { @@ -2999,6 +3245,12 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) { } c->foreign_context.visibility_kind = kind; return true; + } else if (name == "require_results") { + if (value != nullptr) { + error(elem, "Expected no value for '%.*s'", LIT(name)); + } + c->foreign_context.require_results = true; + return true; } return false; @@ -3095,6 +3347,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { linkage == "link_once") { ac->linkage = linkage; } else { + ERROR_BLOCK(); error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage)); error_line("\tinternal\n"); error_line("\tstrong\n"); @@ -3267,36 +3520,34 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { if (ev.kind == ExactValue_String) { ac->link_prefix = ev.value_string; - if (!is_foreign_name_valid(ac->link_prefix)) { + if (ac->link_prefix.len != 0 && !is_foreign_name_valid(ac->link_prefix)) { error(elem, "Invalid link prefix: %.*s", LIT(ac->link_prefix)); } } else { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; - } else if (name == "deprecated") { + } else if (name == "link_suffix") { ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_String) { - String msg = ev.value_string; - if (msg.len == 0) { - error(elem, "Deprecation message cannot be an empty string"); - } else { - ac->deprecated_message = msg; + ac->link_suffix = ev.value_string; + if (ac->link_suffix.len != 0 && !is_foreign_name_valid(ac->link_suffix)) { + error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix)); } } else { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; - } else if (name == "warning") { + } else if (name == "deprecated") { ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_String) { String msg = ev.value_string; if (msg.len == 0) { - error(elem, "Warning message cannot be an empty string"); + error(elem, "Deprecation message cannot be an empty string"); } else { - ac->warning_message = msg; + ac->deprecated_message = msg; } } else { error(elem, "Expected a string value for '%.*s'", LIT(name)); @@ -3336,18 +3587,19 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { String mode = ev.value_string; if (mode == "none") { ac->optimization_mode = ProcedureOptimizationMode_None; + } else if (mode == "favor_size") { + ac->optimization_mode = ProcedureOptimizationMode_FavorSize; } else if (mode == "minimal") { - ac->optimization_mode = ProcedureOptimizationMode_Minimal; + error(elem, "Invalid optimization_mode 'minimal' for '%.*s', mode has been removed due to confusion, but 'none' has the same behaviour", LIT(name)); } else if (mode == "size") { - ac->optimization_mode = ProcedureOptimizationMode_Size; + error(elem, "Invalid optimization_mode 'size' for '%.*s', mode has been removed due to confusion, but 'favor_size' has the same behaviour", LIT(name)); } else if (mode == "speed") { - ac->optimization_mode = ProcedureOptimizationMode_Speed; + error(elem, "Invalid optimization_mode 'speed' for '%.*s', mode has been removed due to confusion, but 'favor_size' has the same behaviour", LIT(name)); } else { + ERROR_BLOCK(); error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name)); error_line("\tnone\n"); - error_line("\tminimal\n"); - error_line("\tsize\n"); - error_line("\tspeed\n"); + error_line("\tfavor_size\n"); } } else { error(elem, "Expected a string for '%.*s'", LIT(name)); @@ -3405,6 +3657,39 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "entry_point_only") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } + ac->entry_point_only = true; + return true; + } else if (name == "no_instrumentation") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_Invalid) { + ac->no_instrumentation = Instrumentation_Disabled; + } else if (ev.kind == ExactValue_Bool) { + if (ev.value_bool) { + ac->no_instrumentation = Instrumentation_Disabled; + } else { + ac->no_instrumentation = Instrumentation_Enabled; + } + } else { + error(value, "Expected either a boolean or no parameter for '%.*s'", LIT(name)); + return false; + } + return true; + } else if (name == "instrumentation_enter") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } + ac->instrumentation_enter = true; + return true; + } else if (name == "instrumentation_exit") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } + ac->instrumentation_exit = true; + return true; } return false; } @@ -3422,6 +3707,12 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { } ac->is_static = true; return true; + } else if (name == "rodata") { + if (value != nullptr) { + error(elem, "'rodata' does not have any parameters"); + } + ac->rodata = true; + return true; } else if (name == "thread_local") { ExactValue ev = check_decl_attribute_value(c, value); if (ac->init_expr_list_count > 0) { @@ -3440,6 +3731,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { model == "localexec") { ac->thread_local_model = model; } else { + ERROR_BLOCK(); error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model)); error_line("\tdefault\n"); error_line("\tlocaldynamic\n"); @@ -3490,6 +3782,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { linkage == "link_once") { ac->linkage = linkage; } else { + ERROR_BLOCK(); error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage)); error_line("\tinternal\n"); error_line("\tstrong\n"); @@ -3512,13 +3805,24 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_String) { ac->link_prefix = ev.value_string; - if (!is_foreign_name_valid(ac->link_prefix)) { + if (ac->link_prefix.len != 0 && !is_foreign_name_valid(ac->link_prefix)) { error(elem, "Invalid link prefix: %.*s", LIT(ac->link_prefix)); } } else { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "link_suffix") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind == ExactValue_String) { + ac->link_suffix = ev.value_string; + if (ac->link_suffix.len != 0 && !is_foreign_name_valid(ac->link_suffix)) { + error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix)); + } + } else { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } + return true; } else if (name == "link_section") { ExactValue ev = check_decl_attribute_value(c, value); if (ev.kind == ExactValue_String) { @@ -3544,6 +3848,16 @@ gb_internal DECL_ATTRIBUTE_PROC(const_decl_attribute) { } else if (name == "private") { // NOTE(bill): Handled elsewhere `check_collect_value_decl` return true; + } else if (name == "static" || + name == "thread_local" || + name == "require" || + name == "linkage" || + name == "link_name" || + name == "link_prefix" || + name == "link_suffix" || + false) { + error(elem, "@(%.*s) is not supported for compile time constant value declarations", LIT(name)); + return true; } return false; } @@ -3583,8 +3897,10 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at if (attributes.count == 0) return; String original_link_prefix = {}; + String original_link_suffix = {}; if (ac) { original_link_prefix = ac->link_prefix; + original_link_suffix = ac->link_suffix; } StringSet set = {}; @@ -3643,9 +3959,11 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at } if (!proc(c, elem, name, value, ac)) { - if (!build_context.ignore_unknown_attributes) { + if (!build_context.ignore_unknown_attributes && + !string_set_exists(&build_context.custom_attributes, name)) { + ERROR_BLOCK(); error(elem, "Unknown attribute element name '%.*s'", LIT(name)); - error_line("\tDid you forget to use build flag '-ignore-unknown-attributes'?\n"); + error_line("\tDid you forget to use the build flag '-ignore-unknown-attributes' or '-custom-attribute:%.*s'?\n", LIT(name)); } } } @@ -3658,6 +3976,12 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at ac->link_prefix.len = 0; } } + if (ac->link_suffix.text == original_link_suffix.text) { + if (ac->link_name.len > 0) { + ac->link_suffix.text = nullptr; + ac->link_suffix.len = 0; + } + } } } @@ -3713,6 +4037,8 @@ gb_internal bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_ gb_string_free(str); return false; } else if (is_global) { + ERROR_BLOCK(); + Ast *n = vd->values[rhs-1]; error(n, "Expected %td expressions on the right hand side, got %td", lhs, rhs); error_line("Note: Global declarations do not allow for multi-valued expressions"); @@ -3764,6 +4090,7 @@ gb_internal void check_builtin_attributes(CheckerContext *ctx, Entity *e, Array< case Entity_ProcGroup: case Entity_Procedure: case Entity_TypeName: + case Entity_Constant: // Okay break; default: @@ -3832,6 +4159,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { bool is_test = false; bool is_init = false; bool is_fini = false; + bool is_priv = false; for_array(i, vd->attributes) { Ast *attr = vd->attributes[i]; @@ -3876,6 +4204,8 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { } if (!success) { error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name)); + } else { + is_priv = true; } @@ -3897,6 +4227,11 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { } } + if (is_priv && is_test) { + error(decl, "Attribute 'private' is not allowed on a test case"); + return; + } + if (entity_visibility_kind == EntityVisiblity_Public && (c->scope->flags&ScopeFlag_File) && c->scope->file) { @@ -3931,6 +4266,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { Entity *e = alloc_entity_variable(c->scope, name->Ident.token, nullptr); e->identifier = name; e->file = c->file; + e->Variable.is_global = true; if (entity_visibility_kind != EntityVisiblity_Public) { e->flags |= EntityFlag_NotExported; @@ -3948,6 +4284,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { e->Variable.foreign_library_ident = fl; e->Variable.link_prefix = c->foreign_context.link_prefix; + e->Variable.link_suffix = c->foreign_context.link_suffix; } Ast *init_expr = value; @@ -4002,6 +4339,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { } ast_node(pl, ProcLit, init); e = alloc_entity_procedure(d->scope, token, nullptr, pl->tags); + d->foreign_require_results = c->foreign_context.require_results; if (fl != nullptr) { GB_ASSERT(fl->kind == Ast_Ident); e->Procedure.foreign_library_ident = fl; @@ -4014,15 +4352,15 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (c->foreign_context.default_cc > 0) { cc = c->foreign_context.default_cc; } else if (is_arch_wasm()) { - begin_error_block(); + ERROR_BLOCK(); error(init, "For wasm related targets, it is required that you either define the" " @(default_calling_convention=<string>) on the foreign block or" " explicitly assign it on the procedure signature"); error_line("\tSuggestion: when dealing with normal Odin code (e.g. js_wasm32), use \"contextless\"; when dealing with Emscripten like code, use \"c\"\n"); - end_error_block(); } } e->Procedure.link_prefix = c->foreign_context.link_prefix; + e->Procedure.link_suffix = c->foreign_context.link_suffix; GB_ASSERT(cc != ProcCC_Invalid); pl->type->ProcType.calling_convention = cc; @@ -4066,8 +4404,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (e->kind != Entity_Procedure) { if (fl != nullptr) { - begin_error_block(); - defer (end_error_block()); + ERROR_BLOCK(); AstKind kind = init->kind; error(name, "Only procedures and variables are allowed to be in a foreign block, got %.*s", LIT(ast_strings[kind])); @@ -4128,17 +4465,22 @@ gb_internal bool correct_single_type_alias(CheckerContext *c, Entity *e) { gb_internal bool correct_type_alias_in_scope_backwards(CheckerContext *c, Scope *s) { bool correction = false; - u32 n = s->elements.count; - for (u32 i = n-1; i < n; i--) { - correction |= correct_single_type_alias(c, s->elements.entries[i].value); + for (u32 n = s->elements.count, i = n-1; i < n; i--) { + auto const &entry = s->elements.entries[i]; + Entity *e = entry.value; + if (entry.hash && e != nullptr) { + correction |= correct_single_type_alias(c, e); + } } return correction; } gb_internal bool correct_type_alias_in_scope_forwards(CheckerContext *c, Scope *s) { bool correction = false; - u32 n = s->elements.count; - for (isize i = 0; i < n; i++) { - correction |= correct_single_type_alias(c, s->elements.entries[i].value); + for (auto const &entry : s->elements) { + Entity *e = entry.value; + if (e != nullptr) { + correction |= correct_single_type_alias(c, entry.value); + } } return correction; } @@ -4221,7 +4563,9 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n case_end; case_ast_node(fb, ForeignBlockDecl, decl); - check_add_foreign_block_decl(c, decl); + if (curr_file != nullptr) { + array_add(&curr_file->delayed_decls_queues[AstDelayQueue_ForeignBlock], decl); + } case_end; default: @@ -4237,6 +4581,14 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n // NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something // declared after this stmt in source if (curr_file == nullptr) { + // For 'foreign' block statements that are not in file scope. + for_array(decl_index, nodes) { + Ast *decl = nodes[decl_index]; + if (decl->kind == Ast_ForeignBlockDecl) { + check_add_foreign_block_decl(c, decl); + } + } + for_array(decl_index, nodes) { Ast *decl = nodes[decl_index]; if (decl->kind == Ast_WhenStmt) { @@ -4286,6 +4638,8 @@ 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; + // NOTE(bill): This must be single threaded // Don't bother trying for_array(i, c->info.entities) { @@ -4297,10 +4651,16 @@ gb_internal void check_all_global_entities(Checker *c) { DeclInfo *d = e->decl_info; check_single_global_entity(c, e, d); if (e->type != nullptr && is_type_typed(e->type)) { + for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) { + complete_soa_type(c, t, false); + } + (void)type_size_of(e->type); (void)type_align_of(e->type); } } + + in_single_threaded_checker_stage = false; } @@ -4387,7 +4747,7 @@ gb_internal void add_import_dependency_node(Checker *c, Ast *decl, PtrMap<AstPac if (found == nullptr) { Token token = ast_token(decl); error(token, "Unable to find package: %.*s", LIT(path)); - gb_exit(1); + exit_with_errors(); } AstPackage *pkg = *found; GB_ASSERT(pkg->scope != nullptr); @@ -4508,10 +4868,10 @@ gb_internal Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start continue; } - if (pkg->kind == Package_Runtime) { - // NOTE(bill): Allow cyclic imports within the runtime package for the time being - continue; - } + // if (pkg->kind == Package_Runtime) { + // // NOTE(bill): Allow cyclic imports within the runtime package for the time being + // continue; + // } ImportPathItem item = {pkg, decl}; if (pkg == end) { @@ -4615,12 +4975,18 @@ gb_internal void check_add_import_decl(CheckerContext *ctx, Ast *decl) { } - if (import_name.len == 0) { + if (is_blank_ident(import_name) && !is_blank_ident(id->import_name.string)) { String invalid_name = id->fullpath; invalid_name = get_invalid_import_name(invalid_name); - error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import <new_name> \"%.*s\" ", LIT(invalid_name), LIT(invalid_name)); - error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); + ERROR_BLOCK(); + + if (id->import_name.string.len > 0) { + error(token, "Import name '%.*s' cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); + } else { + error(id->token, "Import name '%.*s' is not a valid identifier", LIT(invalid_name)); + error_line("\tSuggestion: Rename the directory or explicitly set an import name like this 'import <new_name> %.*s'", LIT(id->relpath.string)); + } } else { GB_ASSERT(id->import_name.pos.line != 0); id->import_name.string = import_name; @@ -4672,6 +5038,114 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) { return false; } +gb_internal void check_foreign_import_fullpaths(Checker *c) { + CheckerContext ctx = make_checker_context(c); + + UntypedExprInfoMap untyped = {}; + defer (map_destroy(&untyped)); + + for (Entity *e = nullptr; mpsc_dequeue(&c->info.foreign_imports_to_check_fullpaths, &e); /**/) { + GB_ASSERT(e != nullptr); + GB_ASSERT(e->kind == Entity_LibraryName); + Ast *decl = e->LibraryName.decl; + ast_node(fl, ForeignImportDecl, decl); + + AstFile *f = decl->file(); + + reset_checker_context(&ctx, f, &untyped); + ctx.collect_delayed_decls = false; + + GB_ASSERT(ctx.scope == e->scope); + + if (fl->fullpaths.count == 0) { + String base_dir = dir_from_path(decl->file()->fullpath); + + auto fullpaths = array_make<String>(permanent_allocator(), 0, fl->filepaths.count); + + for (Ast *fp_node : fl->filepaths) { + Operand op = {}; + check_expr(&ctx, &op, fp_node); + if (op.mode != Addressing_Constant && op.value.kind != ExactValue_String) { + gbString s = expr_to_string(op.expr); + error(fp_node, "Expected a constant string value, got '%s'", s); + gb_string_free(s); + continue; + } + if (!is_type_string(op.type)) { + gbString s = type_to_string(op.type); + error(fp_node, "Expected a constant string value, got value of type '%s'", s); + gb_string_free(s); + continue; + } + + String file_str = op.value.value_string; + file_str = string_trim_whitespace(file_str); + String fullpath = file_str; + if (!is_arch_wasm() || string_ends_with(file_str, str_lit(".o"))) { + String foreign_path = {}; + bool ok = determine_path_from_string(nullptr, decl, base_dir, file_str, &foreign_path, /*use error not syntax_error*/true); + if (ok) { + fullpath = foreign_path; + } + } + array_add(&fullpaths, fullpath); + } + fl->fullpaths = slice_from_array(fullpaths); + } + + for (String const &path : fl->fullpaths) { + String ext = path_extension(path); + if (str_eq_ignore_case(ext, ".c") || + str_eq_ignore_case(ext, ".cpp") || + str_eq_ignore_case(ext, ".cxx") || + str_eq_ignore_case(ext, ".h") || + str_eq_ignore_case(ext, ".hpp") || + str_eq_ignore_case(ext, ".hxx") || + false + ) { + error(fl->token, "With 'foreign import', you cannot import a %.*s file/directory, you must precompile the library and link against that", LIT(ext)); + break; + } + } + + add_untyped_expressions(ctx.info, &untyped); + + e->LibraryName.paths = fl->fullpaths; + } + + for (Entity *e = nullptr; mpsc_dequeue(&c->info.foreign_decls_to_check, &e); /**/) { + GB_ASSERT(e != nullptr); + if (e->kind != Entity_Procedure) { + continue; + } + if (!is_arch_wasm()) { + continue; + } + Entity *foreign_library = e->Procedure.foreign_library; + GB_ASSERT(foreign_library != nullptr); + + String name = e->Procedure.link_name; + + String module_name = str_lit("env"); + GB_ASSERT (foreign_library->kind == Entity_LibraryName); + if (foreign_library->LibraryName.paths.count != 1) { + error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td", + LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count); + } + + if (foreign_library->LibraryName.paths.count >= 1) { + module_name = foreign_library->LibraryName.paths[0]; + } + + if (!string_ends_with(module_name, str_lit(".o"))) { + name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name); + } + e->Procedure.link_name = name; + + check_foreign_procedure(&ctx, e, e->decl_info); + } +} + gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { if (decl->state_flags & StateFlag_BeenHandled) return; decl->state_flags |= StateFlag_BeenHandled; @@ -4681,43 +5155,26 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { Scope *parent_scope = ctx->scope; GB_ASSERT(parent_scope->flags&ScopeFlag_File); - GB_ASSERT(fl->fullpaths.count > 0); - String fullpath = fl->fullpaths[0]; - String library_name = path_to_entity_name(fl->library_name.string, fullpath); - if (is_blank_ident(library_name)) { - error(fl->token, "File name, %.*s, cannot be as a library name as it is not a valid identifier", LIT(fl->library_name.string)); + String library_name = fl->library_name.string; + if (library_name.len == 0 && fl->fullpaths.count != 0) { + String fullpath = fl->fullpaths[0]; + library_name = path_to_entity_name(fl->library_name.string, fullpath); + } + if (library_name.len == 0 || is_blank_ident(library_name)) { + error(fl->token, "File name, '%.*s', cannot be as a library name as it is not a valid identifier", LIT(library_name)); return; } - // if (fl->collection_name != "system") { - // char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1); - // defer (gb_free(heap_allocator(), c_str)); - // gb_memmove(c_str, fullpath.text, fullpath.len); - // c_str[fullpath.len] = '\0'; - - // gbFile f = {}; - // gbFileError file_err = gb_file_open(&f, c_str); - // defer (gb_file_close(&f)); - - // switch (file_err) { - // case gbFileError_Invalid: - // error(decl, "Invalid file or cannot be found ('%.*s')", LIT(fullpath)); - // return; - // case gbFileError_NotExists: - // error(decl, "File cannot be found ('%.*s')", LIT(fullpath)); - // return; - // } - // } GB_ASSERT(fl->library_name.pos.line != 0); fl->library_name.string = library_name; Entity *e = alloc_entity_library_name(parent_scope, fl->library_name, t_invalid, fl->fullpaths, library_name); + e->LibraryName.decl = decl; add_entity_flags_from_file(ctx, e, parent_scope); add_entity(ctx, parent_scope, nullptr, e); - AttributeContext ac = {}; check_decl_attributes(ctx, fl->attributes, foreign_import_decl_attribute, &ac); if (ac.require_declaration) { @@ -4732,12 +5189,8 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { e->LibraryName.extra_linker_flags = extra_linker_flags; } - if (has_asm_extension(fullpath)) { - if (build_context.metrics.arch != TargetArch_amd64) { - error(decl, "Assembly files are not yet supported on this platform: %.*s_%.*s", - LIT(target_os_names[build_context.metrics.os]), LIT(target_arch_names[build_context.metrics.arch])); - } - } + mpsc_enqueue(&ctx->info->foreign_imports_to_check_fullpaths, e); + } // Returns true if a new package is present @@ -4844,9 +5297,9 @@ gb_internal bool collect_file_decl(CheckerContext *ctx, Ast *decl) { case_end; case_ast_node(fb, ForeignBlockDecl, decl); - if (check_add_foreign_block_decl(ctx, decl)) { - return true; - } + GB_ASSERT(ctx->collect_delayed_decls); + decl->state_flags |= StateFlag_BeenHandled; + array_add(&curr_file->delayed_decls_queues[AstDelayQueue_ForeignBlock], decl); case_end; case_ast_node(ws, WhenStmt, decl); @@ -4911,7 +5364,7 @@ gb_internal void check_create_file_scopes(Checker *c) { for_array(i, c->parser->packages) { AstPackage *pkg = c->parser->packages[i]; - gb_sort_array(pkg->files.data, pkg->files.count, sort_file_by_name); + array_sort(pkg->files, sort_file_by_name); isize total_pkg_decl_count = 0; for_array(j, pkg->files) { @@ -5129,8 +5582,6 @@ gb_internal void check_import_entities(Checker *c) { for_array(i, pkg->files) { AstFile *f = pkg->files[i]; reset_checker_context(&ctx, f, &untyped); - ctx.collect_delayed_decls = false; - correct_type_aliases_in_scope(&ctx, pkg->scope); } @@ -5138,6 +5589,17 @@ gb_internal void check_import_entities(Checker *c) { AstFile *f = pkg->files[i]; reset_checker_context(&ctx, f, &untyped); + ctx.collect_delayed_decls = true; + for (Ast *decl : f->delayed_decls_queues[AstDelayQueue_ForeignBlock]) { + check_add_foreign_block_decl(&ctx, decl); + } + array_clear(&f->delayed_decls_queues[AstDelayQueue_ForeignBlock]); + } + + for_array(i, pkg->files) { + AstFile *f = pkg->files[i]; + reset_checker_context(&ctx, f, &untyped); + for (Ast *expr : f->delayed_decls_queues[AstDelayQueue_Expr]) { Operand o = {}; check_expr(&ctx, &o, expr); @@ -5304,6 +5766,59 @@ gb_internal void calculate_global_init_order(Checker *c) { } } +gb_internal void check_procedure_later_from_entity(Checker *c, Entity *e, char const *from_msg) { + if (e == nullptr || e->kind != Entity_Procedure) { + return; + } + if (e->Procedure.is_foreign) { + return; + } + if ((e->flags & EntityFlag_ProcBodyChecked) != 0) { + return; + } + if ((e->flags & EntityFlag_Overridden) != 0) { + // NOTE (zen3ger) Delay checking of a proc alias until the underlying proc is checked. + GB_ASSERT(e->aliased_of != nullptr); + GB_ASSERT(e->aliased_of->kind == Entity_Procedure); + if ((e->aliased_of->flags & EntityFlag_ProcBodyChecked) != 0) { + e->flags |= EntityFlag_ProcBodyChecked; + return; + } + // NOTE (zen3ger) A proc alias *does not* have a body and tags! + check_procedure_later(c, e->file, e->token, e->decl_info, e->type, nullptr, 0); + return; + } + Type *type = base_type(e->type); + if (type == t_invalid) { + return; + } + GB_ASSERT_MSG(type->kind == Type_Proc, "%s", type_to_string(e->type)); + + if (is_type_polymorphic(type) && !type->Proc.is_poly_specialized) { + return; + } + + GB_ASSERT(e->decl_info != nullptr); + + ProcInfo *pi = gb_alloc_item(permanent_allocator(), ProcInfo); + pi->file = e->file; + pi->token = e->token; + pi->decl = e->decl_info; + pi->type = e->type; + + Ast *pl = e->decl_info->proc_lit; + GB_ASSERT(pl != nullptr); + pi->body = pl->ProcLit.body; + pi->tags = pl->ProcLit.tags; + if (pi->body == nullptr) { + return; + } + if (from_msg != nullptr) { + debugf("CHECK PROCEDURE LATER [FROM %s]! %.*s :: %s {...}\n", from_msg, LIT(e->token.string), type_to_string(e->type)); + } + check_procedure_later(c, pi); +} + gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped) { if (pi == nullptr) { @@ -5410,6 +5925,15 @@ 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) { + if (dep && dep->kind == Entity_Procedure && + (dep->flags & EntityFlag_ProcBodyChecked) == 0) { + check_procedure_later_from_entity(c, dep, NULL); + } + } + rw_mutex_shared_unlock(&ctx.decl->deps_mutex); + return true; } @@ -5432,30 +5956,7 @@ gb_internal void check_unchecked_bodies(Checker *c) { global_procedure_body_in_worker_queue = false; for (Entity *e : c->info.minimum_dependency_set) { - if (e == nullptr || e->kind != Entity_Procedure) { - continue; - } - if (e->Procedure.is_foreign) { - continue; - } - if ((e->flags & EntityFlag_ProcBodyChecked) == 0) { - GB_ASSERT(e->decl_info != nullptr); - - ProcInfo *pi = gb_alloc_item(permanent_allocator(), ProcInfo); - pi->file = e->file; - pi->token = e->token; - pi->decl = e->decl_info; - pi->type = e->type; - - Ast *pl = e->decl_info->proc_lit; - GB_ASSERT(pl != nullptr); - pi->body = pl->ProcLit.body; - pi->tags = pl->ProcLit.tags; - if (pi->body == nullptr) { - continue; - } - check_procedure_later(c, pi); - } + check_procedure_later_from_entity(c, e, "check_unchecked_bodies"); } if (!global_procedure_body_in_worker_queue) { @@ -5497,35 +5998,27 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) { } } -gb_internal void check_test_procedures(Checker *c) { - if (build_context.test_names.entries.count == 0) { - return; - } +gb_internal GB_COMPARE_PROC(init_procedures_cmp); +gb_internal GB_COMPARE_PROC(fini_procedures_cmp); - AstPackage *pkg = c->info.init_package; - Scope *s = pkg->scope; +gb_internal void remove_neighbouring_duplicate_entires_from_sorted_array(Array<Entity *> *array) { + Entity *prev = nullptr; - for (String const &name : build_context.test_names) { - Entity *e = scope_lookup(s, name); - if (e == nullptr) { - Token tok = {}; - if (pkg->files.count != 0) { - tok = pkg->files[0]->tokens[0]; - } - error(tok, "Unable to find the test '%.*s' in 'package %.*s' ", LIT(name), LIT(pkg->name)); - } - } - - for (isize i = 0; i < c->info.testing_procedures.count; /**/) { - Entity *e = c->info.testing_procedures[i]; - String name = e->token.string; - if (!string_set_exists(&build_context.test_names, name)) { - array_ordered_remove(&c->info.testing_procedures, i); + for (isize i = 0; i < array->count; /**/) { + Entity *curr = array->data[i]; + if (prev == curr) { + array_ordered_remove(array, i); } else { + prev = curr; i += 1; } } +} + +gb_internal void check_test_procedures(Checker *c) { + array_sort(c->info.testing_procedures, init_procedures_cmp); + remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.testing_procedures); } @@ -5690,6 +6183,11 @@ gb_internal void check_deferred_procedures(Checker *c) { case DeferredProcedure_in_out_by_ptr: attribute = "deferred_in_out_by_ptr"; break; } + if (src == dst) { + error(src->token, "'%.*s' cannot be used as its own %s", LIT(dst->token.string), attribute); + continue; + } + if (is_type_polymorphic(src->type) || is_type_polymorphic(dst->type)) { error(src->token, "'%s' cannot be used with a polymorphic procedure", attribute); continue; @@ -5737,7 +6235,7 @@ gb_internal void check_deferred_procedures(Checker *c) { } if ((src_params == nullptr && dst_params != nullptr) || (src_params != nullptr && dst_params == nullptr)) { - error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string)); + error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(dst->token.string), LIT(src->token.string)); continue; } @@ -5750,8 +6248,8 @@ gb_internal void check_deferred_procedures(Checker *c) { gbString s = type_to_string(src_params); gbString d = type_to_string(dst_params); error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s':\n\t(%s) =/= (%s)", - LIT(src->token.string), LIT(dst->token.string), - s, d + LIT(dst->token.string), LIT(src->token.string), + d, s ); gb_string_free(d); gb_string_free(s); @@ -5767,7 +6265,7 @@ gb_internal void check_deferred_procedures(Checker *c) { } if ((src_results == nullptr && dst_params != nullptr) || (src_results != nullptr && dst_params == nullptr)) { - error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string)); + error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(dst->token.string), LIT(src->token.string)); continue; } @@ -5780,8 +6278,8 @@ gb_internal void check_deferred_procedures(Checker *c) { gbString s = type_to_string(src_results); gbString d = type_to_string(dst_params); error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)", - LIT(src->token.string), LIT(dst->token.string), - s, d + LIT(dst->token.string), LIT(src->token.string), + d, s ); gb_string_free(d); gb_string_free(s); @@ -5833,8 +6331,8 @@ gb_internal void check_deferred_procedures(Checker *c) { gbString s = type_to_string(tsrc); gbString d = type_to_string(dst_params); error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)", - LIT(src->token.string), LIT(dst->token.string), - s, d + LIT(dst->token.string), LIT(src->token.string), + d, s ); gb_string_free(d); gb_string_free(s); @@ -5873,11 +6371,14 @@ gb_internal void check_unique_package_names(Checker *c) { continue; } + + begin_error_block(); error(curr, "Duplicate declaration of 'package %.*s'", LIT(name)); error_line("\tA package name must be unique\n" "\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(prev, "found at previous location"); + error_line("%s found at previous location\n", token_pos_to_string(ast_token(prev).pos)); + end_error_block(); } } @@ -5898,6 +6399,9 @@ gb_internal void check_add_definitions_from_queues(Checker *c) { } gb_internal void check_merge_queues_into_arrays(Checker *c) { + for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) { + complete_soa_type(c, t, false); + } check_add_entities_from_queues(c); check_add_definitions_from_queues(c); } @@ -5943,10 +6447,14 @@ gb_internal GB_COMPARE_PROC(fini_procedures_cmp) { return init_procedures_cmp(b, a); } - gb_internal void check_sort_init_and_fini_procedures(Checker *c) { - gb_sort_array(c->info.init_procedures.data, c->info.init_procedures.count, init_procedures_cmp); - gb_sort_array(c->info.fini_procedures.data, c->info.fini_procedures.count, fini_procedures_cmp); + array_sort(c->info.init_procedures, init_procedures_cmp); + array_sort(c->info.fini_procedures, fini_procedures_cmp); + + // NOTE(bill): remove possible duplicates from the init/fini lists + // NOTE(bill): because the arrays are sorted, you only need to check the previous element + remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.init_procedures); + remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.fini_procedures); } gb_internal void add_type_info_for_type_definitions(Checker *c) { @@ -6041,18 +6549,22 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("check procedure bodies"); check_procedure_bodies(c); + TIME_SECTION("check foreign import fullpaths"); + check_foreign_import_fullpaths(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 = build_context.vet_flags; - if (f->vet_flags_set) { - vet_flags = f->vet_flags; - } + 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("add basic type information"); // Add "Basic" type information @@ -6091,9 +6603,6 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("calculate global init order"); calculate_global_init_order(c); - TIME_SECTION("check test procedures"); - check_test_procedures(c); - TIME_SECTION("add type info for type definitions"); add_type_info_for_type_definitions(c); check_merge_queues_into_arrays(c); @@ -6107,8 +6616,21 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("check bodies have all been checked"); check_unchecked_bodies(c); + TIME_SECTION("check #soa types"); + check_merge_queues_into_arrays(c); + thread_pool_wait(); + + TIME_SECTION("update minimum dependency set"); + generate_minimum_dependency_set_internal(c, c->info.entry_point); + // NOTE(laytan): has to be ran after generate_minimum_dependency_set, + // because that collects the test procedures. + TIME_SECTION("check test procedures"); + check_test_procedures(c); + + check_merge_queues_into_arrays(c); + thread_pool_wait(); TIME_SECTION("check entry point"); if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) { @@ -6130,6 +6652,8 @@ gb_internal void check_parsed_files(Checker *c) { error(token, "Undefined entry point procedure 'main'"); } + } else if (build_context.build_mode == BuildMode_DynamicLibrary && build_context.no_entry_point) { + c->info.entry_point = nullptr; } thread_pool_wait(); @@ -6150,6 +6674,17 @@ gb_internal void check_parsed_files(Checker *c) { GB_ASSERT(c->info.entity_queue.count.load(std::memory_order_relaxed) == 0); GB_ASSERT(c->info.definition_queue.count.load(std::memory_order_relaxed) == 0); + TIME_SECTION("check instrumentation calls"); + { + if ((c->info.instrumentation_enter_entity != nullptr) ^ + (c->info.instrumentation_exit_entity != nullptr)) { + Entity *e = c->info.instrumentation_enter_entity; + if (!e) e = c->info.instrumentation_exit_entity; + error(e->token, "Both @(instrumentation_enter) and @(instrumentation_exit) must be defined"); + } + } + + TIME_SECTION("add untyped expression values"); // Add untyped expression values for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) { |