Skip to content

Commit

Permalink
[Swift Export] KT-71598: resolve init overrides
Browse files Browse the repository at this point in the history
calculateAllAvailableInitializers
initKind -> { isConvenience, isRequired }

Merge-request: KT-MR-18154
Merged-by: Gleb Lukianets <[email protected]>
  • Loading branch information
Gleb Lukianets authored and qodana-bot committed Oct 1, 2024
1 parent 7566565 commit 0a1907d
Show file tree
Hide file tree
Showing 36 changed files with 304 additions and 160 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ package org.jetbrains.sir.lightclasses.nodes

import org.jetbrains.kotlin.analysis.api.components.DefaultTypeClassIds
import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule
import org.jetbrains.kotlin.analysis.api.symbols.KaClassKind
import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KaNamedClassSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KaSymbolModality
import org.jetbrains.kotlin.analysis.api.symbols.*
import org.jetbrains.kotlin.analysis.api.types.symbol
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.builder.buildGetter
Expand All @@ -19,14 +16,14 @@ import org.jetbrains.kotlin.sir.builder.buildVariable
import org.jetbrains.kotlin.sir.providers.SirSession
import org.jetbrains.kotlin.sir.providers.source.KotlinSource
import org.jetbrains.kotlin.sir.providers.utils.KotlinRuntimeModule
import org.jetbrains.kotlin.sir.providers.utils.computeIsOverrideForDesignatedInit
import org.jetbrains.kotlin.sir.providers.utils.containingModule
import org.jetbrains.kotlin.sir.providers.utils.updateImport
import org.jetbrains.kotlin.sir.util.SirSwiftModule
import org.jetbrains.sir.lightclasses.SirFromKtSymbol
import org.jetbrains.sir.lightclasses.extensions.documentation
import org.jetbrains.sir.lightclasses.extensions.lazyWithSessions
import org.jetbrains.sir.lightclasses.extensions.withSessions
import org.jetbrains.sir.lightclasses.utils.computeIsOverride

