diff options
| author | Jeroen van Rijn <Kelimion@users.noreply.github.com> | 2025-06-27 01:20:37 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-27 01:20:37 +0200 |
| commit | 2e83e221419b480160dbf11dd09edf8b5fa97f32 (patch) | |
| tree | bc5e666006bf43931bc793ac921061b5630db4d4 /src/checker.cpp | |
| parent | c51df72f1a8a21dd0064086dfc1186a3dd79e2ef (diff) | |
| parent | f72b2b153057e1d629c85af2ea7c54f7928198d5 (diff) | |
Merge branch 'master' into args-leak
Diffstat (limited to 'src/checker.cpp')
| -rw-r--r-- | src/checker.cpp | 408 |
1 files changed, 341 insertions, 67 deletions
diff --git a/src/checker.cpp b/src/checker.cpp index 5d3263789..4ebabe004 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3,7 +3,10 @@ #include "entity.cpp" #include "types.cpp" -String get_final_microarchitecture(); + +gb_internal u64 type_hash_canonical_type(Type *type); + +gb_internal 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); @@ -170,7 +173,7 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { d->parent = parent; d->scope = scope; ptr_set_init(&d->deps, 0); - ptr_set_init(&d->type_info_deps, 0); + type_set_init(&d->type_info_deps, 0); d->labels.allocator = heap_allocator(); d->variadic_reuses.allocator = heap_allocator(); d->variadic_reuse_max_bytes = 0; @@ -355,6 +358,10 @@ gb_internal void check_open_scope(CheckerContext *c, Ast *node) { scope->flags |= ScopeFlag_Type; break; } + if (c->decl && c->decl->proc_lit) { + // Number the scopes within a procedure body depth-first + scope->index = c->decl->scope_index++; + } c->scope = scope; c->state_flags |= StateFlag_bounds_check; } @@ -721,12 +728,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 { @@ -744,14 +756,20 @@ 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? 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); + bool is_ref = false; + if((e->flags & EntityFlag_ForValue) != 0) { + is_ref = type_deref(e->Variable.for_loop_parent_type) != NULL; + } + if(!is_ref) { + 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); + } } } } @@ -819,11 +837,17 @@ gb_internal void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) { rw_mutex_unlock(&d->deps_mutex); } gb_internal void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type) { - if (d == nullptr) { + if (d == nullptr || type == nullptr) { return; } + if (type->kind == Type_Named) { + Entity *e = type->Named.type_name; + if (e->TypeName.is_type_alias) { + type = type->Named.base; + } + } rw_mutex_lock(&d->type_info_deps_mutex); - ptr_set_add(&d->type_info_deps, type); + type_set_add(&d->type_info_deps, type); rw_mutex_unlock(&d->type_info_deps_mutex); } @@ -1054,11 +1078,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] = { @@ -1078,7 +1121,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]); } @@ -1130,6 +1173,7 @@ gb_internal void init_universal(void) { GlobalEnumValue values[Subtarget_COUNT] = { {"Default", Subtarget_Default}, {"iOS", Subtarget_iOS}, + {"Android", Subtarget_Android}, }; auto fields = add_global_enum_type(str_lit("Odin_Platform_Subtarget_Type"), values, gb_count_of(values)); @@ -1331,10 +1375,12 @@ gb_internal void init_universal(void) { 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); } } @@ -1352,8 +1398,11 @@ gb_internal void init_checker_info(CheckerInfo *i) { string_map_init(&i->foreigns); // map_init(&i->gen_procs); map_init(&i->gen_types); - array_init(&i->type_info_types, a); - map_init(&i->type_info_map); + + 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); @@ -1364,6 +1413,10 @@ 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()); @@ -1386,8 +1439,10 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { string_map_destroy(&i->foreigns); // map_destroy(&i->gen_procs); map_destroy(&i->gen_types); - array_free(&i->type_info_types); - map_destroy(&i->type_info_map); + + type_set_destroy(&i->min_dep_type_info_set); + map_destroy(&i->min_dep_type_info_index_map); + string_map_destroy(&i->files); string_map_destroy(&i->packages); array_free(&i->variable_init_order); @@ -1472,6 +1527,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); @@ -1621,41 +1678,36 @@ gb_internal void check_remove_expr_info(CheckerContext *c, Ast *e) { } } - -gb_internal isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) { - type = default_type(type); - if (type == t_llvm_bool) { - type = t_bool; - } - - mutex_lock(&info->type_info_mutex); +gb_internal isize type_info_index(CheckerInfo *info, TypeInfoPair pair, bool error_on_failure) { + mutex_lock(&info->minimum_dependency_type_info_mutex); isize entry_index = -1; - isize *found_entry_index = map_get(&info->type_info_map, type); + u64 hash = pair.hash; + isize *found_entry_index = map_get(&info->min_dep_type_info_index_map, hash); if (found_entry_index) { entry_index = *found_entry_index; } - if (entry_index < 0) { - // NOTE(bill): Do manual linear search - for (auto const &e : info->type_info_map) { - if (are_types_identical_unique_tuples(e.key, type)) { - entry_index = e.value; - // NOTE(bill): Add it to the search map - map_set(&info->type_info_map, type, entry_index); - break; - } - } - } - - mutex_unlock(&info->type_info_mutex); + mutex_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(type)); + compiler_error("Type_Info for '%s' could not be found", type_to_string(pair.type)); } return entry_index; } +gb_internal isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) { + type = default_type(type); + if (type == t_llvm_bool) { + type = t_bool; + } + + u64 hash = type_hash_canonical_type(type); + return type_info_index(info, {type, hash}, error_on_failure); +} + + + gb_internal void add_untyped(CheckerContext *c, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value) { if (expr == nullptr) { return; @@ -2002,13 +2054,17 @@ 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; } add_type_info_dependency(c->info, c->decl, t); - +#if 0 MUTEX_GUARD_BLOCK(&c->info->type_info_mutex) { + if (type_set_update(&c->info->type_info_set, t)) { + // return; + } + auto found = map_get(&c->info->type_info_map, t); if (found != nullptr) { // Types have already been added @@ -2031,7 +2087,8 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { // Unique entry // NOTE(bill): map entries grow linearly and in order ti_index = c->info->type_info_types.count; - array_add(&c->info->type_info_types, t); + TypeInfoPair tt = {t, type_hash_canonical_type(t)}; + array_add(&c->info->type_info_types, tt); } map_set(&c->checker->info.type_info_map, t, ti_index); @@ -2226,6 +2283,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { GB_PANIC("Unhandled type: %*.s %d", LIT(type_strings[bt->kind]), bt->kind); break; } +#endif } @@ -2283,19 +2341,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { return; } - auto *set = &c->info.minimum_dependency_type_info_set; - - isize ti_index = type_info_index(&c->info, t, false); - if (ti_index < 0) { - add_type_info_type(&c->builtin_ctx, t); // Missing the type information - ti_index = type_info_index(&c->info, t, false); - } - GB_ASSERT(ti_index >= 0); - // 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+1, count)) { - // Type already exists; + if (type_set_update(&c->info.min_dep_type_info_set, t)) { return; } @@ -2495,8 +2541,8 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) { if (decl == nullptr) { return; } - for (Type *t : decl->type_info_deps) { - add_min_dep_type_info(c, t); + for (TypeInfoPair const tt : decl->type_info_deps) { + add_min_dep_type_info(c, tt.type); } for (Entity *e : decl->deps) { @@ -2632,6 +2678,10 @@ 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); array_add(&c->info.init_procedures, e); @@ -2654,6 +2704,10 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st 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); array_add(&c->info.fini_procedures, e); @@ -2696,7 +2750,6 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) { 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) { \ @@ -3641,6 +3694,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) { @@ -3690,6 +3770,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; } @@ -3880,6 +3972,51 @@ 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; + } } return false; } @@ -3888,6 +4025,7 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) { #include "check_expr.cpp" #include "check_builtin.cpp" #include "check_type.cpp" +#include "name_canonicalization.cpp" #include "check_decl.cpp" #include "check_stmt.cpp" @@ -5010,6 +5148,9 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "export") { + ac->is_export = true; + return true; } else if (name == "force" || name == "require") { if (value != nullptr) { error(elem, "Expected no parameter for '%.*s'", LIT(name)); @@ -5034,6 +5175,12 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) { ac->extra_linker_flags = ev.value_string; } return true; + } else if (name == "ignore_duplicates") { + if (value != nullptr) { + error(elem, "Expected no parameter for '%.*s'", LIT(name)); + } + ac->ignore_duplicates = true; + return true; } return false; } @@ -5169,14 +5316,21 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { GB_ASSERT(fl->library_name.pos.line != 0); fl->library_name.string = library_name; + AttributeContext ac = {}; + check_decl_attributes(ctx, fl->attributes, foreign_import_decl_attribute, &ac); + + Scope *scope = parent_scope; + if (ac.is_export) { + scope = parent_scope->parent; + } + 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); + add_entity(ctx, scope, nullptr, e); + - AttributeContext ac = {}; - check_decl_attributes(ctx, fl->attributes, foreign_import_decl_attribute, &ac); if (ac.require_declaration) { mpsc_enqueue(&ctx->info->required_foreign_imports_through_force_queue, e); add_entity_use(ctx, nullptr, e); @@ -5184,6 +5338,9 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { if (ac.foreign_import_priority_index != 0) { e->LibraryName.priority_index = ac.foreign_import_priority_index; } + if (ac.ignore_duplicates) { + e->LibraryName.ignore_duplicates = true; + } String extra_linker_flags = string_trim_whitespace(ac.extra_linker_flags); if (extra_linker_flags.len != 0) { e->LibraryName.extra_linker_flags = extra_linker_flags; @@ -6193,6 +6350,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; @@ -6294,6 +6457,10 @@ gb_internal void check_deferred_procedures(Checker *c) { continue; } + if (dst_params == nullptr) { + error(src->token, "Deferred procedure must have parameters for %s", attribute); + continue; + } GB_ASSERT(dst_params->kind == Type_Tuple); Type *tsrc = alloc_type_tuple(); @@ -6344,6 +6511,44 @@ gb_internal void check_deferred_procedures(Checker *c) { } +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(); @@ -6378,6 +6583,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(); } } @@ -6460,7 +6678,7 @@ 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) { + if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) { i64 align = type_align_of(e->type); if (align > 0 && ptr_set_exists(&c->info.minimum_dependency_set, e)) { add_type_info_type(&c->builtin_ctx, e->type); @@ -6491,6 +6709,7 @@ gb_internal void check_update_dependency_tree_for_procedures(Checker *c) { } } + gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("map full filepaths to scope"); add_type_info_type(&c->builtin_ctx, t_invalid); @@ -6581,7 +6800,7 @@ gb_internal void check_parsed_files(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 && e->type != nullptr) { + if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) { (void)type_align_of(e->type); } else if (e->kind == Entity_Procedure) { DeclInfo *decl = e->decl_info; @@ -6600,6 +6819,9 @@ gb_internal void check_parsed_files(Checker *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); @@ -6695,6 +6917,58 @@ gb_internal void check_parsed_files(Checker *c) { add_type_and_value(&c->builtin_ctx, u.expr, u.info->mode, u.info->type, u.info->value); } + TIME_SECTION("initialize and check for collisions in type info array"); + { + Array<TypeInfoPair> type_info_types; // sorted after filled + array_init(&type_info_types, heap_allocator()); + defer (array_free(&type_info_types)); + + for (auto const &tt : c->info.min_dep_type_info_set) { + array_add(&type_info_types, tt); + } + array_sort(type_info_types, type_info_pair_cmp); + + array_init(&c->info.type_info_types_hash_map, heap_allocator(), type_info_types.count*2 + 1); + map_reserve(&c->info.min_dep_type_info_index_map, type_info_types.count); + + isize hash_map_len = c->info.type_info_types_hash_map.count; + for (auto const &tt : type_info_types) { + isize index = tt.hash % hash_map_len; + // NOTE(bill): no need for a sanity check since there + // will always be enough space for the entries + for (;;) { + if (index == 0 || c->info.type_info_types_hash_map[index].hash != 0) { + index = (index+1) % hash_map_len; + continue; + } + break; + } + c->info.type_info_types_hash_map[index] = tt; + + bool exists = map_set_if_not_previously_exists(&c->info.min_dep_type_info_index_map, tt.hash, index); + if (exists) { + for (auto const &entry : c->info.min_dep_type_info_index_map) { + if (entry.key != tt.hash) { + continue; + } + auto const &other = type_info_types[entry.value]; + if (are_types_identical_unique_tuples(tt.type, other.type)) { + continue; + } + gbString t = temp_canonical_string(tt.type); + gbString o = temp_canonical_string(other.type); + GB_PANIC("%s (%s) %llu vs %s (%s) %llu", + type_to_string(tt.type, false), t, cast(unsigned long long)tt.hash, + type_to_string(other.type, false), o, cast(unsigned long long)other.hash); + } + } + } + + + GB_ASSERT(c->info.min_dep_type_info_index_map.count <= type_info_types.count); + } + + TIME_SECTION("sort init and fini procedures"); check_sort_init_and_fini_procedures(c); |