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]
