feat(lang): universal backtick raw identifier — valid in value, decl, AND type position [F0.6]
AGRA ruling (attempt 4): `` `name `` is THE LITERAL identifier `name`, usable in EVERY position — the backtick only means "treat this token as a plain identifier, never the reserved keyword/type", and is never part of the name's text. - Raw in TYPE position is now VALID (reverses attempt-2 "raw is not a type"): `parseTypeExpr` emits a raw `type_expr`; `TypeResolver.resolveNamed` gains a `skip_builtin` flag (threaded from `te.is_raw` via lower.zig + type_bridge) so a `` `s2 `` reference resolves to a `` `s2 ``-declared type (struct/enum/union/alias), else a normal "unknown type 's2'" error (reportIfUnknownType skips the builtin exemption when raw). Bare `s2` in type position stays the builtin int. - Every declaration-name site is is_raw-exemptible: `is_raw` added to TypeExpr + StructDecl/EnumDecl/UnionDecl/ErrorSetDecl/ProtocolDecl/ForeignClassDecl/UfcsAlias/ NamespaceDecl/ImportDecl/CImportDecl/LibraryDecl; parser threads name_is_raw to every decl parse fn; namespace imports carry it through imports.addNamespace. Typed-const path (`` `s2 : s64 : 5 ``) now threads name_span+is_raw (fixes the 1:1-caret bug). - Check<->exemption made structurally symmetric: checkBindingName/checkDeclName take is_raw as a REQUIRED argument and skip inside the check, so no call site can validate a name without honoring the exemption (the desync cause of prior rounds). - Bare reserved-name declarations of every kind still error (0076 preserved); `#import c` foreign names stay auto-raw + bare-callable. specs.md + readme.md updated to the universal model. issue 0089 RESOLVED banner rewritten. Examples: replace 1139 (raw-not-a-type) with 0154 (raw type reference); add 0155 (typed const + union tag) and 1141 (bare type-decl negatives). Gate: zig build + zig build test + run_examples (426 passed, 0 failed).
This commit is contained in:
@@ -1,62 +1,68 @@
|
||||
# 0089 — backtick raw-identifier escape + `#import c` foreign-name exemption from the reserved-type-name rule
|
||||
|
||||
> **✅ RESOLVED** (foundation step F0.6). Two mechanisms, per Agra's design ruling:
|
||||
> **✅ RESOLVED** (foundation step F0.6). Two mechanisms, per Agra's design
|
||||
> ruling; the final shape is the **universal raw identifier** (attempt 4):
|
||||
> `` `name `` is THE LITERAL identifier `name`, usable in EVERY position — value,
|
||||
> declaration, AND type — meaning only "treat this token as a plain identifier,
|
||||
> never the reserved keyword/type." The backtick is never part of the name's text.
|
||||
>
|
||||
> 1. **Backtick raw identifier.** The lexer recognises a leading backtick
|
||||
> (`` `s2 ``) and emits an `.identifier` token whose span excludes the backtick,
|
||||
> 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 `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).
|
||||
> carrying a `Token.is_raw` flag ([src/lexer.zig], [src/token.zig]). The flag
|
||||
> threads through `ast.Identifier`, `ast.TypeExpr`, and EVERY binding / capture /
|
||||
> declaration node ([src/ast.zig]): `VarDecl` / `ConstDecl` / `Param` / `FnDecl`
|
||||
> plus `IfExpr` / `WhileExpr` optional bindings, `ForExpr` capture + index,
|
||||
> `MatchArm` capture, `CatchExpr` / `OnFailStmt` tag bindings, `DestructureDecl`
|
||||
> per-name, protocol-default / foreign-class method params, AND every
|
||||
> type-introducing decl — `StructDecl` / `EnumDecl` / `UnionDecl` /
|
||||
> `ErrorSetDecl` / `ProtocolDecl` / `ForeignClassDecl` / `UfcsAlias` /
|
||||
> `NamespaceDecl` / `ImportDecl` / `CImportDecl` / `LibraryDecl`.
|
||||
>
|
||||
> The `::` DECLARATION forms are binding sites too and are equally covered
|
||||
> (F0.6 attempt-3): a bare reserved-name **constant** (`s2 :: 5`), **function**
|
||||
> (`s2 :: (…) {…}`, incl. struct/impl methods), or **type** declaration
|
||||
> (`struct`/`enum`/`union`/`error`/alias/`protocol`/foreign-class/ufcs/namespace)
|
||||
> is rejected, exactly like `s2 := …`. `ConstDecl`/`FnDecl` carry `is_raw` +
|
||||
> `name_span` threaded from the parser (`parseConstBinding`/`parseFnDecl`), so the
|
||||
> backtick form (`` `s2 :: … ``) is exempt; the compiler's own builtin definition
|
||||
> (`string :: []u8 #builtin`) is the sole non-backtick exemption (a `#builtin`
|
||||
> constant defines the reserved type). This closed the attempt-2 hole where a
|
||||
> bare `s2 :: (…) {…}` compiled silently and the call rewrite made it callable.
|
||||
> - **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
|
||||
> "unknown type 's2'" error (`UnknownTypeChecker.reportIfUnknownType` skips the
|
||||
> builtin-name exemption when raw). A bare `s2` in type position is still the
|
||||
> builtin int.
|
||||
> - **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.
|
||||
> 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.
|
||||
> `#foreign` decls with `Param.is_raw = true` (and the synthesized `FnDecl`
|
||||
> `is_raw = true`), so generated C names that collide with reserved type names
|
||||
> (`s1`, `s2`) import unedited and a reserved-name foreign fn is bare-callable.
|
||||
>
|
||||
> **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 RAW provenance** of that name is in scope
|
||||
> ([src/ir/lower.zig]) — the rewrite is scoped to the callee `FnDecl`'s `is_raw`
|
||||
> flag (F0.6 attempt-3), so it only ever fires for a backtick / `#import c` foreign
|
||||
> fn (the decl check guarantees no bare reserved-name fn exists), 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.
|
||||
> **Bare-callable foreign / backtick fn.** `lowerCall` rewrites a `.type_expr`
|
||||
> callee to an identifier when a function **of RAW provenance** of that name is in
|
||||
> scope ([src/ir/lower.zig]) — scoped to the callee `FnDecl`'s `is_raw` flag, so it
|
||||
> only ever fires for a backtick / `#import c` foreign fn (the decl check guarantees
|
||||
> no bare reserved-name fn exists). `s2(4)` resolves to the function (`TypeName(val)`
|
||||
> is not a cast).
|
||||
>
|
||||
> 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, 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),
|
||||
> `examples/0153-types-backtick-const-fn-decl.sx` (positive — backtick `::` const +
|
||||
> function decl, bare + backtick call), and
|
||||
> `examples/1140-diagnostics-reserved-name-const-fn-decl.sx` (negative — bare `::`
|
||||
> const + function decl rejected). Backtick lexer unit tests in `src/lexer.zig`.
|
||||
> **Regression tests.** `examples/0151-types-backtick-raw-identifier.sx` (every
|
||||
> VALUE position), `examples/0152-types-backtick-control-flow.sx` (every
|
||||
> control-flow / capture form), `examples/0153-types-backtick-const-fn-decl.sx`
|
||||
> (backtick `::` const + fn decl, bare + backtick call),
|
||||
> `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/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`.
|
||||
>
|
||||
> The original report is preserved below.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user