Skip to content

Commit

Permalink
JVM IR: Support sealed inline class methods
Browse files Browse the repository at this point in the history
 #KT-27576: Fixed
  • Loading branch information
ilmirus committed Oct 19, 2022
1 parent 1dd356a commit 122be41
Show file tree
Hide file tree
Showing 8 changed files with 777 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,11 @@ internal class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass
// Generate common bridges
val generated = mutableMapOf<Method, Bridge>()

for (override in irFunction.allOverridden()) {
// Do not generate additional bridges for functions, inherited from sealed inline classes in addition to functions from interfaces
val trulyOverridden = irFunction.allOverridden().filterNot {
it.origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT
}
for (override in trulyOverridden) {
if (override.isFakeOverride) continue

val signature = override.jvmMethod
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
copyAttributes(source)
}

override fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean) {
override fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, returnBoxedSealedInlineClass: Boolean) {
allScopes.push(createScope(source))
source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run {
val sourceExplicitParameters = source.explicitParameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
// This is a potential problem for bridge generation, where we have to ensure that the overridden
// symbols are always up to date. Right now they might not be since we lower each file independently
// and since deserialized declarations are not mangled at all.
if (function is IrSimpleFunction) {
if (function is IrSimpleFunction && function.parent.safeAs<IrClass>()?.isChildOfSealedInlineClass() != true) {
function.overriddenSymbols = replacements.replaceOverriddenSymbols(function)
}
return null
Expand All @@ -87,10 +87,10 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
}
}

private fun transformFlattenedConstructor(function: IrConstructor, replacement: IrConstructor): List<IrDeclaration>? {
replacement.valueParameters.forEach {
it.transformChildrenVoid()
it.defaultValue?.patchDeclarationParents(replacement)
private fun transformFlattenedConstructor(function: IrConstructor, replacement: IrConstructor): List<IrDeclaration> {
for (parameter in replacement.valueParameters) {
parameter.transformChildrenVoid()
parameter.defaultValue?.patchDeclarationParents(replacement)
}
allScopes.push(createScope(function))
replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
Expand All @@ -106,10 +106,10 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon

protected abstract fun transformSecondaryConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration>

private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
replacement.valueParameters.forEach {
it.transformChildrenVoid()
it.defaultValue?.patchDeclarationParents(replacement)
protected open fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> {
for (parameter in replacement.valueParameters) {
parameter.transformChildrenVoid()
parameter.defaultValue?.patchDeclarationParents(replacement)
}
allScopes.push(createScope(replacement))
replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement)
Expand Down Expand Up @@ -208,9 +208,9 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
// Replace the function body with a wrapper
if (bridgeFunction.isFakeOverride && bridgeFunction.parentAsClass.isSpecificLoweringLogicApplicable()) {
// Fake overrides redirect from the replacement to the original function, which is in turn replaced during interfacePhase.
createBridgeBody(replacement, bridgeFunction, function, true)
createBridgeBody(replacement, bridgeFunction)
} else {
createBridgeBody(bridgeFunction, replacement, function, false)
createBridgeBody(bridgeFunction, replacement)
}
return bridgeFunction
}
Expand All @@ -231,5 +231,5 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
// visibility rules for bridge methods.
abstract fun createBridgeDeclaration(source: IrSimpleFunction, replacement: IrSimpleFunction, mangledName: Name): IrSimpleFunction

protected abstract fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, original: IrFunction, inverted: Boolean)
protected abstract fun createBridgeBody(source: IrSimpleFunction, target: IrSimpleFunction, returnBoxedSealedInlineClass: Boolean = false)
}
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,24 @@ class JvmSymbols(
}
val illegalArgumentExceptionCtorString = illegalArgumentException.constructors.single()

val illegalStateException = createClass(FqName("java.lang.IllegalStateException")) { irClass ->
irClass.addConstructor {
name = Name.special("<init>")
}.apply {
addValueParameter("message", irBuiltIns.stringType)
}
}
val illegalStateExceptionCtorString = illegalStateException.constructors.single()

val classCastException = createClass(FqName("java.lang.ClassCastException")) { irClass ->
irClass.addConstructor {
name = Name.special("<init>")
}.apply {
addValueParameter("message", irBuiltIns.stringType)
}
}
val classCastExceptionCtorString = classCastException.constructors.single()

val jvmMethodType: IrSimpleFunctionSymbol =
irFactory.buildFun {
name = Name.special("<jvm-method-type>")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,12 @@ class MemoizedInlineClassReplacements(
// The [updateFrom] call will set the modality to FINAL for constructors, while the JVM backend would use OPEN here.
modality = Modality.OPEN
}
if (function is IrSimpleFunction && function.modality == Modality.ABSTRACT &&
function.parentAsClass.isSealedInline &&
replacementOrigin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT
) {
modality = Modality.OPEN
}
origin = when {
function.origin == IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER ->
JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD
Expand All @@ -336,6 +342,12 @@ class MemoizedInlineClassReplacements(
replacementOrigin
}
name = InlineClassAbi.mangledNameFor(function, mangleReturnTypes, useOldManglingScheme)
}.apply {
if (function is IrSimpleFunction) {
copyPropertyIfNeeded(function)
}

body()
}

override val getReplacementForRegularClassConstructor: (IrConstructor) -> IrConstructor? = alwaysNull()
Expand All @@ -346,4 +358,40 @@ class MemoizedInlineClassReplacements(
computeOverrideReplacement(it.owner).symbol
}
}

private fun IrSimpleFunction.copyPropertyIfNeeded(function: IrSimpleFunction) {
val propertySymbol = function.correspondingPropertySymbol
if (propertySymbol != null) {
val property = commonBuildProperty(propertySymbol)
when (function.withoutReceiver()) {
propertySymbol.owner.getter?.withoutReceiver() -> property.getter = this
propertySymbol.owner.setter?.withoutReceiver() -> property.setter = this
else -> error("Orphaned property getter/setter: ${function.render()}")
}
}
}

class SimpleFunctionWithoutReceiver(
val function: IrSimpleFunction
) {
override fun equals(other: Any?): Boolean {
if (other === this) return true
if (other !is SimpleFunctionWithoutReceiver) return false
return function.name == other.function.name &&
function.typeParameters == other.function.typeParameters &&
function.returnType == other.function.returnType &&
function.extensionReceiverParameter == other.function.extensionReceiverParameter &&
function.valueParameters == other.function.valueParameters
}

override fun hashCode(): Int {
return function.name.hashCode() xor
function.typeParameters.hashCode() xor
function.returnType.hashCode() xor
function.extensionReceiverParameter.hashCode() xor
function.valueParameters.hashCode()
}
}
}

fun IrSimpleFunction.withoutReceiver() = MemoizedInlineClassReplacements.SimpleFunctionWithoutReceiver(this)
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,7 @@ abstract class MemoizedValueClassAbstractReplacements(protected val irFactory: I
if (function is IrSimpleFunction) {
val propertySymbol = function.correspondingPropertySymbol
if (propertySymbol != null) {
val property = propertyMap.getOrPut(propertySymbol) {
irFactory.buildProperty {
name = propertySymbol.owner.name
updateFrom(propertySymbol.owner)
}.apply {
parent = propertySymbol.owner.parent
copyAttributes(propertySymbol.owner)
annotations = propertySymbol.owner.annotations
// In case this property is declared in an object in another file which is not yet lowered, its backing field will
// be made static later. We have to handle it here though, because this new property will be saved to the cache
// and reused when lowering the same call in all subsequent files, which would be incorrect if it was unlowered.
backingField = context.cachedDeclarations.getStaticBackingField(propertySymbol.owner)
?: propertySymbol.owner.backingField
}
}
correspondingPropertySymbol = property.symbol
val property = commonBuildProperty(propertySymbol)
when (function) {
propertySymbol.owner.getter -> property.getter = this
propertySymbol.owner.setter -> property.setter = this
Expand All @@ -106,6 +91,26 @@ abstract class MemoizedValueClassAbstractReplacements(protected val irFactory: I
body()
}

protected fun IrSimpleFunction.commonBuildProperty(propertySymbol: IrPropertySymbol): IrProperty {
val property = propertyMap.getOrPut(propertySymbol) {
irFactory.buildProperty {
name = propertySymbol.owner.name
updateFrom(propertySymbol.owner)
}.apply {
parent = propertySymbol.owner.parent
copyAttributes(propertySymbol.owner)
annotations = propertySymbol.owner.annotations
// In case this property is declared in an object in another file which is not yet lowered, its backing field will
// be made static later. We have to handle it here though, because this new property will be saved to the cache
// and reused when lowering the same call in all subsequent files, which would be incorrect if it was unlowered.
backingField = context.cachedDeclarations.getStaticBackingField(propertySymbol.owner)
?: propertySymbol.owner.backingField
}
}
correspondingPropertySymbol = property.symbol
return property
}

abstract val replaceOverriddenSymbols: (IrSimpleFunction) -> List<IrSimpleFunctionSymbol>

abstract val getReplacementForRegularClassConstructor: (IrConstructor) -> IrConstructor?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ fun IrBuilderWithScope.irEqeqeqWithoutBox(arg1: IrExpression, arg2: IrExpression
arg2
)

fun IrBuilderWithScope.irAndand(arg1: IrExpression, arg2: IrExpression) =
context.andand(startOffset, endOffset, arg1, arg2)

fun IrBuilderWithScope.irNull() =
irNull(context.irBuiltIns.nothingNType)

Expand Down

0 comments on commit 122be41

Please sign in to comment.