test: complete scalar-function package acceptance

This commit is contained in:
2026-10-25 22:00:38 +01:00
parent c40d09b5cc
commit 0385e46295
3 changed files with 32 additions and 7 deletions
+2
View File
@@ -59,6 +59,8 @@ the backend (`@source/llvm`) consumes the existing typed IR, emits native functi
run the bootstrap suite with `bro test`. after `bro build`, run `python3 tests/native.py` for executable-level interpreter comparisons at both `-O0` and `-O2`, including traps, source diagnostics, array ownership, calling conventions, and CLI error handling. run the bootstrap suite with `bro test`. after `bro build`, run `python3 tests/native.py` for executable-level interpreter comparisons at both `-O0` and `-O2`, including traps, source diagnostics, array ownership, calling conventions, and CLI error handling.
the scalar-function package acceptance gate is verified by the native suite: root and dependency sibling files, transitive/shared imports, same-spelled declarations with distinct signatures, file-local aliases and conflicts, missing packages/members, argument errors and dependency-file diagnostics. file-command checks cover `run`, `emit-llvm` and `build`, loading dependency siblings while excluding root siblings.
dependency provenance checks cover lexer, parser, resolver, checker and lowering diagnostics, plus overflow, bounds, local initialization and global-cycle traps through transitive calls. they assert originating filenames and line/byte-column positions, compare reached and untaken paths, and run generated executables with the source tree temporarily removed. dependency provenance checks cover lexer, parser, resolver, checker and lowering diagnostics, plus overflow, bounds, local initialization and global-cycle traps through transitive calls. they assert originating filenames and line/byte-column positions, compare reached and untaken paths, and run generated executables with the source tree temporarily removed.
the native bootstrap accepts either a no-argument `main` or `main proc(init process.Init)`. it supplies `init.io` and `init.args` from native startup `argc`/`argv`. startup scans each argument once into a heap-allocated array of borrowed zero-terminated slices (`process.Args.values: [][;0]u8`), without copying strings. the descriptor array and borrowed strings remain valid until `main` returns; startup then frees the descriptors, including on a fallible return. allocation failure exits with status `2`. `init.args.values.len` includes the executable name, and `init.args.values[index]` directly accesses an argument after a length guard. callers can supply their own `process.Args { values = slices[..] }` for testing. the native bootstrap accepts either a no-argument `main` or `main proc(init process.Init)`. it supplies `init.io` and `init.args` from native startup `argc`/`argv`. startup scans each argument once into a heap-allocated array of borrowed zero-terminated slices (`process.Args.values: [][;0]u8`), without copying strings. the descriptor array and borrowed strings remain valid until `main` returns; startup then frees the descriptors, including on a fallible return. allocation failure exits with status `2`. `init.args.values.len` includes the executable name, and `init.args.values[index]` directly accesses an argument after a length guard. callers can supply their own `process.Args { values = slices[..] }` for testing.
+4 -4
View File
@@ -11,10 +11,10 @@ and source provenance through `@source/compilation`, with compilation-wide AST n
file commands retain single-file behavior; directory commands compile the file commands retain single-file behavior; directory commands compile the
package's production files and load its dependencies in separate scopes. package's production files and load its dependencies in separate scopes.
## next milestone: scalar-function packages ## completed milestone: scalar-function packages
compile and run a small multi-file directory package importing a second package verified: compile and run a small multi-file directory package importing a second
of scalar functions, through both the interpreter and native backend. package of scalar functions, through both the interpreter and native backend.
using the existing standard library is a later acceptance gate: even `std/mem` using the existing standard library is a later acceptance gate: even `std/mem`
needs records, pointers, generics and C calls. needs records, pointers, generics and C calls.
@@ -23,7 +23,7 @@ needs records, pointers, generics and C calls.
- [x] finish postfix namespace selection and resolve file-local import aliases without injecting dependency names into the importing package; bare and aliased import declarations are already parsed for dependency discovery. - [x] finish postfix namespace selection and resolve file-local import aliases without injecting dependency names into the importing package; bare and aliased import declarations are already parsed for dependency discovery.
- [x] reuse ordinary scalar signature checking, function values and IR calls for selected declarations; namespaces have no runtime representation. - [x] reuse ordinary scalar signature checking, function values and IR calls for selected declarations; namespaces have no runtime representation.
- [x] preserve originating filenames and spans in dependency diagnostics and native runtime traps; retain demand-driven diagnostic recovery. - [x] preserve originating filenames and spans in dependency diagnostics and native runtime traps; retain demand-driven diagnostic recovery.
- [ ] verify sibling source files, transitive/shared imports, same-spelled declarations in different packages, alias conflicts, missing packages/members, argument errors and dependency-file diagnostics. preserve existing single-file commands. - [x] verify sibling source files, transitive/shared imports, same-spelled declarations in different packages, alias conflicts, missing packages/members, argument errors and dependency-file diagnostics. preserve existing single-file commands.
visibility follows Bro's public-by-default contract; unsupported visibility visibility follows Bro's public-by-default contract; unsupported visibility
attributes must be diagnosed, never ignored. this milestone does not include attributes must be diagnosed, never ignored. this milestone does not include
+26 -3
View File
@@ -364,6 +364,7 @@ sibling proc(n int) int { return n * 2 + right.helper() - 1 }
put("left/main.hon", """ put("left/main.hon", """
import "../shared" import "../shared"
helper proc() int { return shared.helper() - 2 } helper proc() int { return shared.helper() - 2 }
identity proc(n int) int { return n }
main proc() int { return 99 } main proc() int { return 99 }
""") """)
put("right/main.hon", """ put("right/main.hon", """
@@ -371,11 +372,14 @@ import "../shared-link"
helper proc() int { return shared.helper() - 1 } helper proc() int { return shared.helper() - 1 }
""") """)
dependency = put("shared/main.hon", """ dependency = put("shared/main.hon", """
value :: 3
helper proc() int { return value } helper proc() int { return value }
identity proc(n f64) f64 { return n }
fail proc() int { return 9223372036854775807 + 1 } fail proc() int { return 9223372036854775807 + 1 }
""") """)
put("shared/zfunctions.bro", """
value :: 3
identity proc(n f64) f64 { return n }
""")
put("shared/ignored.test.bro", "this is deliberately not valid source\n")
(project / "shared-link").symlink_to(project / "shared", target_is_directory=True) (project / "shared-link").symlink_to(project / "shared", target_is_directory=True)
app = main.parent app = main.parent
check(app, executable, (b"42\n", None)) check(app, executable, (b"42\n", None))
@@ -386,6 +390,13 @@ fail proc() int { return 9223372036854775807 + 1 }
('import "../left"\nmain proc() int { return left.helper() }', b"1\n", None), ('import "../left"\nmain proc() int { return left.helper() }', b"1\n", None),
('lib :: import "../shared"\nmain proc() f64 { f :: lib.identity\nreturn f(1.5) }', b"1.5\n", None), ('lib :: import "../shared"\nmain proc() f64 { f :: lib.identity\nreturn f(1.5) }', b"1.5\n", None),
('lib :: import "../shared"\nmain proc() int { return lib.value }', b"3\n", None), ('lib :: import "../shared"\nmain proc() int { return lib.value }', b"3\n", None),
("""
import "../left"
import "../shared"
main proc() bool {
return left.identity(41) == 41 and shared.identity(1.5) == 1.5
}
""", b"true\n", None),
('lib :: import "../shared"\nmain proc() int { return lib.absent() }', b"", b"unknown name"), ('lib :: import "../shared"\nmain proc() int { return lib.absent() }', b"", b"unknown name"),
('lib :: import "../shared"\nmain proc() int { return helper() }', b"", b"unknown name"), ('lib :: import "../shared"\nmain proc() int { return helper() }', b"", b"unknown name"),
('import "../left"\nmain proc() int { return left.shared.helper() }', b"", b"unknown name"), ('import "../left"\nmain proc() int { return left.shared.helper() }', b"", b"unknown name"),
@@ -426,7 +437,19 @@ fail proc() int { return 9223372036854775807 + 1 }
check(app, executable, (b"", b"integer arithmetic overflow")) check(app, executable, (b"", b"integer arithmetic overflow"))
trapped = run(str(HONEY), "run", str(app)) trapped = run(str(HONEY), "run", str(app))
assert str(dependency.resolve()).encode() + b":2:" in trapped.stderr, outcome(trapped) assert str(dependency.resolve()).encode() + b":2:" in trapped.stderr, outcome(trapped)
return len(cases) + 4 # File commands still load dependency siblings, but never their own siblings.
single = put("single/main.bro", """
import "../shared"
main proc() f64 { return shared.identity(42) }
""")
put("single/poison.hon", """
main proc() int { return missing }
""")
check(single, executable, (b"42\n", None))
emitted = run(str(HONEY), "emit-llvm", str(single))
assert emitted.returncode == 0 and emitted.stderr == b"", outcome(emitted)
check(single.parent, executable, (b"42\n", b"duplicate name"))
return len(cases) + 6
def check_dependency_provenance(temporary, executable): def check_dependency_provenance(temporary, executable):