This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory-site.git

commit 57369ae41ef9901c91a86c6fad675cd5997b515a
Author: chaokunyang <[email protected]>
AuthorDate: Thu May 28 05:22:50 2026 +0000

    🔄 synced local 'docs/compiler/' with remote 'docs/compiler/'
---
 docs/compiler/compiler-guide.md |   2 +-
 docs/compiler/generated-code.md | 110 +++++++++++++++++++++++++++++-----------
 docs/compiler/schema-idl.md     |  10 ++--
 3 files changed, 87 insertions(+), 35 deletions(-)

diff --git a/docs/compiler/compiler-guide.md b/docs/compiler/compiler-guide.md
index 7aeea2fe6d..1efe1d1c3d 100644
--- a/docs/compiler/compiler-guide.md
+++ b/docs/compiler/compiler-guide.md
@@ -419,7 +419,7 @@ generated/
 - Messages derive `org.apache.fory.scala.ForySerializer`
 - `optional T` fields use `Option[T]`
 - Enums use Scala 3 `enum`
-- Unions use Scala 3 ADT `enum` with `@ForyUnion`, `@ForyCase`, and an 
`UnknownCase`
+- Unions use Scala 3 ADT `enum` with `@ForyUnion`, `@ForyCase`, and an 
`Unknown`
 - Schema module object included
 
 ### Kotlin
diff --git a/docs/compiler/generated-code.md b/docs/compiler/generated-code.md
index 089bf5ae31..5164d3eb35 100644
--- a/docs/compiler/generated-code.md
+++ b/docs/compiler/generated-code.md
@@ -405,15 +405,29 @@ Rust output is one module file per schema, for example:
 
 ### Type Generation
 
-Unions map to Rust enums with `#[fory(id = ...)]` case attributes:
+Unions map to Rust enums with `#[fory(id = ...)]` schema case attributes.
+`#[fory(unknown)] Unknown(::fory::UnknownCase)` marks the runtime
+forward-compatibility carrier. The marker only selects the carrier and does not
+add an entry to the schema case table; schema cases still use the full `0..N`
+ID range. A generated typed union must have at least one non-`Unknown` case. 
The
+compiler marks the first declared non-`Unknown` case as `#[fory(default)]` and
+emits `Default` from that case:
 
 ```rust
-#[derive(ForyUnion, Debug, Clone, PartialEq)]
+#[derive(::fory::ForyUnion, Clone, Debug, PartialEq, Eq, Hash)]
 pub enum Animal {
+    #[fory(unknown)]
+    Unknown(::fory::UnknownCase),
+    #[fory(id = 0, default)]
+    Dog(self::Dog),
     #[fory(id = 1)]
-    Dog(Dog),
-    #[fory(id = 2)]
-    Cat(Cat),
+    Cat(self::Cat),
+}
+
+impl ::std::default::Default for Animal {
+    fn default() -> Self {
+        Self::Dog(<self::Dog as ::fory::ForyDefault>::fory_default())
+    }
 }
 ```
 
@@ -493,7 +507,7 @@ fory.register_union_by_name::<Holder>("myapp.models", 
"Holder")?;
 ```rust
 let person = Person {
     name: "Alice".into(),
-    pet: Animal::Dog(Dog::default()),
+    pet: Animal::Dog(self::Dog::default()),
     ..Default::default()
 };
 
@@ -787,11 +801,14 @@ public sealed partial class Person
 }
 ```
 
-Unions generate `[ForyUnion]` ADTs. Case ID `0` is the unknown-case
-carrier; schema-defined cases use positive `[ForyCase]` IDs. If a case needs
+Unions generate `[ForyUnion]` ADTs. `Unknown(UnknownCase)` is the
+runtime-owned forward-compatibility carrier marked with `[ForyUnknownCase]`.
+The marker only selects the carrier and does not add an entry to the schema 
case
+table. Schema-defined cases use non-negative `[ForyCase]` IDs. If a case needs
 non-default schema encoding, the generated `[ForyCase]` carries `Type`. Known
 case record names are PascalCase FDL case names; payload types are emitted as
-qualified references when needed to avoid name conflicts.
+qualified references when needed to avoid name conflicts. A typed union must
+have at least one non-`Unknown` case.
 
 ```csharp
 [ForyUnion]
@@ -799,13 +816,13 @@ public abstract partial record Animal
 {
     private Animal() {}
 
-    [ForyCase(0)]
-    public sealed partial record UnknownCase(int CaseId, object? Value) : 
Animal;
+    [ForyUnknownCase]
+    public sealed partial record Unknown(UnknownCase Value) : Animal;
 
-    [ForyCase(1)]
+    [ForyCase(0)]
     public sealed partial record Dog(global::addressbook.Dog Value) : Animal;
 
-    [ForyCase(2)]
+    [ForyCase(1)]
     public sealed partial record Cat(global::addressbook.Cat Value) : Animal;
 }
 ```
@@ -884,6 +901,10 @@ Swift output is one `.swift` file per schema, for example:
 ### Type Generation
 
 The generator creates Swift models with split model macros and stable 
field/case IDs.
+A typed union must include `@ForyUnknownCase case unknown(UnknownCase)` and at
+least one non-`unknown` case; `unknown(UnknownCase)` is only the
+runtime-owned forward-compatibility carrier. The marker only selects the 
carrier
+and does not add an entry to the schema case table.
 
 When package/namespace is non-empty, namespace shaping is controlled by 
`swift_namespace_style`:
 