internal class SirClassFromKtSymbol(
override val ktSymbol: KaNamedClassSymbol,
Expand Down Expand Up @@ -93,7 +90,6 @@ internal class SirClassFromKtSymbol(
private fun kotlinBaseInitDeclaration(): SirDeclaration = buildInit {
origin = SirOrigin.KotlinBaseInitOverride(`for` = KotlinSource(ktSymbol))
isFailable = false
initKind = SirInitializerKind.ORDINARY
isOverride = true
parameters.add(
SirParameter(
Expand All @@ -106,13 +102,7 @@ internal class SirClassFromKtSymbol(
private fun syntheticDeclarations(): List<SirDeclaration> = when (ktSymbol.classKind) {
KaClassKind.OBJECT, KaClassKind.COMPANION_OBJECT -> listOf(
kotlinBaseInitDeclaration(),
buildInit {
origin = SirOrigin.PrivateObjectInit(`for` = KotlinSource(ktSymbol))
visibility = SirVisibility.PRIVATE
isFailable = false
initKind = SirInitializerKind.ORDINARY
isOverride = computeIsOverrideForDesignatedInit(this@SirClassFromKtSymbol, emptyList())
},
SirObjectSyntheticInit(ktSymbol),
buildVariable {
origin = SirOrigin.ObjectAccessor(`for` = KotlinSource(ktSymbol))
visibility = SirVisibility.PUBLIC
Expand All @@ -129,3 +119,17 @@ internal class SirClassFromKtSymbol(
else -> listOf(kotlinBaseInitDeclaration())
}
}

internal class SirObjectSyntheticInit(ktSymbol: KaNamedClassSymbol) : SirInit() {
override val origin: SirOrigin = SirOrigin.PrivateObjectInit(`for` = KotlinSource(ktSymbol))
override val visibility: SirVisibility = SirVisibility.PRIVATE
override val isFailable: Boolean = false
override val parameters: List<SirParameter> = emptyList()
override val documentation: String? = null
override val isRequired: Boolean = false
override val isConvenience: Boolean = false
override val isOverride: Boolean get() = computeIsOverride()
override lateinit var parent: SirDeclarationParent
override val attributes: MutableList<SirAttribute> = mutableListOf()
override var body: SirFunctionBody? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import org.jetbrains.sir.lightclasses.extensions.*
import org.jetbrains.sir.lightclasses.extensions.documentation
import org.jetbrains.sir.lightclasses.extensions.lazyWithSessions
import org.jetbrains.sir.lightclasses.extensions.withSessions
import org.jetbrains.sir.lightclasses.utils.isSuitableForCovariantOverrideOf
import org.jetbrains.sir.lightclasses.utils.*
import org.jetbrains.sir.lightclasses.utils.isSubtypeOf
import org.jetbrains.sir.lightclasses.utils.overridableCandidates
import org.jetbrains.sir.lightclasses.utils.translateParameters
import org.jetbrains.sir.lightclasses.utils.translateReturnType
Expand Down Expand Up @@ -53,8 +54,8 @@ internal class SirFunctionFromKtSymbol(
override val isOverride: Boolean
get() = isInstance && overridableCandidates.any {
this.name == it.name &&
this.parameters == it.parameters &&
this.returnType.isSuitableForCovariantOverrideOf(it.returnType) &&
this.parameters.isSuitableForOverrideOf(it.parameters) &&
this.returnType.isSubtypeOf(it.returnType) &&
this.isInstance == it.isInstance
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.providers.SirSession
import org.jetbrains.kotlin.sir.providers.source.KotlinSource
import org.jetbrains.kotlin.sir.providers.utils.computeIsOverrideForDesignatedInit
import org.jetbrains.sir.lightclasses.SirFromKtSymbol
import org.jetbrains.sir.lightclasses.extensions.documentation
import org.jetbrains.sir.lightclasses.extensions.lazyWithSessions
import org.jetbrains.sir.lightclasses.extensions.withSessions
import org.jetbrains.sir.lightclasses.utils.computeIsOverride
import org.jetbrains.sir.lightclasses.utils.translateParameters

internal class SirInitFromKtSymbol(
Expand All @@ -25,7 +25,6 @@ internal class SirInitFromKtSymbol(

override val visibility: SirVisibility = SirVisibility.PUBLIC
override val isFailable: Boolean = false
override val initKind: SirInitializerKind = SirInitializerKind.ORDINARY

override val origin: SirOrigin by lazy {
KotlinSource(ktSymbol)
Expand All @@ -37,9 +36,11 @@ internal class SirInitFromKtSymbol(
ktSymbol.documentation()
}

override val isOverride: Boolean by lazy {
computeIsOverrideForDesignatedInit(parent, parameters)
}
override val isRequired: Boolean = false

override val isConvenience: Boolean = false

override val isOverride: Boolean get() = computeIsOverride()

override var parent: SirDeclarationParent
get() = withSessions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import org.jetbrains.sir.lightclasses.extensions.*
import org.jetbrains.sir.lightclasses.extensions.documentation
import org.jetbrains.sir.lightclasses.extensions.lazyWithSessions
import org.jetbrains.sir.lightclasses.extensions.withSessions
import org.jetbrains.sir.lightclasses.utils.isSuitableForCovariantOverrideOf
import org.jetbrains.sir.lightclasses.utils.isSubtypeOf
import org.jetbrains.sir.lightclasses.utils.overridableCandidates
import org.jetbrains.sir.lightclasses.utils.translateReturnType

Expand Down Expand Up @@ -68,7 +68,7 @@ internal class SirVariableFromKtSymbol(
override val isOverride: Boolean
get() = isInstance && overridableCandidates.any {
this.name == it.name &&
(it.setter == null && this.type.isSuitableForCovariantOverrideOf(it.type) || this.type == it.type) &&
(it.setter == null && this.type.isSubtypeOf(it.type) || this.type == it.type) &&
this.isInstance == it.isInstance
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,63 @@ internal inline val <reified T : SirClassMemberDeclaration> T.overridableCandida
.filter { it.modality != SirModality.FINAL }
.toList()


private val SirClass.superClassDeclaration: SirClass? get() = (superClass as? SirNominalType)?.typeDeclaration as? SirClass

internal fun SirType.isSuitableForCovariantOverrideOf(other: SirType): Boolean = when (this) {
is SirOptionalType -> (other as? SirOptionalType)?.let { wrappedType.isSuitableForCovariantOverrideOf(it.wrappedType) } ?: false
internal fun SirType.isSubtypeOf(other: SirType): Boolean = when (this) {
is SirOptionalType -> (other as? SirOptionalType)?.let { wrappedType.isSubtypeOf(it.wrappedType) } ?: false
is SirNominalType -> when (other) {
is SirOptionalType -> this.isSuitableForCovariantOverrideOf(other.wrappedType)
is SirOptionalType -> this.isSubtypeOf(other.wrappedType)
is SirNominalType -> this.typeDeclaration.isSubclassOf(other.typeDeclaration)
else -> false
}
else -> false
}

private fun SirDeclaration.isSubclassOf(other: SirDeclaration): Boolean = this == other || this is SirClass && (superClass as? SirNominalType)?.typeDeclaration?.isSubclassOf(other) ?: false

internal fun SirInit.computeIsOverride(): Boolean = (this.parent as? SirClass)?.superClassDeclaration?.let { cls ->
cls.overrideableInitializers.any { this.isViableOverrideFor(it) }
} ?: false

internal fun List<SirParameter>.isSuitableForOverrideOf(other: List<SirParameter>): Boolean =
this.size == other.size && this.zip(other).all { it.second.type.isSubtypeOf(it.first.type) }

private fun SirInit.isViableOverrideFor(other: SirInit): Boolean =
this.parameters.isSuitableForOverrideOf(other.parameters) && (!this.isFailable || this.isFailable && other.isFailable)

private val SirClass.overrideableInitializers: List<SirInit>
// By swift rules:
// 1) Only inits matching designated initializers from the superclass need to be marked `override`
// 2) Class inherits its parent's designated initializers if it doesn't itself define any designated (or required) initializers
get() = this.declarations.filterIsInstance<SirInit>().let { initializers ->
if (initializers.all { it.isConvenience }) {
return this.superClassDeclaration?.overrideableInitializers ?: emptyList()
} else {
return initializers.filter { !it.isConvenience }
}
}

/**
* Returns all available initializers to call, which includes both own and initializers inherited from parent
* Doesn't take into account initializers generated by the swift compiler and macros
*
* See https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization
*/
public fun SirClass.calculateAllAvailableInitializers(): List<SirInit> {
val parentInitializers = this.superClassDeclaration?.calculateAllAvailableInitializers() ?: emptyList()
val ownInitializers = this.declarations.filterIsInstance<SirInit>()
val result = ownInitializers.toMutableList()

// If the class does not define own designated initializers, it inherits designated initializers from parent
if (ownInitializers.none { !it.isConvenience }) {
result.addAll(parentInitializers.filter { !it.isConvenience })
}

// If the class overrides or inherits every parent designated initializer, it inherits convenience initializers from its parent
if (parentInitializers.filter { !it.isConvenience }.all { base -> result.any { it.isViableOverrideFor(base) } }) {
result.addAll(parentInitializers.filter { it.isConvenience })
}

return result
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,22 @@ public class SirAsSwiftSourcesPrinter(
}

private fun SirModule.printImports() {
val lastImport = imports.lastOrNull()
imports.forEach {
it.print()
if (it == lastImport) {
imports
.let {
if (stableDeclarationsOrder)
imports.sortedWith(compareBy(
{ it.moduleName },
{ it.mode },
))
else
imports
}.takeIf {
it.isNotEmpty()
}?.forEach {
it.print()
}?.also {
println()
}
}
}

private fun SirTypealias.print() {
Expand Down Expand Up @@ -203,7 +212,7 @@ public class SirAsSwiftSourcesPrinter(

private fun SirCallable.printOverride() {
when (this) {
is SirInit -> if (this.isOverride) {
is SirInit -> if (this.isOverride && !this.isRequired) {
print("override ")
}
is SirClassMemberDeclaration -> (this as SirClassMemberDeclaration).printOverride()
Expand Down Expand Up @@ -322,11 +331,20 @@ public class SirAsSwiftSourcesPrinter(
}
}

private fun SirCallable.printPreNameKeywords() = when (this) {
is SirInit -> initKind.print()
is SirFunction -> {}
is SirGetter -> print("get")
is SirSetter -> print("set")
private fun SirCallable.printPreNameKeywords() = this.also {
when (this) {
is SirInit -> {
if (isRequired) {
print("required ")
}
if (isConvenience) {
print("convenience ")
}
}
is SirFunction -> {}
is SirGetter -> print("get")
is SirSetter -> print("set")
}
}

private fun SirCallable.printName() = print(
Expand Down Expand Up @@ -364,14 +382,6 @@ public class SirAsSwiftSourcesPrinter(
}
)

private fun SirInitializerKind.print() = print(
when (this) {
SirInitializerKind.ORDINARY -> ""
SirInitializerKind.REQUIRED -> "required "
SirInitializerKind.CONVENIENCE -> "convenience "
}
)

private fun List<SirParameter>.print() =
takeIf { it.isNotEmpty() }
?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,6 @@ class SirAsSwiftSourcesPrinterTests {
declarations.add(
buildInit {
origin = SirOrigin.Unknown
initKind = SirInitializerKind.ORDINARY
visibility = SirVisibility.PUBLIC
isFailable = true
isOverride = false
Expand Down Expand Up @@ -620,7 +619,6 @@ class SirAsSwiftSourcesPrinterTests {
declarations.add(
buildInit {
origin = SirOrigin.Unknown
initKind = SirInitializerKind.ORDINARY
visibility = SirVisibility.PUBLIC
isFailable = false
isOverride = false
Expand Down Expand Up @@ -650,7 +648,7 @@ class SirAsSwiftSourcesPrinterTests {
declarations.add(
buildInit {
origin = SirOrigin.Unknown
initKind = SirInitializerKind.REQUIRED
isRequired = true
visibility = SirVisibility.PUBLIC
isFailable = false
isOverride = false
Expand Down Expand Up @@ -680,7 +678,7 @@ class SirAsSwiftSourcesPrinterTests {
declarations.add(
buildInit {
origin = SirOrigin.Unknown
initKind = SirInitializerKind.CONVENIENCE
isConvenience = true
visibility = SirVisibility.PUBLIC
isFailable = false
isOverride = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,11 @@ public object KotlinRuntimeModule : SirModule() {
declarations += buildInit {
origin = KotlinRuntimeElement()
isFailable = false
initKind = SirInitializerKind.ORDINARY
isOverride = false
}
declarations += buildInit {
origin = KotlinRuntimeElement()
isFailable = false
initKind = SirInitializerKind.ORDINARY
isOverride = false
parameters.add(
SirParameter(
Expand Down

This file was deleted.

3 changes: 2 additions & 1 deletion native/swift/sir/gen/org/jetbrains/kotlin/sir/SirInit.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ abstract class SirInit : SirElementBase(), SirCallable {
abstract override var body: SirFunctionBody?
abstract val isFailable: Boolean
abstract val parameters: List<SirParameter>
abstract val initKind: SirInitializerKind
abstract val isConvenience: Boolean
abstract val isRequired: Boolean
abstract val isOverride: Boolean
}
Loading

0 comments on commit 0a1907d

Please sign in to comment.