feat(lang): universal raw identifier — parser exhaustiveness + raw type continuations + sema/LSP [F0.6]

Closes the remaining three F0.6 findings so the universal backtick raw
identifier holds in BOTH classifiers and at EVERY parser construction site.

1. Struct-body constants thread is_raw + name_span. The struct-body const
   forms (untyped `` `s2 :: 5 `` and typed `` `s2 : T : v ``) built the
   const_decl node without name_span/is_raw, so a backtick const was falsely
   rejected and a bare reserved-name const caretted at 1:1. They now capture
   both. Structural cure: `ast.ConstDecl`'s name_span + is_raw carry NO
   default, so the compiler rejects any construction site that omits them
   (mirrors checkBindingName's required `is_raw` arg). FnDecl keeps its
   defaults — every parser fn_decl routes through parseFnDecl whose
   `name_is_raw` is a required parameter (equivalent guarantee).

2. Raw identifier in TYPE position flows through the normal continuations.
   parseTypeExpr no longer returns a terminal type_expr for a raw atom; the
   raw flag rides the atom through the qualified-path / Closure / parameterized
   continuations, so `` `s2(s64) ``, `` *`s2 ``, `` ?`s2 `` all parse.
   ParameterizedTypeExpr carries is_raw; resolveParameterizedWithBindings
   skips the `Vector` intrinsic when raw.

3. sema/LSP (the second classifier) honors is_raw. Type.fromTypeExpr returns
   null for a raw type_expr; resolveTypeNode skips the builtin classifier when
   raw; resolveTypeNameStr takes a skip_builtin arg threaded from te/id.is_raw
   (compound inner names pass false). A backtick reserved-name annotation now
   resolves to the user type in the editor index, not the builtin.

Tests: examples/0156 (struct-body const), 0157 (parameterized raw type +
wrappers), 1142 (bare struct-body const errors, caret on name); src/sema.test.zig
pins the LSP raw-type resolution (fail-before verified). Gate: 365 unit tests,
429 examples, 0 failed.
This commit is contained in:
agra
2026-06-04 21:14:35 +03:00
parent 023971cae5
commit ef8f021c01
22 changed files with 300 additions and 53 deletions

View File

@@ -21,21 +21,35 @@
> - **Value position.** The parser skips `Type.fromName` for a raw identifier
> in expression position ([src/parser.zig] `parsePrimary`), so `` `s2 `` is a
> value identifier; a later bare reference resolves to the binding.
> - **Type position.** `parseTypeExpr` emits a raw `type_expr` (no qualified /
> `Closure` / parameterized continuation). Resolution skips the builtin
> classifier (`TypeResolver.resolveNamed`'s `skip_builtin`, threaded from
> `te.is_raw` in [src/ir/lower.zig] and [src/ir/type_bridge.zig]) and looks up
> a `` `s2 ``-declared type (struct / enum / union / alias), else a NORMAL
> - **Type position.** `parseTypeExpr` sets the raw flag on the type ATOM and
> lets it flow through the SAME continuations as a bare name (attempt 5), so a
> raw reference parameterizes a reserved-spelled template (`` `s2(s64) ``) and
> composes under the pointer / optional / slice wrappers; `ParameterizedTypeExpr`
> carries `is_raw` and `resolveParameterizedWithBindings` skips the `Vector`
> intrinsic when raw. Resolution skips the builtin classifier
> (`TypeResolver.resolveNamed`'s `skip_builtin`, threaded from `te.is_raw` in
> [src/ir/lower.zig] and [src/ir/type_bridge.zig]) and looks up a
> `` `s2 ``-declared type (struct / enum / union / alias), else a NORMAL
> "unknown type 's2'" error (`UnknownTypeChecker.reportIfUnknownType` skips the
> builtin-name exemption when raw). A bare `s2` in type position is still the
> builtin int.
> builtin int. The SECOND (editor/LSP) classifier in [src/sema.zig]
> (`Type.fromTypeExpr` / `resolveTypeNode` / `resolveTypeNameStr`) honors
> `is_raw` too, so a backtick reserved-name annotation resolves to the user type
> in hover/completion, not the builtin (no two-resolver divergence).
> - **Declaration position.** A bare reserved-name declaration of EVERY kind
> still errors (issue 0076 preserved); the backtick form is exempt. The check
> and the exemption are made structurally symmetric:
> `checkBindingName` / `checkDeclName` ([src/ir/semantic_diagnostics.zig]) take
> `is_raw` as a REQUIRED argument and skip inside the check — no call site can
> validate a name without also honoring the exemption, which is what kept the
> two from desyncing across the earlier attempts.
> two from desyncing across the earlier attempts. On the PARSER side the
> symmetry is enforced structurally for the bug-prone node: `ConstDecl`'s
> `name_span` + `is_raw` carry NO default (attempt 5), so the compiler rejects
> any construction site — including the two struct-body const forms (untyped
> `` `s2 :: 5 `` and typed `` `s2 : T : v ``) that previously dropped both —
> that omits them. `FnDecl` is built at every parser site through `parseFnDecl`,
> whose `name_is_raw` is a REQUIRED parameter (the equivalent guarantee); the
> type decls likewise route through parse-functions taking `name_is_raw`.
> 2. **`#import c` foreign-name exemption.** `c_import.zig` synthesizes foreign
> `#foreign` decls with `Param.is_raw = true` (and the synthesized `FnDecl`
> `is_raw = true`), so generated C names that collide with reserved type names
@@ -55,14 +69,20 @@
> `examples/0154-types-backtick-raw-type-reference.sx` (raw in TYPE position —
> struct / enum / union / alias decl + reference; bare `s2` still the int),
> `examples/0155-types-backtick-typed-const-union-tag.sx` (typed const + union tag),
> `examples/0156-types-backtick-struct-const.sx` (struct-body const, untyped + typed),
> `examples/0157-types-backtick-parameterized-raw-type.sx` (raw parameterized type +
> pointer/field wrappers),
> `examples/1054-errors-backtick-reserved-binding.sx` (`catch`/`onfail` tag
> bindings), `examples/1220-ffi-c-import-reserved-name-params.{sx,h,c}` (foreign
> param + fn-name exemption, bare-callable foreign fn); negatives
> `examples/1119`/`1121`/`1123` (bare reserved binding across forms),
> `examples/1140-diagnostics-reserved-name-const-fn-decl.sx` (bare const + fn decl),
> `examples/1141-diagnostics-reserved-name-type-decl.sx` (bare struct / enum / union
> / error / typed-const decl). Backtick lexer + `resolveNamed(skip_builtin)` unit
> tests in `src/lexer.zig` / `src/ir/type_resolver.test.zig`.
> / error / typed-const decl),
> `examples/1142-diagnostics-reserved-name-struct-const.sx` (bare struct-body const,
> caret on the name). Backtick lexer + `resolveNamed(skip_builtin)` unit tests in
> `src/lexer.zig` / `src/ir/type_resolver.test.zig`; the editor/LSP raw-type
> resolution (the second classifier) is pinned in `src/sema.test.zig`.
>
> The original report is preserved below.