From eb75d8f82acf7f8f06ecef8810a369afb5a53dc9 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 18 Jun 2025 00:18:56 +0200 Subject: [PATCH] Method ambiguity when importing parent module publicly in private submodule. #2208 --- releasenotes.md | 3 +- src/compiler/compiler.c | 55 ++++++++-------- src/compiler/compiler_internal.h | 23 ++++++- src/compiler/sema_name_resolution.c | 13 +++- src/compiler/semantic_analyser.c | 2 +- src/compiler/symtab.c | 62 +++++++++++++++++++ .../methods/method_lookup_extensions_2208.c3 | 11 ++++ 7 files changed, 136 insertions(+), 33 deletions(-) create mode 100644 test/test_suite/methods/method_lookup_extensions_2208.c3 diff --git a/releasenotes.md b/releasenotes.md index 02f57cf59..eb0ba0f43 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -45,7 +45,8 @@ - Incorrect codegen if a macro ends with unreachable and is assigned to something. #2210 - Fix error for named arguments-order with compile-time arguments #2212 - Bug in AST copying would make operator overloading like `+=` compile incorrectly #2217. -- `$defined(#expr)` broken with binary. #2219 +- `$defined(#expr)` broken with binary. #2219 +- Method ambiguity when importing parent module publicly in private submodule. #2208 ### Stdlib changes - Deprecate `String.is_zstr` and `String.quick_zstr` #2188. diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index eb6a9ba3c..ab0399bd2 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -67,6 +67,7 @@ void compiler_init(BuildOptions *build_options) //compiler.lib_dir = find_lib_dir(); //DEBUG_LOG("Found std library: %s", compiler.lib_dir); htable_init(&compiler.context.modules, 16 * 1024); + pathtable_init(&compiler.context.path_symbols, INITIAL_SYMBOL_MAP); decltable_init(&compiler.context.symbols, INITIAL_SYMBOL_MAP); decltable_init(&compiler.context.generic_symbols, INITIAL_GENERIC_SYMBOL_MAP); @@ -166,23 +167,10 @@ void **tilde_gen(Module** modules, unsigned module_count) const char *build_base_name(void) { const char *name = out_name(); - if (!name) - { - Module **modules = compiler.context.module_list; - Module *main_module = (modules[0] == compiler.context.core_module && vec_size(modules) > 1) ? modules[1] : modules[0]; - Path *path = main_module->name; - size_t first = 0; - for (size_t i = path->len; i > 0; i--) - { - if (path->module[i - 1] == ':') - { - first = i; - break; - } - } - name = &path->module[first]; - } - return name; + if (name) return name; + Module **modules = compiler.context.module_list; + Module *main_module = (modules[0] == compiler.context.core_module && vec_size(modules) > 1) ? modules[1] : modules[0]; + return main_module->short_path; } static const char *exe_name(void) @@ -195,17 +183,7 @@ static const char *exe_name(void) } if (!name) { - Path *path = compiler.context.main->unit->module->name; - size_t first = 0; - for (size_t i = path->len; i > 0; i--) - { - if (path->module[i - 1] == ':') - { - first = i; - break; - } - } - name = &path->module[first]; + name = compiler.context.main->unit->module->short_path; } // Use custom extension if specified @@ -1544,6 +1522,7 @@ void compile() void global_context_add_decl(Decl *decl) { decltable_set(&compiler.context.symbols, decl); + pathtable_set(&compiler.context.path_symbols, decl); } void global_context_add_generic_decl(Decl *decl) @@ -1603,6 +1582,26 @@ Module *compiler_find_or_create_module(Path *module_name, const char **parameter // Set up the module. module = CALLOCS(Module); module->name = module_name; + size_t first = 0; + for (size_t i = module_name->len; i > 0; i--) + { + if (module_name->module[i - 1] == ':') + { + first = i; + break; + } + } + if (!first) + { + module->short_path = module_name->module; + } + else + { + const char *name = &module_name->module[first]; + size_t len = module_name->len - first; + TokenType type = TOKEN_IDENT; + module->short_path = symtab_add(name, len, fnv1a(name, len), &type); + } module->stage = ANALYSIS_NOT_BEGUN; module->parameters = parameters; module->is_generic = vec_size(parameters) > 0; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 1ff93e304..4d1e65b32 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -210,19 +210,32 @@ typedef struct SEntry *entries; } STable; -typedef struct SEntry2_ +typedef struct HEntry_ { void *key; void *value; - struct SEntry2_ *next; + struct HEntry_ *next; } HTEntry; +typedef struct PathTableEntry_ +{ + const char *short_path; + const char *name; + Decl *value; + struct PathTableEntry_ *next; +} PathTableEntry; + typedef struct { uint32_t mask; HTEntry **entries; } HTable; +typedef struct +{ + uint32_t mask; + PathTableEntry **entries; +} PathTable; typedef struct Path_ { @@ -1511,6 +1524,7 @@ static_assert(sizeof(void*) != 8 || sizeof(Ast) == 56, "Not expected Ast size"); typedef struct Module_ { Path *name; + const char *short_path; // Extname in case a module is renamed externally const char *extname; @@ -1858,6 +1872,7 @@ typedef struct HTable features; Module std_module; DeclTable symbols; + PathTable path_symbols; DeclTable generic_symbols; Path std_module_path; Type *string_type; @@ -2404,6 +2419,10 @@ void htable_init(HTable *table, uint32_t initial_size); void *htable_set(HTable *table, void *key, void *value); void *htable_get(HTable *table, void *key); +void pathtable_init(PathTable *table, uint32_t initial_size); +void pathtable_set(PathTable *table, Decl *value); +Decl *pathtable_get(PathTable *table, const char *short_path, const char *name); + UNUSED void stable_clear(STable *table); void decltable_init(DeclTable *table, uint32_t initial_size); diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 0ed5e4a82..a089c5a43 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -354,6 +354,11 @@ INLINE bool sema_resolve_ambiguity(SemaContext *context, Decl **current, Decl *c UNREACHABLE } +static Decl *sema_find_decl_by_short_path(Path *path, const char *name) +{ + return pathtable_get(&compiler.context.path_symbols, (void*)path->module, (void*)name); +} + static bool sema_find_decl_in_global(SemaContext *context, DeclTable *table, Module **module_list, NameResolve *name_resolve, bool want_generic) { @@ -450,6 +455,12 @@ static bool sema_resolve_path_symbol(SemaContext *context, NameResolve *name_res name_resolve->path_found = unit->module; } + Decl *decl = sema_find_decl_by_short_path(name_resolve->path, symbol); + if (decl && decl_ok(decl) && decl_is_visible(context->unit, decl)) + { + name_resolve->found = decl; + return true; + } // 3. Loop over imports. if (!sema_find_decl_in_imports(context, name_resolve, false)) return false; @@ -976,7 +987,7 @@ Decl *sema_resolve_type_method(CompilationUnit *unit, Type *type, const char *me import->import.import_private_as_public ? METHOD_SEARCH_PRIVATE_IMPORTED : METHOD_SEARCH_IMPORTED); - if (!new_found) continue; + if (!new_found || found == new_found) continue; if (found) { *ambiguous_ref = new_found; diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 0f429c317..b0af473bf 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -449,7 +449,7 @@ void sema_analysis_run(void) // All global defines are added to the std module compiler.context.std_module_path = (Path) { .module = kw_std, .span = INVALID_SPAN, .len = (uint32_t) strlen(kw_std) }; - compiler.context.std_module = (Module){ .name = &compiler.context.std_module_path }; + compiler.context.std_module = (Module){ .name = &compiler.context.std_module_path, .short_path = compiler.context.std_module_path.module }; compiler.context.std_module.stage = ANALYSIS_LAST; compiler.context.locals_list = NULL; diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index f050f4422..71c0c91e9 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -584,3 +584,65 @@ void *htable_get(HTable *table, void *key) return NULL; } +void pathtable_init(PathTable *table, uint32_t initial_size) +{ + ASSERT(initial_size && "Size must be larger than 0"); + size_t size = next_highest_power_of_2(initial_size); + + size_t mem_size = initial_size * sizeof(PathTableEntry); + table->entries = calloc_arena(mem_size); + + table->mask = size - 1; +} + +void pathtable_set(PathTable *table, Decl *value) +{ + ASSERT(value && "Cannot insert NULL"); + ASSERT_SPAN(value, value->name); + const char *short_path = value->unit->module->short_path; + const char *name = value->name; + uint32_t idx = ((((uintptr_t)short_path) ^ ((uintptr_t)short_path) >> 8) ^(((uintptr_t)name) ^ ((uintptr_t)name) >> 8)) & table->mask; + PathTableEntry **entry_ref = &table->entries[idx]; + PathTableEntry *entry = *entry_ref; + if (!entry) + { + entry = CALLOCS(HTEntry); + entry->short_path = short_path; + entry->name = name; + entry->value = value; + *entry_ref = entry; + return; + } + PathTableEntry *first_entry = entry; + do + { + if (entry->short_path == short_path && entry->name == name) + { + entry->value = poisoned_decl; + return; + } + entry = entry->next; + } while (entry); + + entry = CALLOCS(HTEntry); + entry->short_path = short_path; + entry->name = name; + entry->value = value; + entry->next = first_entry; + *entry_ref = entry; +} + + +Decl *pathtable_get(PathTable *table, const char *short_path, const char *name) +{ + uint32_t idx = ((((uintptr_t)short_path) ^ ((uintptr_t)short_path) >> 8) ^(((uintptr_t)name) ^ ((uintptr_t)name) >> 8)) & table->mask; + PathTableEntry *entry = table->entries[idx]; + if (!entry) return NULL; + do + { + if (entry->short_path == short_path && entry->name == name) return entry->value; + entry = entry->next; + } while (entry); + return NULL; +} + diff --git a/test/test_suite/methods/method_lookup_extensions_2208.c3 b/test/test_suite/methods/method_lookup_extensions_2208.c3 new file mode 100644 index 000000000..91801f52e --- /dev/null +++ b/test/test_suite/methods/method_lookup_extensions_2208.c3 @@ -0,0 +1,11 @@ +fn void main() {} +module foo @private; + +typedef Int = int; + +module foo::bar @private; +import foo @public @norecurse; + +fn Int Int.foo(x) => x + 0xc3; + +fn Int bar(Int x) => x * x.foo(); \ No newline at end of file