feat(lang): backtick raw identifier in every binding form + raw-not-a-type + foreign reserved-name fn bare-call [F0.6]

Completes the issue-0089 backtick raw-identifier / `#import c` exemption
across all remaining identifier positions and closes three boundary gaps
the F0.6 review found.

1. Exhaustive raw-binding coverage. The `is_raw` bit now threads through
   `ast.Identifier` and EVERY binding/capture form — `IfExpr`/`WhileExpr`
   optional bindings, `ForExpr` capture + index, `MatchArm` capture,
   `CatchExpr`/`OnFailStmt` tag bindings, `DestructureDecl` per-name, and
   the protocol-default-body / foreign-class method param lists — not just
   `var_decl`/`param`. `UnknownTypeChecker` skips the reserved-name check at
   each arm when raw, so a backtick works in every identifier position while
   a bare reserved spelling still errors (issue 0076 preserved).

2. Raw identifier is never a type. `parseTypeExpr`'s atom rejects a raw
   identifier in type position (`x : `s2 = 1`, `List(`s2)`) with an accurate
   diagnostic instead of silently type-classifying it.

3. Reserved-name function bare-callable. A bare `s2(4)` parses its callee as
   a `.type_expr` (reserved spelling); `lowerCall` now rewrites a type_expr
   callee to an identifier when a function of that name is in scope, so a
   backtick-declared sx fn and a `#import c` foreign fn whose C name collides
   with a reserved type spelling both resolve by their bare name.
   (`TypeName(val)` is not a cast, so there is no ambiguity.)

Tests: examples/0152 (every control-flow/capture form + bare ref/call/member
access), examples/1054 (catch/onfail tag bindings), examples/1139 (raw in
type position rejected), examples/1220 extended (foreign reserved-name
function bare-call). 0076 negatives 1119/1121/1122/1123/1124/1125 stay green.
Gate: zig build + zig build test + 422 examples pass. specs.md + readme.md
updated; issues/0089 RESOLVED banner refreshed.
This commit is contained in:
agra
2026-06-04 18:31:08 +03:00
parent 0dbdc530ba
commit 640f59dc54
23 changed files with 356 additions and 56 deletions

View File

@@ -4,25 +4,41 @@
>
> 1. **Backtick raw identifier.** The lexer recognises a leading backtick
> (`` `s2 ``) and emits an `.identifier` token whose span excludes the backtick,
> carrying a new `Token.is_raw` flag ([src/lexer.zig], [src/token.zig]). A raw
> carrying a `Token.is_raw` flag ([src/lexer.zig], [src/token.zig]). A raw
> identifier is NEVER type-classified — the parser skips `Type.fromName` for it
> in expression position ([src/parser.zig] `parsePrimary`), so it is always a
> value identifier. The flag threads to `VarDecl.is_raw` / `Param.is_raw`
> ([src/ast.zig]) at the binding sites, and `UnknownTypeChecker` skips the
> reserved-name check for raw bindings ([src/ir/semantic_diagnostics.zig]).
> Because the token tag stays `.identifier`, the escape works in every position
> (local, global, param, field, function name, struct member, later reference)
> with no per-site parser change.
> value identifier. The `is_raw` flag threads through `ast.Identifier` and EVERY
> binding/capture form ([src/ast.zig]): `VarDecl` / `Param` plus `IfExpr` /
> `WhileExpr` optional bindings, `ForExpr` capture + index, `MatchArm` capture,
> `CatchExpr` / `OnFailStmt` tag bindings, `DestructureDecl` per-name, and the
> protocol-default-body / foreign-class method param lists. `UnknownTypeChecker`
> skips the reserved-name check at each of those arms when raw
> ([src/ir/semantic_diagnostics.zig]). The backtick works in every identifier
> position (local, global, param, field, function name, struct member, later
> reference, and all the control-flow/capture/binding forms).
> 2. **`#import c` foreign-name exemption.** `c_import.zig` synthesizes foreign
> `#foreign` decls with `Param.is_raw = true`, so generated C param names that
> collide with reserved type names (`s1`, `s2`) import unedited.
>
> **Boundary rules.** A raw identifier is a value name and is NEVER a type: using
> one in type position (`x : `s2 = 1`) is a clean parse error ([src/parser.zig]
> `parseTypeExpr` atom). A reserved-spelled FUNCTION (backtick-declared or
> `#import c` foreign) is bare-callable: `lowerCall` rewrites a `.type_expr` callee
> to an identifier when a function of that name is in scope ([src/ir/lower.zig]),
> so `s2(4)` resolves to the function (`TypeName(val)` is not a cast). A later BARE
> reference in value position resolves to the binding; a bare `s2` in type position
> is still the type.
>
> A *bare* reserved-name binding in sx still errors (issue 0076 preserved): the
> `is_raw`-gated skip only fires for backtick / foreign names. Regression tests:
> `examples/0151-types-backtick-raw-identifier.sx` (backtick, every position),
> `examples/1220-ffi-c-import-reserved-name-params.{sx,h,c}` (foreign exemption),
> `examples/1119-diagnostics-reserved-type-name-as-identifier.sx` (negative —
> bare binding still rejected). Backtick lexer unit tests in `src/lexer.zig`.
> `examples/0151-types-backtick-raw-identifier.sx` (backtick, decl positions),
> `examples/0152-types-backtick-control-flow.sx` (every control-flow/capture form
> + bare ref/call/member access), `examples/1054-errors-backtick-reserved-binding.sx`
> (`catch`/`onfail` tag bindings), `examples/1220-ffi-c-import-reserved-name-params.{sx,h,c}`
> (foreign param + function-name exemption, bare-callable foreign fn),
> `examples/1139-diagnostics-backtick-raw-not-a-type.sx` (negative — raw in type
> position), `examples/1119`/`1121`/`1123` (negative — bare reserved binding still
> rejected across all forms). Backtick lexer unit tests in `src/lexer.zig`.
>
> The original report is preserved below.