From d14e29be02d5b9afc22db5f15573eaad2dde253c Mon Sep 17 00:00:00 2001 From: agra Date: Thu, 4 Jun 2026 22:17:53 +0300 Subject: [PATCH] =?UTF-8?q?docs(lang):=20precise=20reserved-name=20rule=20?= =?UTF-8?q?=E2=80=94=20member-name=20positions=20are=20EXEMPT=20[F0.6]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AGRA RULING (issue 0089, attempt 7): bare reserved-name MEMBER positions are intentionally exempt from the reserved-type-name rule, and the implementation already does the right thing — this is a docs + one-example change, no code. The exempt member positions are struct FIELD names, union TAG names, and protocol method-SIGNATURE names: they sit in a member slot, are reached via obj.name (or dispatched by string), and are never type-classified, so they never mis-lower. The backtick is optional there. The exemption stops at member DEFINITIONS: an impl method is a real function (reached through the impl_block -> fn_decl arm), so a reserved-spelled impl method still needs the backtick, exactly like a free function (cf. examples/1122) — and every bare reserved-name value binding / declaration name still errors (0076 preserved). - specs.md / readme.md: replace the "every binding site" / "any binding site" overclaim with the precise rule — required positions (value bindings + declaration names + impl method definitions) vs the exempt member-name positions (field / tag / protocol signature; backtick optional). - examples/0158-types-reserved-name-member-exempt.sx: pins the exempt behavior — bare reserved-name struct fields + union tag read & written bare AND via backtick, and a protocol with a bare reserved-name method dispatched through the protocol (impl definition takes the backtick). - issues/0089: document the member-name exemption in the RESOLVED banner + add 0158 to the regression list. Gate: zig build, zig build test, bash tests/run_examples.sh — all green (430 passed, 0 failed, 0 timed out). --- .../0158-types-reserved-name-member-exempt.sx | 55 +++++++++++++++++++ ...158-types-reserved-name-member-exempt.exit | 1 + ...8-types-reserved-name-member-exempt.stderr | 1 + ...8-types-reserved-name-member-exempt.stdout | 5 ++ issues/0089-backtick-raw-identifier.md | 17 ++++++ readme.md | 21 ++++--- specs.md | 34 +++++++++--- 7 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 examples/0158-types-reserved-name-member-exempt.sx create mode 100644 examples/expected/0158-types-reserved-name-member-exempt.exit create mode 100644 examples/expected/0158-types-reserved-name-member-exempt.stderr create mode 100644 examples/expected/0158-types-reserved-name-member-exempt.stdout diff --git a/examples/0158-types-reserved-name-member-exempt.sx b/examples/0158-types-reserved-name-member-exempt.sx new file mode 100644 index 0000000..63d66f5 --- /dev/null +++ b/examples/0158-types-reserved-name-member-exempt.sx @@ -0,0 +1,55 @@ +// Reserved-name MEMBER positions are EXEMPT from the reserved-type-name rule: +// a bare reserved spelling (`s2`, `u8`, `s1`, …) is legal as a struct FIELD +// name, a union TAG name, and a protocol METHOD-SIGNATURE name. These are +// unambiguous — the name sits in a member slot and is reached via `obj.name` +// (or dispatched by string), so it is never type-classified and never +// mislowers. The backtick form is optional there and resolves to the same +// member. Backtick access (`obj.`s2`) and bare access (`obj.s2`) both work. +// +// The exemption stops at member SIGNATURES: an `impl` method DEFINITION is a +// real function, so its name is a declaration site (like a free function) and a +// reserved spelling still needs the backtick (`` `s2 :: (self) ``) — bare would +// be type-classified and mislower (the issue-0076 protection). A bare reserved +// VALUE binding / declaration name still errors (see examples/1119, 1141, 1142). +// Regression (issue 0089 — attempt-7: pins the Agra-ruled member-name exemption). +#import "modules/std.sx"; + +// Struct fields spelled with reserved type names — bare is legal. +Holder :: struct { + s2: s64; + u8: s64; +} + +// Union tags spelled with reserved type names — bare is legal. +Tag :: union { + s1: s32; + u16: f64; +} + +// Protocol method SIGNATURE spelled with a reserved type name — bare is legal. +Speaker :: protocol { + s2 :: () -> s64; +} + +Dog :: struct { n: s64; } +impl Speaker for Dog { + `s2 :: (self: *Dog) -> s64 { self.n } // impl DEFINITION → backtick required +} + +main :: () -> s32 { + h := Holder.{ s2 = 10, u8 = 20 }; + print("fields bare = {} {}\n", h.s2, h.u8); // bare member access + print("fields tick = {} {}\n", h.`s2, h.`u8); // backtick member access + h.s2 = 11; + h.`u8 = 21; // backtick write + print("fields set = {} {}\n", h.s2, h.u8); + + t : Tag = ---; + t.s1 = 5; + print("union = {} {}\n", t.s1, t.`s1); // bare + backtick — same tag + + items : List(Speaker) = .{}; + items.append(Dog.{ n = 7 }); + print("dispatch = {}\n", items.items[0].s2()); // bare reserved-name method call + return 0; +} diff --git a/examples/expected/0158-types-reserved-name-member-exempt.exit b/examples/expected/0158-types-reserved-name-member-exempt.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/examples/expected/0158-types-reserved-name-member-exempt.exit @@ -0,0 +1 @@ +0 diff --git a/examples/expected/0158-types-reserved-name-member-exempt.stderr b/examples/expected/0158-types-reserved-name-member-exempt.stderr new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/expected/0158-types-reserved-name-member-exempt.stderr @@ -0,0 +1 @@ + diff --git a/examples/expected/0158-types-reserved-name-member-exempt.stdout b/examples/expected/0158-types-reserved-name-member-exempt.stdout new file mode 100644 index 0000000..1f5052e --- /dev/null +++ b/examples/expected/0158-types-reserved-name-member-exempt.stdout @@ -0,0 +1,5 @@ +fields bare = 10 20 +fields tick = 10 20 +fields set = 11 21 +union = 5 5 +dispatch = 7 diff --git a/issues/0089-backtick-raw-identifier.md b/issues/0089-backtick-raw-identifier.md index 77efe16..de3c3ca 100644 --- a/issues/0089-backtick-raw-identifier.md +++ b/issues/0089-backtick-raw-identifier.md @@ -58,6 +58,20 @@ > 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`. +> - **Member-name positions are exempt** (Agra ruling, attempt 7). A struct +> **field** name, a union **tag** name, and a protocol **method-signature** +> name accept a bare reserved spelling: these sit in a member slot and are +> reached via `obj.name` / dispatched by string, so they are never +> type-classified and never mis-lower — the binding-name walk's `struct_decl` +> / `union_decl` / `enum_decl` / `protocol_decl` arms +> ([src/ir/semantic_diagnostics.zig]) check only the *type* name (and method +> *params*), not field / tag / variant / method-signature names. The backtick +> is optional there (`obj.s2` and `` obj.`s2 `` resolve to the same member). +> The exemption stops at member *definitions*: an `impl` method is a real +> function reached through the `impl_block` → `fn_decl` arm, so a +> reserved-spelled impl method needs the backtick (`` `s2 :: (self) ``), no +> more exempt than a free function (cf. `examples/1122`). Pinned by +> `examples/0158-types-reserved-name-member-exempt.sx`. > 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 @@ -80,6 +94,9 @@ > `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/0158-types-reserved-name-member-exempt.sx` (bare reserved-name struct +> fields / union tag / protocol method signature — read & written bare and via +> backtick; impl method definition takes the backtick), > `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 diff --git a/readme.md b/readme.md index 4ad2119..712142d 100644 --- a/readme.md +++ b/readme.md @@ -106,14 +106,19 @@ z : s32 = ---; // uninitialized ``` Builtin type names (`s2`, `u8`, `bool`, `string`, …) are reserved and a *bare* -spelling can't be used as an identifier at **any** binding site — a value binding -(`:=` / typed local / parameter), a `::` constant or function declaration, or a -`::` type declaration (`struct` / `enum` / `union` / alias / `protocol` / …) — each -is an error (`s2 :: 5` and `s2 :: (n) { … }` are rejected just like `s2 := 5`). A -leading backtick escapes one into a **raw identifier**: `` `name `` is the literal -identifier `name` (the backtick drops out of the text), usable in **every** -position — value, declaration, and type. It is the only way handwritten sx can -spell a reserved name. +spelling can't be used as an identifier at a **value-binding or declaration-name** +site — a value binding (`:=` / typed local / parameter), a `::` constant or +function declaration, an `impl` method definition, or a `::` type declaration +(`struct` / `enum` / `union` / alias / `protocol` / …) — each is an error +(`s2 :: 5` and `s2 :: (n) { … }` are rejected just like `s2 := 5`). **Member-name +positions are exempt**: a struct *field*, a union *tag*, and a protocol +*method-signature* may be a bare reserved spelling (`struct { s2: s64 }`, +`union { u8: … }`, `protocol { s2 :: (self) }`) — they are reached via `obj.name`, +so they never mis-lower. A leading backtick escapes one into a **raw identifier**: +`` `name `` is the literal identifier `name` (the backtick drops out of the text), +usable in **every** position — value, declaration, and type, and optional in the +exempt member positions. It is the only way handwritten sx can spell a reserved +name in a binding or declaration site. ```sx `s2 := 2.5; // identifier "s2", distinct from the s2 type diff --git a/specs.md b/specs.md index 2fe7450..583943e 100644 --- a/specs.md +++ b/specs.md @@ -17,14 +17,26 @@ Line comments start with `//` and extend to end of line. A spelling that names a builtin type — the arbitrary-width integers `s1`..`s64` / `u1`..`u64`, plus `bool`, `string`, `void`, `f32`, `f64`, `usize`, `isize`, `Any` — -is reserved. A bare reserved spelling is rejected at **every binding site** — -anywhere handwritten sx introduces a name: a value binding (`:=` / typed local / -parameter), a `::` **constant** or **function** declaration, and a `::` **type** -declaration (`struct` / `enum` / `union` / `error` / type alias / `protocol` / -foreign class / ufcs alias / namespaced import). A value-spelled-as-type parses as -a *type*, not a value, so its address-of / autoref paths would mis-lower; a -type/const/function name spelled as a builtin would shadow the builtin. The only -exemptions are the backtick escape (below) and `#import c` foreign decls. +is reserved. A bare reserved spelling is rejected at **value-binding and +declaration-name sites**: a value binding (`:=` / typed local / parameter), a +`::` **constant** or **function** declaration, an `impl` method **definition**, +and a `::` **type** declaration (`struct` / `enum` / `union` / `error` / type +alias / `protocol` / foreign class / ufcs alias / namespaced import). A +value-spelled-as-type parses as a *type*, not a value, so its address-of / +autoref paths would mis-lower; a type / const / function / method name spelled as +a builtin would shadow the builtin. The exemptions are the backtick escape +(below), `#import c` foreign decls, and **member-name positions** (next) — it is +**not** rejected at every place a name appears. + +**Member-name positions are exempt.** A struct **field** name, a union **tag** +name, and a protocol **method-signature** name may be a bare reserved spelling. +These sit in a member slot (`name: T` / `name :: (…)`) and are reached only via +`obj.name` (or dispatched by string), so they are never type-classified and never +mis-lower. The backtick form is optional there and names the same member — `obj.s2` +and `` obj.`s2 `` both resolve. The exemption covers member *signatures* only: an +`impl` method **definition** is a real function (a declaration site, not a member +slot), so a reserved-spelled impl method still needs the backtick +(`` `s2 :: (self) ``), exactly like a free function. See `examples/0158`. ```sx s2 := 2.5; // ERROR: 's2' is a reserved type name and cannot be used as an identifier @@ -91,6 +103,12 @@ for xs: (`bool, `u16) { } // for capture + index x catch `s2 { } // catch tag binding ``` +In the **member-name positions** among these — struct field, union tag, and +protocol method signature — the backtick is *optional*: the bare reserved +spelling is already legal there (see "Member-name positions are exempt" above). +Everywhere else (value bindings and declaration names, including an `impl` method +definition) the backtick is *required* to spell a reserved name. + A reserved-spelled **function** is bare-callable: `` `s2 :: (n: s64) -> s64 { … } `` can be invoked as `s2(10)` (the bare callee spelling parses as a type but resolves to the function when one of that name is in scope; `TypeName(val)` is not a cast).