diff --git a/LANGUAGE.md b/LANGUAGE.md index a12c2ed..e0dd333 100644 --- a/LANGUAGE.md +++ b/LANGUAGE.md @@ -259,7 +259,7 @@ exactly once. Bare functions named `memcopy` or `memset` remain ordinary user fu - comptime parameters may appear anywhere, are erased from the runtime ABI, and accept recursively stable booleans, integers, floats, types, immutable bytes, enums, fixed arrays, records/tuples, optionals, tagged unions, and bare function identities; equal structural values and aliases of one function declaration share specializations, while distinct declarations remain distinct and pointers, general slices, fallibles, ranges, untagged unions, and undefined values have no stable comptime identity - comptime parameters may be omitted when uniquely recoverable from runtime arguments, the immediate expected result, or exact type-factory provenance; `_` is an explicit inference hole - direct bodyful value calls with no runtime parameters, including parameterless and all-`$` functions, are evaluated at comptime when their resolved result can materialize; otherwise they retain their zero-argument runtime specialization, while a reached `compile_error!` remains a diagnostic -- forced typed comptime expressions such as `$sum(1, 2)`, `$Point { x = 1, y = 2 }`, and comptime value blocks such as `${ yield 4 }` +- forced typed comptime expressions such as `$sum(1, 2)`, `$Point { x = 1, y = 2 }`, and comptime value blocks such as `${ yield 4 }`; imports referenced only by these initializers count as used, including when the initialized global is read from another package - comptime execution for bodyful Brolang functions with mutable locals, loops, `defer`, `match`, `try`/`catch`, exact type `==`/`!=`, pointer/slice storage mutation, pointer captures, and calls through comptime-known function values; `undefined` storage may be initialized at comptime, but remaining poison cannot be observed - comptime type factories such as `Box func($T type) type { return struct { value T } }`; calls like `Box(i32)` are concrete nominal types and may appear anywhere a type is expected - `struct_type!(layout, names, types, defaults)` constructs a nominal record type from comptime fixed arrays or tuples and is valid in every type position; layout is `.auto` or `.c`, bare `null` means a required field, and `some!(null)` installs an optional `null` default diff --git a/compiler/checker/comptime.odin b/compiler/checker/comptime.odin index 5c623ad..c9a17b5 100644 --- a/compiler/checker/comptime.odin +++ b/compiler/checker/comptime.odin @@ -6284,6 +6284,11 @@ build_comptime_expr :: proc( pkg: ast.Package_Id, file: ast.File_Id, ) -> hir.Expr_Id { + if expr.left != ast.INVALID_EXPR { + mark_expr_imports_used(checker, expr.left, file) + } else { + mark_block_imports_used(checker, expr.body, file) + } state := ct_state_make(checker, pkg, file) defer ct_state_destroy(&state) value := INVALID_CT_VALUE diff --git a/compiler_tests.odin b/compiler_tests.odin index 4dc224f..40b874c 100644 --- a/compiler_tests.odin +++ b/compiler_tests.odin @@ -10998,6 +10998,36 @@ unused_import_is_a_warning :: proc(t: ^testing.T) { testing.expect(t, found) } +@(test) +imports_used_only_by_comptime_global_initializers_are_not_warned :: proc(t: ^testing.T) { + directory := "/tmp/brolang-test-comptime-global-import" + output := "/tmp/brolang-test-comptime-global-import-output" + _ = os2.remove_all(directory) + defer _ = os2.remove_all(directory) + defer _ = os.remove(output) + packages := [3]string{"app", "table", "helper"} + for name in packages { + testing.expect(t, os2.make_directory_all(fmt.tprintf("%s/%s", directory, name)) == nil) + } + helper_text := `make func() [1]i32 { return [42] } +` + table_sources := [2]string{ + "helper :: import \"../helper\"\nvalues :: $helper.make()\n", + "helper :: import \"../helper\"\nvalues :: ${ yield helper.make() }\n", + } + app_text := `table :: import "../table" +main func() i32 { return table.values[0] } +` + testing.expect(t, os.write_entire_file(fmt.tprintf("%s/helper/main.bro", directory), transmute([]byte)helper_text)) + testing.expect(t, os.write_entire_file(fmt.tprintf("%s/app/main.bro", directory), transmute([]byte)app_text)) + for table_text in table_sources { + testing.expect(t, os.write_entire_file(fmt.tprintf("%s/table/main.bro", directory), transmute([]byte)table_text)) + testing.expect_value(t, compiler_core.compile_package(fmt.tprintf("%s/app", directory), output), 0) + state := run_executable(output) + testing.expect_value(t, state.exit_code, 42) + } +} + @(test) unused_missing_package_does_not_trap :: proc(t: ^testing.T) { output := "/tmp/brolang-test-package-missing-unused"