diff --git a/lib/std/builtin.c3 b/lib/std/builtin.c3 index a56543e33..590d65739 100644 --- a/lib/std/builtin.c3 +++ b/lib/std/builtin.c3 @@ -2,6 +2,7 @@ // Use of this source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. module std::builtin; +import libc; fault VarCastResult { diff --git a/lib/std/mem_allocator_fn.c3 b/lib/std/mem_allocator_fn.c3 index d0021fbd0..0fa2750e2 100644 --- a/lib/std/mem_allocator_fn.c3 +++ b/lib/std/mem_allocator_fn.c3 @@ -1,4 +1,5 @@ module std::mem; +import libc; private fn void*! null_allocator_fn(void *data, usize bytes, usize alignment, void* old_pointer, AllocationKind kind) { diff --git a/resources/testproject/hello_world.c3 b/resources/testproject/hello_world.c3 index 095dcdd91..e4ec75107 100644 --- a/resources/testproject/hello_world.c3 +++ b/resources/testproject/hello_world.c3 @@ -1,5 +1,6 @@ module hello_world; - +import std; +import bar; $if (env::OS_TYPE == OsType.WIN32): fn int test_doubler(int x) { diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 99713bfce..167cab83a 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1258,6 +1258,9 @@ typedef struct Module_ Decl** generic_cache; HTable symbols; struct CompilationUnit_ **units; + Module *parent_module; + Module *top_module; + Module **sub_modules; } Module; @@ -1322,6 +1325,13 @@ typedef struct LexMode mode; } Lexer; +typedef struct +{ + uint32_t count; + uint32_t capacity; + uint32_t max_load; + DeclId *entries; +} DeclTable; typedef struct CompilationUnit_ { @@ -1405,13 +1415,6 @@ typedef struct SemaContext_ Expr *return_expr; } SemaContext; -typedef struct -{ - uint32_t count; - uint32_t capacity; - uint32_t max_load; - DeclId *entries; -} DeclTable; typedef struct { @@ -1535,6 +1538,7 @@ typedef struct { Decl *ambiguous_other_decl; Decl *private_decl; + Decl *maybe_decl; Path *path; SourceSpan span; const char *symbol; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 19a1e1175..7b8684b01 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -686,6 +686,8 @@ typedef enum typedef enum { ANALYSIS_NOT_BEGUN, + ANALYSIS_MODULE_HIERARCHY, + ANALYSIS_MODULE_TOP, ANALYSIS_IMPORTS, ANALYSIS_REGISTER_GLOBALS, ANALYSIS_CONDITIONAL_COMPILATION, diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 948f63d77..4f93a1e4b 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -63,6 +63,8 @@ void sema_context_init(SemaContext *context, CompilationUnit *unit); void sema_context_destroy(SemaContext *context); Decl **global_context_acquire_locals_list(void); void generic_context_release_locals_list(Decl **); +void sema_analyse_pass_top(Module *module); +void sema_analyse_pass_module_hierarchy(Module *module); void sema_analysis_pass_process_imports(Module *module); void sema_analysis_pass_register_globals(Module *module); void sema_analysis_pass_conditional_compilation(Module *module); diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index b32455e83..17a1a1d11 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -156,6 +156,87 @@ static Decl *sema_find_decl_in_global(DeclTable *table, Module **module_list, Na return decl; } +static bool decl_is_visible(CompilationUnit *unit, Decl *decl) +{ + Module *module = decl->module; + // 1. Same module as unit -> ok + if (module == unit->module) return true; + Module *top = module->top_module; + // 2. Same top module as unit -> ok + if (top == unit->module->top_module) return true; + + VECEACH(unit->imports, i) + { + Decl *import = unit->imports[i]; + Module *import_module = import->module; + // 3. Same as import + if (import_module == module) return true; + // 4. If import and decl doesn't share a top module -> no match + if (import_module->top_module != top) continue; + Module *search = module->parent_module; + // 5. Start upward from the decl module + // break if no parent or we reached the import module. + while (search && search != import_module) search = search->parent_module; + // 6. We found the import module + if (search) return true; + // 7. Otherwise go to next + } + return false; +} + +static Decl *sema_find_decl_in_global_new(CompilationUnit *unit, DeclTable *table, Module **module_list, NameResolve *name_resolve, bool want_generic) +{ + const char *symbol = name_resolve->symbol; + Path *path = name_resolve->path; + DeclId decl_ids = decltable_get(table, symbol); + Decl *maybe_decl = NULL; + // We might have no match at all. + if (!decl_ids) + { + // Update the path found + if (path && !name_resolve->path_found) name_resolve->path_found = sema_is_path_found(module_list, path, want_generic); + return NULL; + } + + Decl *decls = declptr(decl_ids); + // There might just be a single match. + if (decls->decl_kind != DECL_DECLARRAY) + { + if (path && !matches_subpath(decls->module->name, path)) return NULL; + if (!decl_is_visible(unit, decls)) + { + name_resolve->maybe_decl = decls; + return NULL; + } + name_resolve->private_decl = NULL; + return decls; + } + + // Else go through the list + Decl **decl_list = decls->decl_list; + Decl *ambiguous = NULL; + Decl *decl = NULL; + VECEACH(decl_list, i) + { + Decl *candidate = decl_list[i]; + if (path && !matches_subpath(candidate->module->name, path)) continue; + if (!decl_is_visible(unit, candidate)) + { + maybe_decl = candidate; + continue; + } + if (!ambiguous) + { + ambiguous = decl; + decl = candidate; + } + } + name_resolve->ambiguous_other_decl = ambiguous; + name_resolve->private_decl = NULL; + name_resolve->maybe_decl = maybe_decl; + return decl; +} + static Decl *sema_resolve_path_symbol(SemaContext *context, NameResolve *name_resolve) { Path *path = name_resolve->path; @@ -184,7 +265,7 @@ static Decl *sema_resolve_path_symbol(SemaContext *context, NameResolve *name_re decl = sema_find_decl_in_imports(unit->imports, name_resolve, false); // 4. Go to global search - return decl ? decl : sema_find_decl_in_global(&global_context.symbols, global_context.module_list, name_resolve, false); + return decl ? decl : sema_find_decl_in_global_new(unit, &global_context.symbols, global_context.module_list, name_resolve, false); } static inline Decl *sema_find_local(SemaContext *context, const char *symbol) @@ -240,7 +321,7 @@ static Decl *sema_resolve_no_path_symbol(SemaContext *context, NameResolve *name if (decl) return decl; decl = sema_find_decl_in_imports(unit->imports, name_resolve, false); - return decl ? decl : sema_find_decl_in_global(&global_context.symbols, NULL, name_resolve, false); + return decl ? decl : sema_find_decl_in_global_new(context->unit, &global_context.symbols, NULL, name_resolve, false); } @@ -266,7 +347,24 @@ static void sema_report_error_on_decl(Decl *found, NameResolve *name_resolve) } return; } - + if (!found && name_resolve->maybe_decl) + { + const char *maybe_name = decl_to_name(name_resolve->maybe_decl); + const char *module_name = name_resolve->maybe_decl->module->name->module; + if (path_name) + { + sema_error_at(span, "Did you mean the %s '%s::%s' in module %s? If so please add 'import %s'.", + maybe_name, module_name, symbol, module_name, module_name); + + } + else + { + sema_error_at(span, "Did you mean the %s '%s' in module %s? If so please add 'import %s'.", + maybe_name, symbol, module_name, module_name); + } + return; + } + if (name_resolve->ambiguous_other_decl) { assert(found); @@ -311,7 +409,7 @@ INLINE Decl *sema_resolve_symbol_common(SemaContext *context, NameResolve *name_ if (name_resolve->path) { decl = sema_resolve_path_symbol(context, name_resolve); - if (!decl && !name_resolve->path_found) + if (!decl && !name_resolve->maybe_decl && !name_resolve->path_found) { if (!name_resolve->suppress_error) return NULL; SEMA_ERROR(name_resolve->path, "Unknown module '%.*s', did you type it right?", name_resolve->path->len, name_resolve->path->module); @@ -408,9 +506,9 @@ Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, NameResolve *name Decl *decl = sema_find_decl_in_imports(unit->imports, name_resolve, true); if (!decl) { - decl = sema_find_decl_in_global(&global_context.generic_symbols, - global_context.generic_module_list, - name_resolve, true); + decl = sema_find_decl_in_global_new(unit, &global_context.generic_symbols, + global_context.generic_module_list, + name_resolve, true); } // 14. Error report if (!decl || name_resolve->ambiguous_other_decl) diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index ec02df2ef..1bd67e08f 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -4,6 +4,60 @@ #include "sema_internal.h" +void parent_path(StringSlice *slice) +{ + for (int i = (int)slice->len - 1; i >= 0; i--) + { + if (slice->ptr[i] == ':') + { + slice->len = i - 1; + return; + } + } + slice->len = 0; +} + +void sema_analyse_pass_top(Module *module) +{ + Module *parent = module; + while (parent->parent_module) parent = parent->parent_module; + module->top_module = parent; +} + +void sema_analyse_pass_module_hierarchy(Module *module) +{ + const char *name = module->name->module; + StringSlice slice = slice_from_string(name); + // foo::bar::baz -> foo::bar + parent_path(&slice); + // foo -> return, no parent + if (!slice.len) return; + + + unsigned module_count = vec_size(global_context.module_list); + for (int i = 0; i < module_count; i++) + { + Module *checked = global_context.module_list[i]; + Path *checked_name = checked->name; + if (checked_name->len != slice.len) continue; + // Found the parent! We're done, we add this parent + // and this as a child. + if (memcmp(checked_name->module, slice.ptr, slice.len) == 0) + { + module->parent_module = checked; + vec_add(module->sub_modules, module); + return; + } + } + // No match, so we create a synthetic module. + Path *path = path_create_from_string(slice.ptr, slice.len, module->name->span); + DEBUG_LOG("Creating parent module for %s: %s", module->name->module, path->module); + Module *parent_module = compiler_find_or_create_module(path, NULL, false /* always public */); + module->parent_module = parent_module; + vec_add(parent_module->sub_modules, module); + sema_analyze_stage(parent_module, ANALYSIS_MODULE_HIERARCHY); +} + void sema_analysis_pass_process_imports(Module *module) { @@ -57,17 +111,6 @@ void sema_analysis_pass_process_imports(Module *module) // 8. Assign the module. DEBUG_LOG("* Import of %s.", path->module); import->module = import_module; - for (unsigned j = 0; j < i; j++) - { - // 9. We might run into multiple imports of the same package. - if (import->module == unit->imports[j]->module) - { - SEMA_ERROR(import, "Module '%s' was imported more than once, please remove the duplicates.", path->module); - SEMA_PREV(unit->imports[j], "Previous import was here"); - decl_poison(import); - break; - } - } } import_count += imports; } diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index eb77e5a54..e09a1ec49 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -143,6 +143,12 @@ void sema_analyze_stage(Module *module, AnalysisStage stage) { case ANALYSIS_NOT_BEGUN: UNREACHABLE + case ANALYSIS_MODULE_HIERARCHY: + sema_analyse_pass_module_hierarchy(module); + break; + case ANALYSIS_MODULE_TOP: + sema_analyse_pass_top(module); + break; case ANALYSIS_IMPORTS: sema_analysis_pass_process_imports(module); break; @@ -228,6 +234,7 @@ static void analyze_generic_module(Module *module) { register_generic_decls(module, module->units[index]->global_decls); } + sema_analyze_stage(module, ANALYSIS_MODULE_HIERARCHY); } static void sema_analyze_to_stage(AnalysisStage stage) diff --git a/src/version.h b/src/version.h index 32b09daf6..b9873fa2e 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.2.4" \ No newline at end of file +#define COMPILER_VERSION "0.2.5" \ No newline at end of file diff --git a/test/test_suite/abi/darwinx64_2.c3t b/test/test_suite/abi/darwinx64_2.c3t index ecb9ece8a..d05f9f9ab 100644 --- a/test/test_suite/abi/darwinx64_2.c3t +++ b/test/test_suite/abi/darwinx64_2.c3t @@ -1,5 +1,6 @@ // #target: macos-x64 module test; +import std; struct St12 { diff --git a/test/test_suite/assert/unreachable.c3t b/test/test_suite/assert/unreachable.c3t index 41b7bb597..9ef45b607 100644 --- a/test/test_suite/assert/unreachable.c3t +++ b/test/test_suite/assert/unreachable.c3t @@ -1,9 +1,10 @@ - +import std; fn int foo() { return 1; } + fn void test() { int x = foo(); @@ -13,7 +14,7 @@ fn void test() } -// #expect: unreachable.ll +/* #expect: unreachable.ll define void @unreachable.test() #0 { @@ -29,7 +30,7 @@ if.then: ; preds = %entry ret void if.exit: ; preds = %entry - call void @"std::builtin.panic"(i8* getelementptr inbounds ([31 x i8], [31 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.1, i32 0, i32 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.2, i32 0, i32 0), i32 11) + call void @"std::builtin.panic"(i8* getelementptr inbounds ([31 x i8], [31 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.1, i32 0, i32 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.2, i32 0, i32 0), i32 12) unreachable after.unreachable: ; No predecessors! diff --git a/test/test_suite/enumerations/enum_associated_value.c3t b/test/test_suite/enumerations/enum_associated_value.c3t index 4a2b83191..7161f0592 100644 --- a/test/test_suite/enumerations/enum_associated_value.c3t +++ b/test/test_suite/enumerations/enum_associated_value.c3t @@ -1,5 +1,6 @@ // #target: macos-x64 module test; +import libc; enum Foo : uint (int val, char* testme) { diff --git a/test/test_suite/errors/error_regression_2.c3t b/test/test_suite/errors/error_regression_2.c3t index 2db800280..4632edd1d 100644 --- a/test/test_suite/errors/error_regression_2.c3t +++ b/test/test_suite/errors/error_regression_2.c3t @@ -1,6 +1,6 @@ // #target: macos-x64 module test; - +import std; import libc; struct Doc { Head *head; } diff --git a/test/test_suite/errors/general_error_regression.c3t b/test/test_suite/errors/general_error_regression.c3t index 8bc4aa4e4..7a9b687dc 100644 --- a/test/test_suite/errors/general_error_regression.c3t +++ b/test/test_suite/errors/general_error_regression.c3t @@ -1,7 +1,7 @@ // #target: macos-x64 module foo; import std::io; - +import libc; fault Foo { X, diff --git a/test/test_suite/errors/macro_err.c3t b/test/test_suite/errors/macro_err.c3t index 5c76c16ad..da5c55752 100644 --- a/test/test_suite/errors/macro_err.c3t +++ b/test/test_suite/errors/macro_err.c3t @@ -1,5 +1,6 @@ // #target: macos-x64 module test; +import libc; fn int! abc() { return 1; diff --git a/test/test_suite/errors/macro_err2.c3t b/test/test_suite/errors/macro_err2.c3t index 3d6aab51f..2eb945e88 100644 --- a/test/test_suite/errors/macro_err2.c3t +++ b/test/test_suite/errors/macro_err2.c3t @@ -1,5 +1,6 @@ // #target: macos-x64 module test; +import libc; fault Tester { FOO } fn int! abc() diff --git a/test/test_suite/errors/macro_err3.c3t b/test/test_suite/errors/macro_err3.c3t index 041b51b07..06026fcd6 100644 --- a/test/test_suite/errors/macro_err3.c3t +++ b/test/test_suite/errors/macro_err3.c3t @@ -1,5 +1,6 @@ // #target: macos-x64 module test; +import libc; fault Tester { FOO } diff --git a/test/test_suite/from_docs/examples_forswitch.c3t b/test/test_suite/from_docs/examples_forswitch.c3t index 7f8a441b0..78c68421d 100644 --- a/test/test_suite/from_docs/examples_forswitch.c3t +++ b/test/test_suite/from_docs/examples_forswitch.c3t @@ -1,7 +1,7 @@ // #target: macos-x64 module examples; - -import std::io; +import libc; +import std; fn void example_for() { diff --git a/test/test_suite/from_docs/examples_if_catch.c3t b/test/test_suite/from_docs/examples_if_catch.c3t index 6d27d26cd..e09229588 100644 --- a/test/test_suite/from_docs/examples_if_catch.c3t +++ b/test/test_suite/from_docs/examples_if_catch.c3t @@ -1,5 +1,6 @@ // #target: macos-x64 module demo; +import libc; import std::io; fault MathError diff --git a/test/test_suite/generic/generic_idents.c3t b/test/test_suite/generic/generic_idents.c3t index 32d7ad366..c957deedd 100644 --- a/test/test_suite/generic/generic_idents.c3t +++ b/test/test_suite/generic/generic_idents.c3t @@ -11,6 +11,7 @@ fn Type addMult(Type x, Type a, Type b) } module test; +import gen; define intMult = gen::mult; define doubleAddMult = gen::addMult; diff --git a/test/test_suite/import/access_other_module.c3t b/test/test_suite/import/access_other_module.c3t index 3f2ec4332..54d4f3d0c 100644 --- a/test/test_suite/import/access_other_module.c3t +++ b/test/test_suite/import/access_other_module.c3t @@ -1,5 +1,6 @@ // #target: macos-x64 module foo; +import std::math; fn void main() { void* foekf = &math::log; diff --git a/test/test_suite/import/import_error.c3 b/test/test_suite/import/import_error.c3 index 2a9c1acb9..40eee3519 100644 --- a/test/test_suite/import/import_error.c3 +++ b/test/test_suite/import/import_error.c3 @@ -1,6 +1,5 @@ module test; import std::mem; -import std::mem; // #error: was imported more import hello_world; // #error: No module named import test; // #error: Importing the current diff --git a/test/test_suite/import/import_implicit.c3 b/test/test_suite/import/import_implicit.c3 index 3df93af5a..e88d58023 100644 --- a/test/test_suite/import/import_implicit.c3 +++ b/test/test_suite/import/import_implicit.c3 @@ -12,6 +12,6 @@ module dde; fn void test() { - abc::test(); - Foo f; + abc::test(); // #error: Did you mean the function 'abc::test' + Foo f; // #error: Did you mean the struct 'Foo' } \ No newline at end of file diff --git a/test/test_suite/initializer_lists/statics.c3t b/test/test_suite/initializer_lists/statics.c3t index f4a0f6017..597218cdb 100644 --- a/test/test_suite/initializer_lists/statics.c3t +++ b/test/test_suite/initializer_lists/statics.c3t @@ -1,6 +1,7 @@ // #target: macos-x64 import std::io; import std::mem; +import libc; union Baz { int x; diff --git a/test/test_suite/initializer_lists/subarrays.c3t b/test/test_suite/initializer_lists/subarrays.c3t index 2b9ffe1c4..352d72c94 100644 --- a/test/test_suite/initializer_lists/subarrays.c3t +++ b/test/test_suite/initializer_lists/subarrays.c3t @@ -2,6 +2,7 @@ import std::io; import std::mem; +import libc; union Baz { int x; diff --git a/test/test_suite/macros/macro_convert_literal.c3 b/test/test_suite/macros/macro_convert_literal.c3 index e0099270a..25d7900c6 100644 --- a/test/test_suite/macros/macro_convert_literal.c3 +++ b/test/test_suite/macros/macro_convert_literal.c3 @@ -1,5 +1,5 @@ module foo; -import std::io; +import libc; macro foo(y) {