diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt index 53898062685..30b74b35a96 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt @@ -15,7 +15,9 @@ import org.jetbrains.kotlin.backend.jvm.descriptors.JvmDeclarationFactory import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope import org.jetbrains.kotlin.ir.builders.Scope @@ -142,6 +144,7 @@ val IrFunction.propertyIfAccessor: IrDeclaration get() = (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: this fun IrFunction.hasJvmDefault(): Boolean = propertyIfAccessor.hasAnnotation(JVM_DEFAULT_FQ_NAME) +fun IrFunction.hasPlatformDependent(): Boolean = propertyIfAccessor.hasAnnotation(PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME) fun IrFunction.getJvmVisibilityOfDefaultArgumentStub() = if (Visibilities.isPrivate(visibility) || isInlineOnly()) JavaVisibilities.PACKAGE_VISIBILITY else Visibilities.PUBLIC @@ -219,7 +222,6 @@ fun IrBody.replaceThisByStaticReference( } }, null) - // TODO: Interface Parameters // // The call sites using this function share that they are calling an @@ -270,3 +272,12 @@ fun IrFunctionAccessExpression.copyFromWithPlaceholderTypeArguments(existingCall putTypeArgument(i + offset, existingCall.getTypeArgument(i)) } } + +// Check whether a function maps to an abstract method. +// For non-interface methods or interface methods coming from Java the modality is correct. Kotlin interface methods +// are abstract unless they are annotated with @JvmDefault or @PlatformDependent or they override a method with +// such an annotation. +val IrSimpleFunction.isJvmAbstract: Boolean + get() = (modality == Modality.ABSTRACT) || + (parentAsClass.isJvmInterface && !hasJvmDefault() && !hasPlatformDependent() + && (!isFakeOverride || overriddenSymbols.all { it.owner.isJvmAbstract })) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt index b2f48548f83..3184519a1fb 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt @@ -5,287 +5,379 @@ package org.jetbrains.kotlin.backend.jvm.lower -import org.jetbrains.kotlin.backend.common.ClassLoweringPass -import org.jetbrains.kotlin.backend.common.bridges.FunctionHandle -import org.jetbrains.kotlin.backend.common.bridges.findAllReachableDeclarations -import org.jetbrains.kotlin.backend.common.bridges.findConcreteSuperDeclaration -import org.jetbrains.kotlin.backend.common.bridges.generateBridges -import org.jetbrains.kotlin.backend.common.ir.* +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.ir.copyTo +import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny +import org.jetbrains.kotlin.backend.common.ir.isStatic import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin +import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters -import org.jetbrains.kotlin.backend.jvm.ir.hasJvmDefault +import org.jetbrains.kotlin.backend.jvm.ir.isJvmAbstract import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.builders.declarations.addFunction import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl -import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl -import org.jetbrains.kotlin.ir.descriptors.WrappedReceiverParameterDescriptor -import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor -import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor -import org.jetbrains.kotlin.ir.expressions.IrBlockBody -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.IrExpressionBody -import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin -import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl -import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl -import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.getClass -import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl -import org.jetbrains.kotlin.ir.types.isNullable -import org.jetbrains.kotlin.ir.types.makeNullable +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.util.OperatorNameConventions -import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.jetbrains.kotlin.utils.getOrPutNullable import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.Method +/* + * Generate bridge methods to fix virtual dispatch after type erasure and to adapt Kotlin collections to + * the Java collection interfaces. For example, consider the following Kotlin declaration + * + * interface I { fun f(): T } + * abstract class A : MutableCollection, I { + * override fun f(): String = "OK" + * override fun contains(o: Int): Boolean = false + * } + * + * After type erasure we essentially have the following definitions. + * + * interface I { fun f(): java.lang.Object } + * abstract class A : java.util.Collection, I { + * fun f(): java.lang.String = "OK" + * fun contains(o: Int): Boolean = false + * } + * + * In particular, method `A.f` no longer overrides method `I.f`, since the return types do not match. + * This is why we have to introduce a bridge method into `A.f` to redirect calls from `I.f` to `A.f` and + * to add type casts as needed. + * + * The second source of bridge methods in Kotlin are so-called special bridges, which mediate between + * the Kotlin and Java collection interfaces. Note that we map the type `MutableCollection` to its + * Java equivalent `java.util.Collection`. However, there is a mismatch in naming conventions and + * signatures between the Java and Kotlin version. For example, the method `contains` has signature + * + * interface kotlin.Collection { + * fun contains(element: T): Boolean + * ... + * } + * + * in Kotlin, but a different signature + * + * interface java.util.Collection { + * fun contains(element: java.lang.Object): Boolean + * ... + * } + * + * in Java. In particular, the Java version is not type-safe: it requires us to implement the method + * given arbitrary objects, even though we know based on the types that our collection can only contain + * members of type `T`. This is why we have to introduce type-safe wrappers into Kotlin collection classes. + * In the example above, we produce: + * + * abstract class A : java.util.Collection, I { + * ... + * fun contains(element: java.lang.Object): Boolean { + * if (element !is Int) return false + * return contains(element as Int) + * } + * + * fun contains(o: Int): Boolean = false + * } + * + * Similarly, the naming conventions sometimes differ between the Java interfaces and their Kotlin counterparts. + * Sticking with the example above, we find that `java.util.Collection` contains a method `fun size(): Int`, + * which maps to a Kotlin property `val size: Int`. The latter is compiled to a method `fun getSize(): Int` and + * we introduce a bridge to map calls from `size()` to `getSize()`. + * + * Finally, while bridges due to type erasure are marked as synthetic, we need special bridges to be visible to + * the Java compiler. After all, special bridges are the implementation methods for some Java interfaces. If + * they were synthetic, they would be invisible to javac and it would complain that a Kotlin collection implementation + * class does not implement all of its interfaces. Similarly, special bridges should be final, since otherwise + * a user coming from Java might override their implementation, leading to the Kotlin and Java collection + * implementations getting out of sync. + * + * In the other direction, it is possible that a user would reimplement a Kotlin collection in Java. + * In order to guarantee binary compatibility, we remap all calls to Kotlin collection methods to + * their Java equivalents instead. + * + * Apart from these complications, bridge generation is conceptually simple: For a given Kotlin method we + * generate bridges for all overridden methods with different signatures, unless a final method with + * the same signature already exists in a superclass. We only diverge from this idea to match the behavior of + * the JVM backend in a few corner cases. + */ internal val bridgePhase = makeIrFilePhase( ::BridgeLowering, name = "Bridge", description = "Generate bridges" ) -private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass { - private val methodSignatureMapper = context.methodSignatureMapper +private class BridgeLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() { + // Represents a synthetic bridge to `overridden` with a precomputed signature + private class Bridge(val overridden: IrSimpleFunction, val signature: Method) + // Represents a special bridge to `overridden`. Special bridges are always public and non-synthetic. + // There are ten type-safe wrappers for different collection methods, which have an additional `methodInfo` + // field. There is only one special bridge method which uses generic types in Java (MutableList.removeAt) + // and needs a `specializedReturnType` for its overrides. Finally, we sometimes need to use INVOKESPECIAL + // to invoke an existing special bridge implementation in a superclass, which is what `superQualifierSymbol` + // is for. + private data class SpecialBridge( + val overridden: IrSimpleFunction, + val signature: Method, + val specializedReturnType: IrType? = null, + val methodInfo: SpecialMethodWithDefaultInfo? = null, + val superQualifierSymbol: IrClassSymbol? = null + ) + + override fun lower(irFile: IrFile) = irFile.transformChildrenVoid() + + override fun visitClass(declaration: IrClass): IrStatement { + // Bridges in DefaultImpl classes are handled in InterfaceLowering. + if (declaration.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS || declaration.isAnnotationClass) + return super.visitClass(declaration) + + // We generate bridges directly in the class, so we make a copy of the list of relevant members. + val potentialBridgeTargets = declaration.functions.filterTo(mutableListOf(), fun(irFunction: IrSimpleFunction): Boolean { + // Only overrides may need bridges and so in particular, private and static functions do not. + // Note that this includes the static replacements for inline class functions (which are static, but have + // overriddenSymbols in order to produce correct signatures in the type mapper). + if (Visibilities.isPrivate(irFunction.visibility) || irFunction.isStatic || irFunction.overriddenSymbols.isEmpty()) + return false + + // We ignore (fake overrides of) default argument stubs and methods of Any. + // Default argument stubs only appear in the base class and are synthetic, so there is no reason to produce + // bridges for them. Similarly, none of the methods of Any have type parameters and so we will not need bridges + // for them. + if (irFunction.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER || irFunction.isMethodOfAny()) + return false + + // We don't produce bridges for abstract functions in interfaces. + if (irFunction.isJvmAbstract) + return !irFunction.parentAsClass.isJvmInterface + + // Finally, the JVM backend also ignores concrete fake overrides which are implemented in interfaces. + // This is sound, since we do not generate type-specialized versions of fake overrides and if the method + // were to override several interface methods the frontend would require a separate implementation. + // + // In addition, there are @PlatformDependent methods which only exist on newer JDK versions + // (MutableMap.remove and getOrDefault). Trying to produce (special) bridges for these methods could + // result in incorrect bytecode on older JVM versions. However, all such methods are declared + // in interfaces and thus we don't need a separate check for them. + return !irFunction.isFakeOverride || !irFunction.resolveFakeOverride()!!.parentAsClass.isJvmInterface + }) + + for (member in potentialBridgeTargets) { + createBridges(declaration, member) + + // For lambda classes, we move overrides from the `invoke` function to its bridge. This will allow us to avoid boxing + // the return type of `invoke` in codegen for lambdas with primitive return type. + if (member.name == OperatorNameConventions.INVOKE && declaration.origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL) { + member.overriddenSymbols = listOf() + } + } + + return super.visitClass(declaration) + } + + private fun createBridges(irClass: IrClass, irFunction: IrSimpleFunction) { + // Track final overrides and bridges to avoid clashes + val blacklist = mutableSetOf() + + // Add the current method to the blacklist if it is concrete or final. + val targetMethod = (irFunction.resolveFakeOverride() ?: irFunction).jvmMethod + if (!irFunction.isFakeOverride || irFunction.modality == Modality.FINAL) + blacklist += targetMethod + + // Generate special bridges + val specialBridge = irFunction.specialBridgeOrNull + var bridgeTarget = irFunction + if (specialBridge != null) { + // If the current function overrides a special bridge then it's possible that we already generated a final + // bridge methods in a superclass. + blacklist += irFunction.overriddenFinalSpecialBridges() + + // We only generate a special bridge method if it does not clash with a final method in a superclass or the current method + if (specialBridge.signature !in blacklist && (!irFunction.isFakeOverride || irFunction.jvmMethod != specialBridge.signature)) { + // If irFunction is a fake override, we replace it with a stub and redirect all calls to irFunction with + // calls to the stub instead. Otherwise we'll end up calling the special method itself and get into an + // infinite loop. + // + // There are three cases to consider. If the method is abstract, then we simply generate a concrete abstract method + // to avoid generating a call to a method which does not exist in the current class. If the method is final, + // then we will not override it in a subclass and we do not need to generate an additional stub method. + // + // Finally, if we have a non-abstract, non-final fake-override we need to put in an additional bridge which uses + // INVOKESPECIAL to call the special bridge implementation in the superclass. We can be sure that an implementation + // exists in a superclass, since we do not generate bridges for fake overrides of interface methods. + if (irFunction.isFakeOverride) { + bridgeTarget = when { + irFunction.isJvmAbstract -> { + irClass.declarations.remove(irFunction) + irClass.addAbstractMethodStub(irFunction) + } + irFunction.modality != Modality.FINAL -> { + val superTarget = irFunction.overriddenSymbols.first { !it.owner.parentAsClass.isInterface }.owner + val superBridge = SpecialBridge( + irFunction, irFunction.jvmMethod, superQualifierSymbol = superTarget.parentAsClass.symbol, + methodInfo = specialBridge.methodInfo?.copy(argumentsToCheck = 0) // For potential argument boxing + ) + irClass.declarations.remove(irFunction) + irClass.addSpecialBridge(superBridge, superTarget) + } + else -> irFunction + } + + blacklist += bridgeTarget.jvmMethod + } + + irClass.addSpecialBridge(specialBridge, bridgeTarget) + blacklist += specialBridge.signature + } + + // Deal with existing function that override special bridge methods. + if (!irFunction.isFakeOverride && specialBridge.methodInfo != null) { + irFunction.rewriteSpecialMethodBody(targetMethod, specialBridge.signature, specialBridge.methodInfo) + } + } else if (irFunction.isJvmAbstract) { + // Do not generate bridge methods for abstract methods which do not override a special bridge method. + // This matches the behavior of the JVM backend, but it does mean that we generate superfluous bridges + // for abstract methods overriding a special bridge for which we do not create a bridge due to, + // e.g., signature clashes. + return + } + + // Generate common bridges + val generated = mutableMapOf() + + irFunction.allOverridden().filter { !it.isFakeOverride }.forEach { override -> + val signature = override.jvmMethod + if (targetMethod != signature && signature !in generated && signature !in blacklist) { + generated[signature] = Bridge(override, signature) + } + } + + // For concrete fake overrides, some of the bridges may be inherited from the super-classes. Specifically, bridges for all + // declarations that are reachable from all concrete immediate super-functions of the given function. Note that all such bridges are + // guaranteed to delegate to the same implementation as bridges for the given function, that's why it's safe to inherit them. + // + // This can still break binary compatibility, but it matches the behavior of the JVM backend. + if (irFunction.isFakeOverride) { + irFunction.overriddenSymbols.asSequence().map { it.owner }.filter { !it.isJvmAbstract }.forEach { override -> + override.allOverridden().mapTo(blacklist) { it.jvmMethod } + } + } + + generated.values.filter { it.signature !in blacklist }.forEach { irClass.addBridge(it, bridgeTarget) } + } + + // Returns the special bridge overridden by the current methods if it exists. + private val IrSimpleFunction.specialBridgeOrNull: SpecialBridge? + get() = specialBridgeCache.getOrPutNullable(symbol) { + val specialMethodInfo = specialBridgeMethods.getSpecialMethodInfo(this) + return when { + specialMethodInfo != null -> + // Note that there are type-safe special bridges with generic return types in Java (namely Map.getOrDefault, + // Map.get, and MutableMap.remove), but the JVM backend does not produce overrides with specialized return + // types for them. So for compatibility, neither do we. + SpecialBridge(this, jvmMethod, methodInfo = specialMethodInfo) + + specialBridgeMethods.isBuiltInWithDifferentJvmName(this) -> + if (returnType.isTypeParameter()) + SpecialBridge(this, jvmMethod, specializedReturnType = returnType) + else + SpecialBridge(this, jvmMethod) + + else -> { + val overriddenSpecialBridge = overriddenSymbols.asSequence().mapNotNull { it.owner.specialBridgeOrNull }.firstOrNull() + if (overriddenSpecialBridge?.specializedReturnType != null) { + val specializedSignature = Method( + overriddenSpecialBridge.signature.name, + context.methodSignatureMapper.mapReturnType(this), + overriddenSpecialBridge.signature.argumentTypes + ) + overriddenSpecialBridge.copy(signature = specializedSignature, specializedReturnType = returnType) + } else { + overriddenSpecialBridge + } + } + } + } private val specialBridgeMethods = SpecialBridgeMethods(context) + private val specialBridgeCache = mutableMapOf() - override fun lower(irClass: IrClass) { - if (irClass.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) { - return - } - - for (member in irClass.declarations.filterIsInstance()) { - if (!irClass.isInterface || member.hasJvmDefault()) - createBridges(member) - } + private fun IrSimpleFunction.overriddenFinalSpecialBridges(): List = allOverridden().mapNotNullTo(mutableListOf()) { + // Ignore special bridges in interfaces or Java classes. While we never generate special bridges in Java + // classes, we may generate special bridges in interfaces for methods annotated with @JvmDefault. + // However, these bridges are not final and are thus safe to override. + // + // This matches the behavior of the JVM backend, but it's probably a bad idea since this is an + // opportunity for a Java and Kotlin implementation of the same interface to go out of sync. + if (it.parentAsClass.isInterface || it.comesFromJava()) + null + else + it.specialBridgeOrNull?.signature?.takeIf { bridgeSignature -> bridgeSignature != it.jvmMethod } } - - private fun createBridges(irFunction: IrSimpleFunction) { - if (irFunction.isStatic) return - if (irFunction.isMethodOfAny()) return - - if (irFunction.origin === IrDeclarationOrigin.FAKE_OVERRIDE && - irFunction.overriddenSymbols.all { - !it.owner.comesFromJava() && - if ((it.owner.parent as? IrClass)?.isInterface == true) - it.owner.hasJvmDefault() // TODO: Remove this after modality is corrected in InterfaceLowering. - else - it.owner.modality !== Modality.ABSTRACT - } - ) { - // All needed bridges will be generated where functions are implemented. - return + private fun IrClass.addAbstractMethodStub(irFunction: IrSimpleFunction) = + addFunction { + updateFrom(irFunction) + modality = Modality.ABSTRACT + origin = IrDeclarationOrigin.DEFINED + name = irFunction.name + returnType = irFunction.returnType + }.apply { + dispatchReceiverParameter = thisReceiver?.copyTo(this, type = defaultType) + extensionReceiverParameter = irFunction.extensionReceiverParameter?.copyTo(this) + valueParameters = irFunction.valueParameters.map { it.copyTo(this) } } - val irClass = irFunction.parentAsClass - val ourSignature = irFunction.getJvmSignature() - val ourMethodName = ourSignature.name - - val (specialOverride, specialOverrideInfo) = - specialBridgeMethods.findSpecialWithOverride(irFunction) ?: Pair(null, null) - val specialOverrideSignature = specialOverride?.getJvmSignature() - - var targetForCommonBridges = irFunction - - // Special case: fake override redirecting to an implementation with a different JVM name, - // or to a function with SpecialOverrideSignature. - // TODO: we assume here that all implementations come from classes. There may be a default implementation in - // an interface, If it comes from the same module, InterfaceDelegationLowering will build a redirection, and the following code will work. - // But in an imported module, there will be no redirection => failure! - if (irFunction.origin === IrDeclarationOrigin.FAKE_OVERRIDE && - irFunction.modality !== Modality.ABSTRACT && - irFunction.visibility !== Visibilities.INVISIBLE_FAKE && - irFunction.overriddenInClasses().firstOrNull { it.getJvmSignature() != ourSignature || it.origin != IrDeclarationOrigin.FAKE_OVERRIDE } - ?.let { (it.getJvmName() != ourMethodName || it.getJvmSignature() == specialOverrideSignature) && it.isExternalDeclaration() } == true - ) { - val resolved = irFunction.findConcreteSuperDeclaration()!! - val resolvedSignature = resolved.getJvmSignature() - if (!resolvedSignature.sameCallAs(ourSignature)) { - val bridge = createBridgeHeader(irClass, resolved, irFunction, isSpecial = false, isSynthetic = false) - bridge.createBridgeBody(resolved, null, isSpecial = false, invokeStatically = true) - irClass.declarations.add(bridge) - targetForCommonBridges = bridge - } - } else if (irFunction.origin == IrDeclarationOrigin.FAKE_OVERRIDE && - irFunction.modality == Modality.ABSTRACT && - irFunction.overriddenSymbols.all { it.owner.getJvmName() != ourMethodName } - ) { - // Bridges for abstract fake overrides whose JVM names differ from overridden functions. - // The orphaned copy will get irFunction's name; the original irFunction will be renamed - // according to its overrides, - val bridge = irFunction.orphanedCopy() - irClass.declarations.add(bridge) - targetForCommonBridges = bridge + private fun IrClass.addBridge(bridge: Bridge, target: IrSimpleFunction): IrSimpleFunction = + addFunction { + modality = Modality.OPEN + origin = IrDeclarationOrigin.BRIDGE + // Internal functions can be overridden by non-internal functions, which changes their names since the names of internal + // functions are mangled. In order to avoid mangling the name twice we reset the visibility for bridges to internal + // functions to public and use the mangled name directly. + visibility = bridge.overridden.visibility.takeUnless { it == Visibilities.INTERNAL } ?: Visibilities.PUBLIC + name = Name.identifier(bridge.signature.name) + returnType = bridge.overridden.returnType.eraseTypeParameters() + isSuspend = bridge.overridden.isSuspend + }.apply { + copyParametersWithErasure(this@addBridge, bridge.overridden) + body = context.createIrBuilder(symbol).run { irExprBody(delegatingCall(this@apply, target)) } + overriddenSymbols += bridge.overridden.symbol } - val signaturesToSkip = - if (irFunction.origin != IrDeclarationOrigin.FAKE_OVERRIDE || irFunction.modality == Modality.ABSTRACT || targetForCommonBridges != irFunction) - mutableSetOf(ourSignature) - else - mutableSetOf() - - signaturesToSkip += getFinalOverridden(irFunction).map { it.getJvmSignature() } - - val firstOverridden = irFunction.overriddenInClasses().firstOrNull() - val firstOverriddenSignature = firstOverridden?.getJvmSignature() - - val renamedOverridden = getRenamedOverridden(irFunction) - if (renamedOverridden != null) { - val renamer = irFunction.copyRenamingTo(Name.identifier(renamedOverridden.getJvmName())) - // Renaming bridge may have already been generated in parent. - if (firstOverridden == null || firstOverriddenSignature!!.name != ourMethodName || getRenamedOverridden(firstOverridden) == null) { - addBridge( - irClass, targetForCommonBridges, renamer, signaturesToSkip, - specialOverrideInfo = null, - isSpecial = true - ) - } else { - // Renamer bridge in superclass. - signaturesToSkip.add(renamer.getJvmSignature()) - } - } - - if (specialOverride != null && (firstOverridden == null || firstOverriddenSignature != ourSignature) && - specialOverrideSignature !in signaturesToSkip - ) { - addBridge( - irClass, targetForCommonBridges, specialOverride, signaturesToSkip, - specialOverrideInfo, - isSpecial = true + private fun IrClass.addSpecialBridge(specialBridge: SpecialBridge, target: IrSimpleFunction): IrSimpleFunction = + addFunction { + // FIXME: This seems to be a work-around for superfluous specialized collection stubs + modality = if (!target.isCollectionStub()) Modality.FINAL else Modality.OPEN + origin = IrDeclarationOrigin.BRIDGE_SPECIAL + name = Name.identifier(specialBridge.signature.name) + returnType = specialBridge.specializedReturnType ?: specialBridge.overridden.returnType.eraseTypeParameters() + }.apply { + copyParametersWithErasure( + this@addSpecialBridge, + specialBridge.overridden, + specialBridge.methodInfo?.needsArgumentBoxing == true ) - } - - // Deal with existing function that override special bridge methods. - if (!irFunction.isFakeOverride && specialOverride != null) { - irFunction.rewriteSpecialMethodBody(ourSignature, specialOverrideSignature!!, specialOverrideInfo!!) - } - - val bridgeSignatures = generateBridges( - FunctionHandleForIrFunction(irFunction), - { handle -> SignatureWithSource(handle.irFunction.getJvmSignature(), handle.irFunction) } - ) - - for (bridgeSignature in bridgeSignatures) { - val method = bridgeSignature.from.source - addBridge( - irClass, targetForCommonBridges, method, signaturesToSkip, - specialOverrideInfo = null, - isSpecial = targetForCommonBridges.isCollectionStub() - ) - } - } - - private fun addBridge( - irClass: IrClass, - target: IrSimpleFunction, - method: IrSimpleFunction, - signaturesToSkip: MutableSet, - specialOverrideInfo: SpecialMethodWithDefaultInfo?, - isSpecial: Boolean - ) { - val signature = method.getJvmSignature() - if (signature in signaturesToSkip) return - - val bridge = createBridgeHeader(irClass, target, method, isSpecial = isSpecial, isSynthetic = !isSpecial) - bridge.createBridgeBody(target, specialOverrideInfo, isSpecial) - irClass.declarations.add(bridge) - - // For lambda classes, we move override from the `invoke` function to its bridge. This will allow us to avoid boxing - // the return type of `invoke` in codegen, in case lambda's return type is primitive. - if (method.name == OperatorNameConventions.INVOKE && irClass.origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL) { - target.overriddenSymbols = target.overriddenSymbols.filter { it != method.symbol } - bridge.overriddenSymbols += method.symbol - } - - signaturesToSkip.add(signature) - } - - private fun IrSimpleFunction.copyRenamingTo(newName: Name): IrSimpleFunction = - WrappedSimpleFunctionDescriptor(descriptor.annotations).let { newDescriptor -> - IrFunctionImpl( - startOffset, endOffset, origin, - IrSimpleFunctionSymbolImpl(newDescriptor), - newName, - visibility, modality, returnType, - isInline = isInline, isExternal = isExternal, isTailrec = isTailrec, isSuspend = isSuspend, isExpect = isExpect, - isFakeOverride = origin == IrDeclarationOrigin.FAKE_OVERRIDE, - isOperator = isOperator - ).apply { - newDescriptor.bind(this) - parent = this@copyRenamingTo.parent - dispatchReceiverParameter = this@copyRenamingTo.dispatchReceiverParameter?.copyTo(this) - extensionReceiverParameter = this@copyRenamingTo.extensionReceiverParameter?.copyTo(this) - valueParameters = this@copyRenamingTo.valueParameters.map { it.copyTo(this) } + body = context.createIrBuilder(symbol).irBlockBody { + specialBridge.methodInfo?.let { info -> + valueParameters.take(info.argumentsToCheck).forEach { + +parameterTypeCheck(it, target.valueParameters[it.index].type, info.defaultValueGenerator(this@apply)) + } + } + +irReturn(delegatingCall(this@apply, target, specialBridge.superQualifierSymbol)) } + overriddenSymbols += specialBridge.overridden.symbol } - private fun createBridgeHeader( - irClass: IrClass, - target: IrSimpleFunction, - signatureFunction: IrSimpleFunction, - isSpecial: Boolean, - isSynthetic: Boolean - ): IrSimpleFunction { - val modality = if (isSpecial && !target.isCollectionStub()) Modality.FINAL else Modality.OPEN - val origin = if (isSynthetic) IrDeclarationOrigin.BRIDGE else IrDeclarationOrigin.BRIDGE_SPECIAL - - val visibility = if (signatureFunction.visibility === Visibilities.INTERNAL) Visibilities.PUBLIC else signatureFunction.visibility - val descriptor = WrappedSimpleFunctionDescriptor() - return IrFunctionImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, - origin, - IrSimpleFunctionSymbolImpl(descriptor), - Name.identifier(signatureFunction.getJvmName()), - visibility, - modality, - returnType = signatureFunction.returnType.eraseTypeParameters(), - isInline = false, - isExternal = false, - isTailrec = false, - isSuspend = signatureFunction.isSuspend, - isExpect = false, - isFakeOverride = origin == IrDeclarationOrigin.FAKE_OVERRIDE, - isOperator = false - ).apply { - descriptor.bind(this) - parent = irClass - copyTypeParametersFrom(target) - - // Have to specify type explicitly to prevent an attempt to remap it. - dispatchReceiverParameter = irClass.thisReceiver?.copyTo(this, type = irClass.defaultType) - extensionReceiverParameter = signatureFunction.extensionReceiverParameter - ?.copyWithTypeErasure(this) - valueParameters = signatureFunction.valueParameters.map { param -> - param.copyWithTypeErasure(this) - } - } - } - - private fun IrStatementsBuilder.addParameterTypeCheck( - parameter: IrValueParameter, - type: IrType, - defaultValueGenerator: ((IrSimpleFunction) -> IrExpression), - function: IrSimpleFunction - ) { - +irIfThen( - context.irBuiltIns.unitType, - irNot(irIs(irGet(parameter), type)), - irReturn(defaultValueGenerator(function)) - ) - } - private fun IrSimpleFunction.rewriteSpecialMethodBody( ourSignature: Method, specialOverrideSignature: Method, @@ -311,11 +403,10 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass val newParameter = it.copyTo(this@rewriteSpecialMethodBody, type = context.irBuiltIns.anyNType) variableMap.put(valueParameters[it.index], newParameter) newValueParameters[it.index] = newParameter - addParameterTypeCheck( + +parameterTypeCheck( newParameter, parameterType, - specialOverrideInfo.defaultValueGenerator, - this@rewriteSpecialMethodBody + specialOverrideInfo.defaultValueGenerator(this@rewriteSpecialMethodBody) ) } } @@ -346,173 +437,50 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass if (variableMap.isNotEmpty()) { body?.transform(VariableRemapper(variableMap), null) } - } - private fun IrSimpleFunction.createBridgeBody( + private fun IrBuilderWithScope.parameterTypeCheck(parameter: IrValueParameter, type: IrType, defaultValue: IrExpression) = + irIfThen(context.irBuiltIns.unitType, irNot(irIs(irGet(parameter), type)), irReturn(defaultValue)) + + private fun IrSimpleFunction.copyParametersWithErasure(irClass: IrClass, from: IrSimpleFunction, forceArgumentBoxing: Boolean = false) { + // This is a workaround for a bug affecting fake overrides. Sometimes we encounter fake overrides + // with dispatch receivers pointing at a superclass instead of the current class. + dispatchReceiverParameter = irClass.thisReceiver?.copyTo(this, type = irClass.defaultType) + extensionReceiverParameter = from.extensionReceiverParameter?.copyWithTypeErasure(this, forceArgumentBoxing) + valueParameters = from.valueParameters.map { it.copyWithTypeErasure(this, forceArgumentBoxing) } + } + + private fun IrValueParameter.copyWithTypeErasure(target: IrSimpleFunction, forceBoxing: Boolean = false): IrValueParameter = copyTo( + target, IrDeclarationOrigin.BRIDGE, + type = + // SuspendFunction{N} is Function{N+1} at runtime, thus, when we generate a bridge for suspend callable references, + // we need to replace the type of its continuation parameter with Any? + if (target.isSuspend && type.eraseTypeParameters().getClass() + ?.fqNameWhenAvailable == DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME_RELEASE + ) context.irBuiltIns.anyNType else type.eraseTypeParameters().let { if (forceBoxing) it.makeNullable() else it }, + varargElementType = varargElementType?.eraseTypeParameters() + ) + + private fun IrBuilderWithScope.delegatingCall( + bridge: IrSimpleFunction, target: IrSimpleFunction, - specialMethodInfo: SpecialMethodWithDefaultInfo?, - isSpecial: Boolean, - invokeStatically: Boolean = false - ) { - context.createIrBuilder(symbol).run { - body = irBlockBody { - if (specialMethodInfo != null) { - valueParameters.take(specialMethodInfo.argumentsToCheck).forEach { - addParameterTypeCheck( - it, - target.valueParameters[it.index].type, - specialMethodInfo.defaultValueGenerator, - this@createBridgeBody - ) - } - } - +irReturn( - irImplicitCast( - IrCallImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, - target.returnType, - target.symbol, origin = IrStatementOrigin.BRIDGE_DELEGATION, - superQualifierSymbol = if (invokeStatically) target.parentAsClass.symbol else null - ).apply { - passTypeArgumentsFrom(this@createBridgeBody) - dispatchReceiver = irGet(dispatchReceiverParameter!!) - extensionReceiverParameter?.let { - extensionReceiver = irImplicitCast(irGet(it), target.extensionReceiverParameter!!.type) - } - valueParameters.forEach { - putValueArgument(it.index, irImplicitCast(irGet(it), target.valueParameters[it.index].type)) - } - }, - returnType - ) - ) - } + superQualifierSymbol: IrClassSymbol? = null + ) = irCastIfNeeded(irCall(target, origin = IrStatementOrigin.BRIDGE_DELEGATION, superQualifierSymbol = superQualifierSymbol).apply { + for ((param, targetParam) in bridge.explicitParameters.zip(target.explicitParameters)) { + putArgument(targetParam, irGet(param).let { argument -> + if (param == bridge.dispatchReceiverParameter) argument else irCastIfNeeded(argument, targetParam.type) + }) } - } + }, bridge.returnType) - /* A hacky way to make sure the code generator calls the right function, and not some standard interface it implements. */ - private fun IrSimpleFunction.orphanedCopy() = - if (overriddenSymbols.size == 0) - this - else - WrappedSimpleFunctionDescriptor(descriptor.annotations).let { wrappedDescriptor -> - val newOrigin = if (origin == IrDeclarationOrigin.FAKE_OVERRIDE) IrDeclarationOrigin.DEFINED else origin - IrFunctionImpl( - startOffset, endOffset, newOrigin, - IrSimpleFunctionSymbolImpl(wrappedDescriptor), - Name.identifier(getJvmName()), - visibility, modality, returnType, - isInline = isInline, isExternal = isExternal, isTailrec = isTailrec, isSuspend = isSuspend, isExpect = isExpect, - isFakeOverride = newOrigin == IrDeclarationOrigin.FAKE_OVERRIDE, - isOperator = isOperator - ).apply { - wrappedDescriptor.bind(this) - parent = this@orphanedCopy.parent - copyTypeParametersFrom(this@orphanedCopy) - this@orphanedCopy.dispatchReceiverParameter?.let { dispatchReceiverParameter = it.copyTo(this) } - this@orphanedCopy.extensionReceiverParameter?.let { extensionReceiverParameter = it.copyTo(this) } - valueParameters = this@orphanedCopy.valueParameters.map { it.copyTo(this) } - /* Do NOT copy overriddenSymbols */ - } - } + private fun IrBuilderWithScope.irCastIfNeeded(expression: IrExpression, to: IrType): IrExpression = + if (expression.type == to || to.isAny() || to.isNullableAny()) expression else irImplicitCast(expression, to) - private fun IrValueParameter.copyWithTypeErasure(target: IrSimpleFunction): IrValueParameter { - val descriptor = if (this.descriptor is ReceiverParameterDescriptor) { - WrappedReceiverParameterDescriptor(this.descriptor.annotations) - } else { - WrappedValueParameterDescriptor(this.descriptor.annotations) - } - return IrValueParameterImpl( - UNDEFINED_OFFSET, UNDEFINED_OFFSET, - IrDeclarationOrigin.BRIDGE, - IrValueParameterSymbolImpl(descriptor), - name, - index, - // SuspendFunction{N} is Function{N+1} at runtime, thus, when we generate a bridge for suspend callable reference, - // we need to replace type of its continuation parameter with Any? - if (target.isSuspend && type.eraseTypeParameters().getClass() - ?.fqNameWhenAvailable == DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME_RELEASE - ) context.irBuiltIns.anyNType else type.eraseTypeParameters(), - varargElementType?.eraseTypeParameters(), - isCrossinline, - isNoinline - ).apply { - descriptor.bind(this) - parent = target - } - } - - private fun IrSimpleFunction.findAllReachableDeclarations() = - findAllReachableDeclarations(FunctionHandleForIrFunction(this)).map { it.irFunction } - - private fun getFinalOverridden(irFunction: IrSimpleFunction): List { - return irFunction.findAllReachableDeclarations().filter { it.modality === Modality.FINAL } - } - - // There are two sources of method name change: - // 1. Special methods renamed from java - // 2. Internal methods overridden by public ones. - // Here, we want to only deal with the first case. - private fun getRenamedOverridden(irFunction: IrSimpleFunction): IrSimpleFunction? { - val ourName = irFunction.getJvmName() - return irFunction.allOverridden().firstOrNull { - it.visibility == Visibilities.PUBLIC && it.getJvmName() != ourName - } - } - - - private inner class FunctionHandleForIrFunction(val irFunction: IrSimpleFunction) : FunctionHandle { - override val isDeclaration get() = irFunction.origin != IrDeclarationOrigin.FAKE_OVERRIDE || irFunction.findInterfaceImplementation() != null - override val isAbstract get() = irFunction.modality == Modality.ABSTRACT - override val mayBeUsedAsSuperImplementation get() = !irFunction.parentAsClass.isInterface || irFunction.hasJvmDefault() - - override fun getOverridden() = irFunction.overriddenSymbols.map { FunctionHandleForIrFunction(it.owner) } - - override fun hashCode(): Int = - irFunction.parent.safeAs()?.fqNameWhenAvailable.hashCode() + 31 * irFunction.getJvmSignature().hashCode() - - override fun equals(other: Any?): Boolean = - other is FunctionHandleForIrFunction && - irFunction.parent.safeAs()?.fqNameWhenAvailable == other.irFunction.parent.safeAs()?.fqNameWhenAvailable && - irFunction.getJvmSignature() == other.irFunction.getJvmSignature() - - override fun toString(): String = - irFunction.render() - } - - fun IrSimpleFunction.findConcreteSuperDeclaration(): IrSimpleFunction? { - return findConcreteSuperDeclaration(FunctionHandleForIrFunction(this))?.irFunction - } - - private fun IrFunction.getJvmSignature(): Method = methodSignatureMapper.mapAsmMethod(this) - private fun IrFunction.getJvmName(): String = getJvmSignature().name + private val IrFunction.jvmMethod: Method + get() = context.methodSignatureMapper.mapAsmMethod(this) } -private data class SignatureWithSource(val signature: Method, val source: IrSimpleFunction) { - override fun hashCode(): Int { - return signature.hashCode() - } - - override fun equals(other: Any?): Boolean { - return other is SignatureWithSource && signature == other.signature - } -} - - -fun IrSimpleFunction.overriddenInClasses(): Sequence = - allOverridden().filterNot(IrDeclaration::hasInterfaceParent) - -fun IrSimpleFunction.isCollectionStub(): Boolean = +private fun IrSimpleFunction.isCollectionStub(): Boolean = origin == IrDeclarationOrigin.IR_BUILTINS_STUB -val EXTERNAL_ORIGIN = setOf(IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB, IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB) - -fun IrDeclaration.comesFromJava() = parentAsClass.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB - -fun IrDeclaration.isExternalDeclaration() = parentAsClass.origin in EXTERNAL_ORIGIN - -// Method has the same name, same arguments as `other`. Return types may differ. -fun Method.sameCallAs(other: Method) = - name == other.name && - argumentTypes?.contentEquals(other.argumentTypes) == true +private fun IrDeclaration.comesFromJava() = parentAsClass.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt index 3c792e20d97..54dcb34cd2e 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt @@ -268,12 +268,12 @@ fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol, descriptor: FunctionDesc fun IrBuilderWithScope.irCall(callee: IrFunction): IrFunctionAccessExpression = irCall(callee.symbol) -fun IrBuilderWithScope.irCall(callee: IrFunction, origin: IrStatementOrigin): IrCall = +fun IrBuilderWithScope.irCall(callee: IrFunction, origin: IrStatementOrigin, superQualifierSymbol: IrClassSymbol? = null): IrCall = IrCallImpl( startOffset, endOffset, callee.returnType, callee.symbol as IrSimpleFunctionSymbol, callee.typeParameters.size, callee.valueParameters.size, - origin + origin, superQualifierSymbol ) fun IrBuilderWithScope.irDelegatingConstructorCall(callee: IrConstructor): IrDelegatingConstructorCall = diff --git a/compiler/testData/codegen/box/builtinStubMethods/mapRemove/noDefaultImpls.kt b/compiler/testData/codegen/box/builtinStubMethods/mapRemove/noDefaultImpls.kt index 25d21f2bd77..437d064d7f4 100644 --- a/compiler/testData/codegen/box/builtinStubMethods/mapRemove/noDefaultImpls.kt +++ b/compiler/testData/codegen/box/builtinStubMethods/mapRemove/noDefaultImpls.kt @@ -1,7 +1,6 @@ // !JVM_DEFAULT_MODE: enable // IGNORE_BACKEND_FIR: JVM_IR // SKIP_JDK6 -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // JVM_TARGET: 1.8 // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/delegation/delegationToMap.kt b/compiler/testData/codegen/box/delegation/delegationToMap.kt index f6a841cc6dd..90209f96516 100644 --- a/compiler/testData/codegen/box/delegation/delegationToMap.kt +++ b/compiler/testData/codegen/box/delegation/delegationToMap.kt @@ -1,6 +1,5 @@ // IGNORE_BACKEND_FIR: JVM_IR // SKIP_JDK6 -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // FULL_JDK @@ -24,4 +23,4 @@ fun box(): String { if (!test.remove("O", "K")) return "fail 2: entry wasn't removed" return test.getOrDefault("absent", "OK") -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/box/inlineClasses/functionNameMangling/overridingMethodInGenericClass2.kt b/compiler/testData/codegen/box/inlineClasses/functionNameMangling/overridingMethodInGenericClass2.kt index 7e3e4dc95a7..ecd79887a64 100644 --- a/compiler/testData/codegen/box/inlineClasses/functionNameMangling/overridingMethodInGenericClass2.kt +++ b/compiler/testData/codegen/box/inlineClasses/functionNameMangling/overridingMethodInGenericClass2.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +InlineClasses // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JVM_IR abstract class GenericBase { abstract fun foo(x: T): T diff --git a/compiler/testData/codegen/box/jvm8/treeMapBridge.kt b/compiler/testData/codegen/box/jvm8/treeMapBridge.kt index acd42ed5e16..4dc738863b6 100644 --- a/compiler/testData/codegen/box/jvm8/treeMapBridge.kt +++ b/compiler/testData/codegen/box/jvm8/treeMapBridge.kt @@ -1,6 +1,5 @@ // IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM -// IGNORE_BACKEND: JVM_IR // FULL_JDK // JVM_TARGET: 1.8 diff --git a/compiler/testData/codegen/box/specialBuiltins/commonBridgesTarget.kt b/compiler/testData/codegen/box/specialBuiltins/commonBridgesTarget.kt index d78e2c3d2ea..638ea3eca78 100644 --- a/compiler/testData/codegen/box/specialBuiltins/commonBridgesTarget.kt +++ b/compiler/testData/codegen/box/specialBuiltins/commonBridgesTarget.kt @@ -1,6 +1,5 @@ // IGNORE_BACKEND_FIR: JVM_IR // KJS_WITH_FULL_RUNTIME -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: NATIVE open class Base() : HashSet() { diff --git a/compiler/testData/codegen/box/specialBuiltins/removeAtTwoSpecialBridges.kt b/compiler/testData/codegen/box/specialBuiltins/removeAtTwoSpecialBridges.kt index 6245ac90001..8e214c0dec1 100644 --- a/compiler/testData/codegen/box/specialBuiltins/removeAtTwoSpecialBridges.kt +++ b/compiler/testData/codegen/box/specialBuiltins/removeAtTwoSpecialBridges.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND_FIR: JVM_IR -// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: NATIVE open class A0 : MutableList { diff --git a/compiler/testData/codegen/bytecodeText/builtinFunctions/removeAt.kt b/compiler/testData/codegen/bytecodeText/builtinFunctions/removeAt.kt index 998eb803120..849e6cb2022 100644 --- a/compiler/testData/codegen/bytecodeText/builtinFunctions/removeAt.kt +++ b/compiler/testData/codegen/bytecodeText/builtinFunctions/removeAt.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR abstract class A1 : MutableList { override fun remove(x: T): Boolean = true override fun removeAt(index: Int): T = null!! diff --git a/compiler/testData/writeSignature/java8/mutableMapRemove.kt b/compiler/testData/writeSignature/java8/mutableMapRemove.kt index 60c8aff82df..f0a3eb65638 100644 --- a/compiler/testData/writeSignature/java8/mutableMapRemove.kt +++ b/compiler/testData/writeSignature/java8/mutableMapRemove.kt @@ -1,8 +1,5 @@ //FULL_JDK -// In BridgeLowering, no concrete overwrite is found for getOrDefault. -// IGNORE_BACKEND: JVM_IR - class KotlinMap1 : java.util.AbstractMap() { override val entries: MutableSet> get() = throw UnsupportedOperationException()