test: complete scalar-function package acceptance
This commit is contained in:
@@ -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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
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
|
||||
of scalar functions, through both the interpreter and native backend.
|
||||
verified: compile and run a small multi-file directory package importing a second
|
||||
package of scalar functions, through both the interpreter and native backend.
|
||||
using the existing standard library is a later acceptance gate: even `std/mem`
|
||||
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] 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.
|
||||
- [ ] 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
|
||||
attributes must be diagnosed, never ignored. this milestone does not include
|
||||
|
||||
+26
-3
@@ -364,6 +364,7 @@ sibling proc(n int) int { return n * 2 + right.helper() - 1 }
|
||||
put("left/main.hon", """
|
||||
import "../shared"
|
||||
helper proc() int { return shared.helper() - 2 }
|
||||
identity proc(n int) int { return n }
|
||||
main proc() int { return 99 }
|
||||
""")
|
||||
put("right/main.hon", """
|
||||
@@ -371,11 +372,14 @@ import "../shared-link"
|
||||
helper proc() int { return shared.helper() - 1 }
|
||||
""")
|
||||
dependency = put("shared/main.hon", """
|
||||
value :: 3
|
||||
helper proc() int { return value }
|
||||
identity proc(n f64) f64 { return n }
|
||||
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)
|
||||
app = main.parent
|
||||
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),
|
||||
('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),
|
||||
("""
|
||||
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 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"))
|
||||
trapped = run(str(HONEY), "run", str(app))
|
||||
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):
|
||||
|
||||
Reference in New Issue
Block a user