@@ -897,10 +918,12 @@ For non-empty package with default `enum` style:
 ```swift
 public enum Addressbook {
     @ForyUnion
-    public enum Animal: Equatable {
-        @ForyCase(id: 1)
+    public enum Animal {
+        @ForyUnknownCase
+        case unknown(UnknownCase)
+        @ForyCase(id: 0)
         case dog(Addressbook.Dog)
-        @ForyCase(id: 2)
+        @ForyCase(id: 1)
         case cat(Addressbook.Cat)
     }
 
@@ -1193,20 +1216,35 @@ Generated Kotlin IDL sources express nullability with 
Kotlin `?`, not Fory
 construction cycles.
 
 Enums generate Kotlin enum classes with stable Fory enum IDs. Unions generate
-sealed classes with `@ForyUnion`; case ID `0` is the unknown-case carrier and
-schema-defined cases hold a single `value` property.
+sealed classes with `@ForyUnion`; the runtime-owned `Unknown(UnknownCase)`
+carrier is marked with `@ForyUnknownCase`. The marker only selects the carrier
+and does not add an entry to the schema case table. Schema-defined cases may 
use
+case IDs `0..N` and hold a single `value` property. A typed union must have at
+least one non-`Unknown` case.
 
 ```kotlin
+package addressbook
+
+import org.apache.fory.annotation.ForyCase
+import org.apache.fory.annotation.ForyUnion
+import org.apache.fory.annotation.ForyUnknownCase
+import org.apache.fory.type.union.UnknownCase
+
 @ForyUnion
 public sealed class Animal {
-  @ForyCase(id = 0)
-  public data class UnknownCase(public val caseId: Int, public val value: 
Any?) : Animal()
+  @ForyUnknownCase
+  public data class Unknown(public val value: UnknownCase) : Animal()
 
-  @ForyCase(id = 1)
-  public data class DogCase(public val value: Dog) : Animal()
+  @ForyCase(id = 0)
+  public data class Dog(public val value: addressbook.Dog) : Animal()
 }
 ```
 
+Packaged Kotlin output keeps the schema case name and qualifies the payload
+type when both have the same simple name. If a target output mode cannot 
express
+a legal qualifier for a conflict, the compiler appends `Case` to the generated
+case class name.
+
 Kotlin `int32`, `int64`, `uint32`, and `uint64` fields use xlang varint
 encoding by default, so generated Kotlin does not emit `@VarInt` for the
 default case. It emits `@Fixed` or `@Tagged` only when the schema requests that
@@ -1326,26 +1364,38 @@ enum PhoneType {
 }
 ```
 
-Unions generate Scala 3 ADT enums. Case ID `0` is reserved for the unknown-case
-carrier; schema-defined cases start at `1`.
+Unions generate Scala 3 ADT enums. `Unknown(UnknownCase)` is the runtime-owned
+forward-compatibility carrier marked with `@ForyUnknownCase`. It is omitted
+from the schema case table because the marker only selects the carrier and does
+not add a schema entry. Schema-defined cases use non-negative `@ForyCase` IDs.
+A typed union must have at least one
+non-`Unknown` case.
 
 ```scala
-import org.apache.fory.annotation.{ForyCase, ForyUnion}
+package addressbook
+
+import org.apache.fory.annotation.{ForyCase, ForyUnion, ForyUnknownCase}
 import org.apache.fory.scala.ForySerializer
+import org.apache.fory.`type`.union.UnknownCase
 
 @ForyUnion
 enum Animal derives ForySerializer {
+  @ForyUnknownCase
+  case Unknown(value: UnknownCase)
+
   @ForyCase(id = 0)
-  case UnknownCase(caseId: Int, value: Any)
+  case Dog(value: _root_.addressbook.Dog)
 
   @ForyCase(id = 1)
-  case DogCase(value: Dog)
-
-  @ForyCase(id = 2)
-  case CatCase(value: Cat)
+  case Cat(value: _root_.addressbook.Cat)
 }
 ```
 
+Packaged Scala output keeps the schema case name and qualifies the payload type
+when both have the same simple name. If a target output mode cannot express a
+legal qualifier for a conflict, the compiler appends `Case` to the generated
+case name.
+
 `optional T` fields generate `Option[T]`. Top-level message references use
 `@Ref` on the field or constructor parameter. Nested element/value references
 use type-use annotations such as `List[Node @Ref]`.
diff --git a/docs/compiler/schema-idl.md b/docs/compiler/schema-idl.md
index e7f59e2034..5b99fc892a 100644
--- a/docs/compiler/schema-idl.md
+++ b/docs/compiler/schema-idl.md
@@ -887,10 +887,12 @@ message Person [id=100] {
 
 ### Rules
 
-- Case IDs must be positive and unique within the union
-- Case ID `0` is reserved for language runtimes that expose an unknown-case 
carrier
+- Case IDs must be non-negative and unique within the union
+- Runtime unknown-case markers only select the carrier and do not add entries 
to
+  the schema case table
 - Cases cannot be `optional` or `ref`
-- Union cases do not support field options
+- Union cases support field options for payload metadata, such as scalar 
encoding
+  and collection element metadata
 - Case types can be primitives, enums, messages, or other named types
 - Union type IDs follow the rules in [Type IDs](#type-ids).
 
@@ -898,7 +900,7 @@ message Person [id=100] {
 
 ```
 union_def  := 'union' IDENTIFIER [type_options] '{' union_field* '}'
-union_field := field_type IDENTIFIER '=' INTEGER ';'
+union_field := ['repeated'] field_type IDENTIFIER '=' INTEGER [field_options] 
';'
 ```
 
 ## Service Definition


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to