fix(diagnostics): reject reserved type-name bindings in every module (issue 0077)
The issue-0076 reserved-type-name binding diagnostic only ran over main-file decls, so an imported module (or the stdlib) could still declare `s2 := ...` and reach lowering, where the address-of family loads the whole aggregate and passes it by value to a `ptr` param — LLVM verifier abort. Extend coverage to every compiled module: a dedicated `checkBindingNames` walk (in semantic_diagnostics.zig) visits every var/`:=`/typed-local binding name and function/lambda/struct-method parameter at any depth, with NO main-file filter, descending the `namespace_decl` that a `mod :: #import` wraps so imported-module decls are reached. It tracks each module's source_file (save/restore per node) so the diagnostic renders against the imported module's text. Rejection still defers to the parser's `Type.fromName` classifier; the unknown-type check (0064) stays main-file-only. No lowering special-case; `.identifier`-only address-of paths are unchanged. Stdlib audit: the only reserved-name bindings under library/ were two `u1` locals in ui/renderer.sx (UV coords) — renamed to u_min/u_max/v_min/v_max. Regression test: examples/1120-diagnostics-imported-reserved-type-name.sx (+ companion mod.sx) — an imported `s2 := ...` now emits the clean diagnostic at the import's declaration site (exit 1), not an LLVM abort. Resolves issues 0076 (coverage extension) and 0077.
This commit is contained in:
@@ -31,8 +31,18 @@
|
||||
> both `update(@h, …)` and `h.update(…)`.
|
||||
>
|
||||
> Pre-existing example `examples/0904-...` declared locals `s1`/`s2` (incidental
|
||||
> names); renamed to `filled`/`empty`. Scope: main-file decls only, matching the
|
||||
> pass's existing trusted-imports convention.
|
||||
> names); renamed to `filled`/`empty`.
|
||||
>
|
||||
> **Coverage extension (issue 0077).** The first landing scoped the binding
|
||||
> check to main-file decls (matching the unknown-type check's trusted-imports
|
||||
> convention); an imported module could still declare `s2 := …` and hit the
|
||||
> original LLVM verifier abort. The reserved-name binding diagnostic now runs
|
||||
> over EVERY compiled module — imported user modules (descending the
|
||||
> `namespace_decl` an `mod :: #import` wraps) AND the stdlib `library/` — and
|
||||
> the two `u1` locals in `library/modules/ui/renderer.sx` were renamed
|
||||
> accordingly. The unknown-type check (issue 0064) stays main-file-only. See
|
||||
> issue 0077 for the imported-module facet and its pinned regression test
|
||||
> `examples/1120-diagnostics-imported-reserved-type-name.sx`.
|
||||
|
||||
## Symptom (how it first surfaced)
|
||||
|
||||
|
||||
103
issues/0077-imported-reserved-type-name-binding.md
Normal file
103
issues/0077-imported-reserved-type-name-binding.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# 0077 — reserved type-name binding diagnostic skips imported modules
|
||||
|
||||
> **Status: RESOLVED.**
|
||||
>
|
||||
> **Root cause:** the reserved-name binding diagnostic (issue 0076) only ran
|
||||
> over main-file decls (`UnknownTypeChecker.run`'s `main_file` filter). An
|
||||
> imported module's `s2 := …` was never checked and reached lowering, where the
|
||||
> address-of family loaded the whole aggregate and passed it by value to a
|
||||
> `*Box` param — LLVM verifier abort.
|
||||
>
|
||||
> **Fix:** the binding check (`checkBindingNames` in
|
||||
> `src/ir/semantic_diagnostics.zig`) now walks EVERY compiled module — no
|
||||
> main-file filter — visiting every `var`/`:=`/typed-local binding name and
|
||||
> function/lambda/struct-method parameter at any depth, and descending the
|
||||
> `namespace_decl` that a `mod :: #import` wraps so imported-module decls are
|
||||
> reached. The walk tracks each module's `source_file` (via the diagnostic
|
||||
> list's `current_source_file`, saved/restored per node) so the diagnostic
|
||||
> renders against the imported module's text. Rejection still defers to the
|
||||
> parser's `name_class.Type.fromName` classifier (no drift). The unknown-type
|
||||
> check (issue 0064) stays main-file-only. No lowering special-case; the
|
||||
> `.identifier`-only address-of paths are unchanged.
|
||||
>
|
||||
> **Stdlib audit:** the only reserved-name bindings under `library/` were two
|
||||
> `u1` locals in `library/modules/ui/renderer.sx` (lines 203, 382); the UV-coord
|
||||
> locals were renamed `u_min`/`u_max`/`v_min`/`v_max`. No reserved parameter
|
||||
> names or other reserved bindings exist in `library/` or `examples/`.
|
||||
>
|
||||
> **Regression test:** `examples/1120-diagnostics-imported-reserved-type-name.sx`
|
||||
> (+ companion `1120-diagnostics-imported-reserved-type-name/mod.sx`) — an
|
||||
> imported module declaring `s2 := …` now emits the clean diagnostic at the
|
||||
> import's declaration site (exit 1), not an LLVM abort.
|
||||
|
||||
## Symptom
|
||||
|
||||
An imported module can still declare a parameter or `var` binding whose name is a
|
||||
reserved/builtin type name. Observed: the imported-module repro below reaches
|
||||
lowering and fails LLVM verification by passing a loaded struct value to a
|
||||
`*Box` parameter. Expected: the same declaration-site diagnostic used for
|
||||
main-file issue 0076 should reject the imported module's `s2` binding before
|
||||
lowering.
|
||||
|
||||
## Reproduction
|
||||
|
||||
Create these two files under the repo root, then run
|
||||
`./zig-out/bin/sx run .sx-tmp/issue0077_main.sx`.
|
||||
|
||||
`.sx-tmp/issue0077_mod.sx`:
|
||||
|
||||
```sx
|
||||
#import "modules/std.sx";
|
||||
|
||||
Box :: struct { total: s64 = 0; count: s64 = 0; }
|
||||
|
||||
update :: (self: *Box, n: s64) {
|
||||
self.total += n;
|
||||
self.count += 1;
|
||||
}
|
||||
|
||||
run_imported_reserved_name :: () -> s32 {
|
||||
s2 := Box.{ total = 0, count = 0 };
|
||||
update(@s2, 5);
|
||||
s2.update(7);
|
||||
print("imported s2 total={} count={}\n", s2.total, s2.count);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
`.sx-tmp/issue0077_main.sx`:
|
||||
|
||||
```sx
|
||||
#import "modules/std.sx";
|
||||
mod :: #import ".sx-tmp/issue0077_mod.sx";
|
||||
|
||||
main :: () -> s32 {
|
||||
return mod.run_imported_reserved_name();
|
||||
}
|
||||
```
|
||||
|
||||
Current output on `flow/sx-foundation/F0.1`:
|
||||
|
||||
```text
|
||||
LLVM verification failed: Call parameter type does not match function signature!
|
||||
%load = load { i64, i64 }, ptr %alloca, align 8, !dbg !461
|
||||
ptr call void @update(ptr %0, { i64, i64 } %load, i64 5), !dbg !462
|
||||
```
|
||||
|
||||
## Investigation prompt
|
||||
|
||||
Investigate and fix issue 0077 in the sx compiler. The suspected area is
|
||||
`src/ir/semantic_diagnostics.zig`, especially
|
||||
`UnknownTypeChecker.run` and its `main_file` filter. Attempt 2 for issue 0076
|
||||
added `checkBindingName`, but it only runs over main-file declarations; imported
|
||||
modules are still trusted and can hit the original LLVM verifier failure. The
|
||||
fix likely needs to apply reserved-type-name binding diagnostics to all user
|
||||
source modules that are lowered, while preserving the existing trusted-stdlib
|
||||
or library convention only where intentionally required. Also audit existing
|
||||
reserved-name bindings in `library/` (for example `u1 := ...` in
|
||||
`library/modules/ui/renderer.sx`) and rename any source that will become
|
||||
newly illegal under the corrected rule.
|
||||
|
||||
Verification: run the two-file repro above and expect a clean diagnostic at the
|
||||
imported module's `s2 := ...` declaration, not LLVM verification failure. Then
|
||||
run `zig build`, `zig build test`, and `bash tests/run_examples.sh`.
|
||||
Reference in New Issue
Block a user