From 9ade1e1b2c28fa681fa44c9331a5dad368a3e0c9 Mon Sep 17 00:00:00 2001 From: OmarAlJarrah Date: Thu, 5 Nov 2026 02:55:38 +0300 Subject: [PATCH] docs(emitters): reserve Kotlin's facades and Kotlin-word names Kotlin's reserved sets missed two kinds of name that the target and its export give classes of their own. kotlinc compiles a file's top-level functions and properties into a class named for the file, UserKt for User.kt, so a spec class UserKt in the same package fails the JVM build ("Duplicate JVM class name 'p/UserKt'"), and the Multiplatform export renames the file's class UserKt_. A unit's set now holds each such class. In Swift the export puts "Kotlin" before the names of its own classes (KotlinBase, KotlinInt) and of each standard-library class the framework reaches (KotlinThrowable, KotlinEnum). A spec class KotlinInt made the boxed KotlinInt ambiguous for a Swift caller. One named KotlinThrowable took the standard library's Swift name, leaving it KotlinThrowable_, so a caller's `as? KotlinThrowable` no longer matched a thrown exception. One named KotlinEnum kept its name only until the spec declared an enum. Every name whose first word is Kotlin is now reserved among the Swift names, rather than a list that a declaration the spec adds could grow, and T-19 holds the header to it. The package-casing paragraph said no two units differ in letter case alone, which holds for groups but not for partition names the options give, since no rule cases those; it now says so and points at #1491. The imports section points at #1501, which covers a class the target nests in a model or union hiding a class of its name inside it. --- docs/emitters/targets/kotlin/naming.md | 30 ++++++++++++++++-------- docs/emitters/targets/kotlin/swift.md | 11 +++++++++ docs/emitters/testing/mandatory-tests.md | 5 ++-- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/docs/emitters/targets/kotlin/naming.md b/docs/emitters/targets/kotlin/naming.md index d162b29ba..717f85ef7 100644 --- a/docs/emitters/targets/kotlin/naming.md +++ b/docs/emitters/targets/kotlin/naming.md @@ -29,6 +29,11 @@ What each [scope](../../refine/reserved-names.md) reserves in Kotlin: one unit's code happens to write, so adding an operation renames no model. - Each Windows device name above, in any letter case, as the JVM writes a top-level class to a file of its name, `Aux.class`, which Windows refuses whatever the extension. + - The class kotlinc compiles a file's top-level functions and properties into, the file's name + followed by `Kt` (`UserKt` for `User.kt`), for each file of the unit that holds one: a class + `UserKt` beside a top-level function or property in `User.kt` failed the JVM build ("Duplicate + JVM class name 'p/UserKt' generated from: UserKt, UserKt"), and beside a function the framework + exported the file's class as `UserKt_`. - On Multiplatform, in the Objective-C namespace every unit's declarations share, a nested one's name joined to its outer's ([Swift](swift.md)): each name above that the target declares, in every unit; the joined name of each public class the target nests in a type, such as @@ -38,18 +43,22 @@ What each [scope](../../refine/reserved-names.md) reserves in Kotlin: - On Multiplatform, among the Swift names a caller sees: every type name Swift's standard library and Foundation declare, which a Swift caller has in scope, a table of the options version as the naming tables are ([naming policy](../../refine/naming.md)), the target's own names included, so - the runtime's `Optional`, `Duration` and `Decimal` take Swift names of their own; and the - framework's own name, `Swift` and `Foundation`, which a caller writes to qualify a name - (`Probe.Client`). A module a caller imports by choice, such as SwiftUI, reserves nothing, since - a file importing it and the framework qualifies a name the two share. A declaration that meets a - name of this bullet or the last only there keeps its Kotlin name and takes an explicit - Objective-C and Swift name, so no JVM name moves for Swift's sake. + the runtime's `Optional`, `Duration` and `Decimal` take Swift names of their own; every name + whose first word is `Kotlin`, the word the export puts before the Swift name of each class of + its own and of each standard-library class the framework reaches (`KotlinInt`, + `KotlinThrowable`), a set the spec's first enum grows by `KotlinEnum`, so that adding a + declaration moves no Swift name; and the framework's own name, `Swift` and `Foundation`, which a + caller writes to qualify a name (`Probe.Client`). A module a caller imports by choice, such as + SwiftUI, reserves nothing, since a file importing it and the framework qualifies a name the two + share. A declaration that meets a name of this bullet or the last only there keeps its Kotlin + name and takes an explicit Objective-C and Swift name, so no JVM name moves for Swift's sake. - **A type**: `equals`, `hashCode`, `toString`, `Builder` and `Companion`. A unit's package segment is its words in lowercase, joined with no separator (`useraccounts`), as -Go's package names are. So two units whose words break differently render alike and collide ([output -units](../../refine/output-units.md)), and no two differ in letter case alone, which a -case-insensitive file system would make one directory. +Go's package names are. So two groups whose words break differently render alike and collide +([output units](../../refine/output-units.md)), and no two groups differ in letter case alone, which +a case-insensitive file system would make one directory. Two partitions' names, which the options +give and no rule cases, still can (#1491). None of a hard keyword, `kotlin` and `java` is a units name. A hard keyword as a package segment takes the escape below, which kotlinc accepts in a `package` and an `import` alike: every hard @@ -74,4 +83,5 @@ would not set apart, takes a trailing underscore. ## Imports Where a Kotlin file refers to a declaration with the simple name of a class it would import, it -writes that class qualified rather than importing it. +writes that class qualified rather than importing it. How code inside a model or union names a class +hidden by one the target nests there is #1501's. diff --git a/docs/emitters/targets/kotlin/swift.md b/docs/emitters/targets/kotlin/swift.md index 877d69371..bba24afc6 100644 --- a/docs/emitters/targets/kotlin/swift.md +++ b/docs/emitters/targets/kotlin/swift.md @@ -43,6 +43,17 @@ A Multiplatform SDK reaches iOS through the Objective-C export, which shapes the among them, took the same Objective-C name, and the framework failed to link (`Linking globals named 'OBJC_METACLASS_$_ProbeNumber': symbol multiply defined!`). With an explicit `@ObjCName` it linked, so each of these names is reserved ([naming](naming.md#reserved-names)). +- The export's own classes take `Kotlin` before their names in Swift (`KotlinBase`, `KotlinInt`), + and each standard-library class the framework reaches takes it in both languages + (`ProbeKotlinThrowable`, `KotlinThrowable` in Swift). Measured with Kotlin 2.4.0 and Swift 6.2.3, + a spec class `KotlinInt` linked, and a caller's `let b: KotlinInt? = Client().boxed()`, of a + function returning `Int?`, failed (`'KotlinInt' is ambiguous for type lookup in this context`). + With a spec class `KotlinThrowable` the export named the standard library's class + `KotlinThrowable_`, so a caller's `e.kotlinException as? KotlinThrowable` was `nil` for an + exception the SDK threw. A spec class `KotlinEnum` kept its name until the SDK declared an enum, + which brought the standard library's in and renamed the spec's `KotlinEnum_`. So every name whose + first word is `Kotlin` is reserved, not only the names the framework reaches, which a declaration + the spec adds can grow ([naming](naming.md#reserved-names)). - Default arguments do not reach Swift, which is one reason models and request types are built through a builder rather than a constructor with defaults. - An exception reaches Swift only as `@Throws` declares it: any other terminates the app, and a diff --git a/docs/emitters/testing/mandatory-tests.md b/docs/emitters/testing/mandatory-tests.md index 6985626f1..83e4e319d 100644 --- a/docs/emitters/testing/mandatory-tests.md +++ b/docs/emitters/testing/mandatory-tests.md @@ -83,8 +83,9 @@ throughout these documents. `wrapper` at the build's root leave every module's sources, no path the build writes, an artifact's or a class file's, has a Windows device name for a segment, and the Multiplatform framework links, its header giving each declaration the Swift name the naming policy chose, none - of them a type Swift's standard library or Foundation declares or a module's name, so a Swift - file that imports the framework and Foundation compiles its uses of both and qualifies a name by + of them a type Swift's standard library or Foundation declares, a module's name or a name whose + first word is `Kotlin`, so a Swift file that imports the framework and Foundation compiles its + uses of both, the export's `KotlinInt` and `KotlinThrowable` among them, and qualifies a name by either module ([Swift](../targets/kotlin/swift.md)). 20. **T-20 · Recursive types** — a model that holds itself directly, through a base, through a tuple and through another model, and one whose cycle passes through a list or a union, generate code