From 267e28fab937fccd5ebb60e6d1e393edd779e2e7 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 2 Mar 2026 17:04:42 +0000 Subject: [PATCH] add struct with visibility --- .../Host ABI Gate Validation Agenda.md | 4 +-- .../specs/pbs/3. Core Syntax Specification.md | 27 ++++++++++++++----- .../pbs/4. Static Semantics Specification.md | 17 +++++++++--- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/docs/agendas/Host ABI Gate Validation Agenda.md b/docs/agendas/Host ABI Gate Validation Agenda.md index a7be669e..6a7e636f 100644 --- a/docs/agendas/Host ABI Gate Validation Agenda.md +++ b/docs/agendas/Host ABI Gate Validation Agenda.md @@ -16,9 +16,9 @@ The question is whether the current contract is already stable enough to unblock Decision: sufficient for the next phase. -The current Host ABI contract is explicit enough to unblock the next stage even if the specification remains marked `Temporary` for now. +The current Host ABI contract is explicit enough to unblock the next stage. -`Temporary` should be interpreted here as "final binary-format and integration details may still be hardened", not as "core contract still missing". +the final binary-format and integration details may still be hardened, not as "core contract still missing". ## 3. Why This Is Sufficient diff --git a/docs/specs/pbs/3. Core Syntax Specification.md b/docs/specs/pbs/3. Core Syntax Specification.md index e674da92..01aad0e3 100644 --- a/docs/specs/pbs/3. Core Syntax Specification.md +++ b/docs/specs/pbs/3. Core Syntax Specification.md @@ -78,6 +78,7 @@ Active keywords in `.pbs`: - `import`, `from`, `as` - `service`, `host`, `fn`, `apply`, `bind`, `new`, `implements`, `using`, `ctor` - `declare`, `struct`, `contract`, `error`, `enum`, `callback`, `Self`, `this` +- `pub`, `mut` - `let`, `const` - `if`, `else`, `switch`, `default`, `for`, `from`, `until`, `step`, `while`, `break`, `continue`, `return` - `void`, `optional`, `result` @@ -87,7 +88,7 @@ Active keywords in `.pbs`: Barrel-only keywords: -- `pub`, `mod` +- `mod` - `type` Reserved (not active syntax in v1 core): @@ -139,7 +140,8 @@ Visibility levels: - `pub`: visible to importing modules. Top-level declarations not listed in `mod.barrel` are file-private. -Using `mod`/`pub` as top-level modifiers inside `.pbs` files is a syntax error. +Using `mod` or a top-level standalone `pub` modifier inside `.pbs` files is a syntax error. +Inside ordinary `.pbs` source, `pub` and `mut` are reserved for struct field declarations, with `mut` valid only immediately after `pub`. For overloaded functions, visibility is per-signature; each exported overload must appear explicitly in `mod.barrel`. ### 5.3 Barrel grammar @@ -269,7 +271,9 @@ HostDecl ::= 'declare' 'host' Identifier HostBody ErrorDecl ::= 'declare' 'error' Identifier ErrorBody EnumDecl ::= 'declare' 'enum' Identifier '(' EnumCaseDecl (',' EnumCaseDecl)* ','? ')' ';' StructFieldList ::= StructFieldDecl (',' StructFieldDecl)* ','? -StructFieldDecl ::= Identifier ':' TypeRef +StructFieldDecl ::= StructFieldAccess? Identifier ':' TypeRef +StructFieldAccess ::= 'pub' MutOpt +MutOpt ::= 'mut'? StructMethodBody ::= '{' StructMemberDecl* '}' StructMemberDecl ::= StructMethodDecl | StructCtorDecl StructMethodDecl ::= 'fn' Identifier ParamList ReturnAnn? Block @@ -290,7 +294,12 @@ Rules: - `declare struct Name(...);` declares a heap-backed nominal user type with fields only. - `declare struct Name(...) { ... }` declares a heap-backed nominal user type with fields and methods. -- Struct fields are declared positionally in the header and are mutable and accessible by default in v1 core. +- Struct fields are declared positionally in the header. +- A struct field with no access modifier is private to the enclosing struct body and its `ctor` declarations. +- `pub field: T` permits read access from outside the enclosing struct, but external writes remain invalid. +- `pub mut field: T` permits both read and write access from outside the enclosing struct. +- `mut` is valid in a struct field declaration only immediately after `pub`. +- Methods and `ctor` bodies of the enclosing struct may read and write all of that struct's fields regardless of field access modifier. - The enclosing struct name is in scope within its own field types, so self-referential headers such as `declare struct Node(next: optional Node);` are valid. - Struct method and `ctor` declarations belong only in the optional struct body. - Struct methods have an implicit `this` receiver and do not declare `this: Self` explicitly. @@ -632,6 +641,7 @@ At minimum, deterministic diagnostics are required for: - invalid callback declaration shape, - invalid `Self` type usage, - invalid `this` usage, +- invalid struct field access modifier surface, - invalid struct method declaration shape, - invalid service method declaration shape, - invalid `ctor` declaration shape, @@ -988,7 +998,7 @@ fn demo_callback() -> void { ```pbs declare callback UpdateCb(dt: int) -> void; -declare struct Enemy(hp: int); +declare struct Enemy(pub hp: int); fn enemy_tick(self: Enemy, dt: int) -> void { let _ = self.hp; @@ -1014,8 +1024,8 @@ declare contract StasisProcess { declare struct Struct( a: int, - b: str, - c: float, + pub b: str, + pub mut c: float, ) { fn compute_something() -> int { return this.a; @@ -1040,10 +1050,13 @@ fn demo_contract() -> int { let s2 = new Struct.createWithSomethingSpecial(1, 2); let sp: StasisProcess = s; let sp2 = s as StasisProcess; + let visible = s.b; + s.c = 4.0; let direct = s.compute_something(); let via_contract = sp.stasis(); let _ = s2.compute_something(); let _ = sp2.stasis(); + let _ = visible; return direct + via_contract; } ``` diff --git a/docs/specs/pbs/4. Static Semantics Specification.md b/docs/specs/pbs/4. Static Semantics Specification.md index 8d0139bf..6bce0cd8 100644 --- a/docs/specs/pbs/4. Static Semantics Specification.md +++ b/docs/specs/pbs/4. Static Semantics Specification.md @@ -222,7 +222,11 @@ Rules: - Struct construction arguments are matched positionally to the declared struct field order. - Assigning a struct value copies the reference, not the underlying field storage. - Assigning a service value copies the same canonical singleton identity; it does not create a new instance. -- Struct fields are mutable and accessible by default in v1 core. +- A struct field with no access modifier is readable and writable only from methods and `ctor` bodies of its enclosing struct. +- A `pub` struct field is externally readable but remains externally non-assignable. +- A `pub mut` struct field is externally readable and externally assignable. +- An assignment target `expr.name` to a struct field is valid only when the access site is permitted to write that field. +- Writes to a private struct field are valid only through `this.name` inside methods and `ctor` bodies of the enclosing struct. - A struct method declared in a struct body has an implicit receiver binding `this` of type `Self`. - A service method declared in a service body has an implicit receiver binding `this` of type `Self`. - `this` is valid only inside struct method, service method, and `ctor` bodies. @@ -266,7 +270,7 @@ declare contract StasisProcess { fn stasis() -> int; } -declare struct Struct(a: int) { +declare struct Struct(pub a: int) { fn compute() -> int { return this.a; } @@ -287,7 +291,7 @@ fn demo() -> int { let s2 = new Struct.createWithSomethingSpecial(2); let sp: StasisProcess = s; let sp2 = s as StasisProcess; - let a = s.compute(); + let a = s.a; let _ = s2.compute(); let b = sp.stasis(); let c = sp2.stasis(); @@ -483,8 +487,9 @@ Rules: - `TypeName.case` is valid when `TypeName` resolves to a declared enum type and `case` resolves to one of its declared enum cases. - `TypeName.case` has static type `TypeName`. -- `expr.name` is valid when the static type of `expr` is a declared `struct` containing field `name`. +- `expr.name` is valid when the static type of `expr` is a declared `struct` containing field `name` and the access site is permitted to read that field. - For `struct` field access, the projected member type is the declared field type. +- Reads of a private struct field are valid only through `this.name` inside methods and `ctor` bodies of the enclosing struct. - `expr.method` is a valid method-call target when the static type of `expr` is a concrete struct type declaring method `method`. - `expr.method` over a concrete struct receiver does not search visible contract implementations. - `expr.method` is a valid method-call target when the static type of `expr` is a concrete service type declaring method `method`. @@ -503,6 +508,8 @@ Rules: - `expr.key()` has static type `int`. - Access to a missing output label is a compile-time error. - Access to a missing struct field is a compile-time error. +- Read access to an inaccessible private struct field is a compile-time error. +- Write access to an inaccessible or non-`pub mut` struct field is a compile-time error. - Access to a missing struct method is a compile-time error. - Access to a missing service method is a compile-time error. - Access to a missing host method is a compile-time error. @@ -680,6 +687,8 @@ At minimum, deterministic static diagnostics are required for: - invalid mapped error label in `handle`, - attempted use of a single-slot tuple literal where no such surface form exists, - member access on a missing struct field, +- read access to an inaccessible private struct field, +- write access to an inaccessible or non-`pub mut` struct field, - member access on a missing struct method, - member access on a missing service method, - member access on a missing host method,