From 261af10019ddb4c45c4e5a5127e0453c8b1bcee6 Mon Sep 17 00:00:00 2001 From: Mikhail Bogdanov Date: Mon, 30 Mar 2020 09:29:45 +0200 Subject: [PATCH] JVM.IR. Add assertions for JVM defaults checks --- .../jvm/annotations/jvmAnnotationUtil.kt | 5 ++++ .../kotlin/backend/jvm/ir/IrUtils.kt | 23 ++++++++++++++----- ...nheritedDefaultMethodsOnClassesLowering.kt | 15 ++++++++---- .../backend/jvm/lower/InterfaceLowering.kt | 3 +-- .../jvm/lower/SyntheticAccessorLowering.kt | 4 ++-- 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt index da58ad13c0b..a01f88b9816 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt @@ -53,6 +53,11 @@ fun CallableMemberDescriptor.isCompiledToJvmDefaultIfNoAbstract(jvmDefault: JvmD val clazz = directMember.containingDeclaration +// TODO add checks after fixes in diagnostics +// assert(this.kind.isReal && isInterface(clazz) && modality != Modality.ABSTRACT) { +// "`isCompiledToJvmDefault` should be called on non-fakeoverrides and non-abstract methods from interfaces $this" +// } + if (directMember.annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME)) return true if (clazz !is DeserializedClassDescriptor) return jvmDefault.forAllMethodsWithBody return JvmProtoBufUtil.isNewPlaceForBodyGeneration(clazz.classProto) 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 8ec0bc33542..ab0bd35be08 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 @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.backend.jvm.ir +import org.jetbrains.kotlin.backend.common.ir.ir2string import org.jetbrains.kotlin.backend.common.lower.IrLoweringContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin @@ -147,10 +148,17 @@ fun IrDeclaration.getJvmNameFromAnnotation(): String? { val IrFunction.propertyIfAccessor: IrDeclaration get() = (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: this -fun IrFunction.isCompiledToJvmDefault(jvmDefaultMode: JvmDefaultMode): Boolean { +fun IrFunction.isSimpleFunctionCompiledToJvmDefault(jvmDefaultMode: JvmDefaultMode): Boolean { + return (this as? IrSimpleFunction)?.isCompiledToJvmDefault(jvmDefaultMode) == true +} + +fun IrSimpleFunction.isCompiledToJvmDefault(jvmDefaultMode: JvmDefaultMode): Boolean { + assert(!isFakeOverride && parentAsClass.isInterface && modality != Modality.ABSTRACT) { + "`isCompiledToJvmDefault` should be called on non-fakeoverrides and non-abstract methods from interfaces ${ir2string(this)}" + } if (hasJvmDefault()) return true val parentDescriptor = propertyIfAccessor.parentAsClass.descriptor - if (parentDescriptor !is DeserializedClassDescriptor) return jvmDefaultMode.forAllMehtodsWithBody + if (parentDescriptor !is DeserializedClassDescriptor) return jvmDefaultMode.forAllMethodsWithBody return JvmProtoBufUtil.isNewPlaceForBodyGeneration(parentDescriptor.classProto) } @@ -286,10 +294,13 @@ fun IrFunctionAccessExpression.copyFromWithPlaceholderTypeArguments(existingCall // 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. -fun IrSimpleFunction.isJvmAbstract(jvmDefaultMode: JvmDefaultMode): Boolean = (modality == Modality.ABSTRACT) || - (parentAsClass.isJvmInterface && resolveFakeOverride()?.run { !isCompiledToJvmDefault(jvmDefaultMode) && !hasPlatformDependent() } ?: true) +// are abstract unless they are annotated @PlatformDependent or compiled to JVM default (with @JvmDefault annotation or without) +// or they override such method. +fun IrSimpleFunction.isJvmAbstract(jvmDefaultMode: JvmDefaultMode): Boolean { + if (modality == Modality.ABSTRACT) return true + if (!parentAsClass.isJvmInterface) return false + return resolveFakeOverride()?.run { !isCompiledToJvmDefault(jvmDefaultMode) && !hasPlatformDependent() } != false +} fun firstSuperMethodFromKotlin( override: IrSimpleFunction, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt index 77a48f63a7b..83fac0e447a 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InheritedDefaultMethodsOnClassesLowering.kt @@ -153,7 +153,7 @@ private class InterfaceDefaultCallsLowering(val context: JvmBackendContext) : Ir if (!callee.hasInterfaceParent() || callee.origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER || - callee.isCompiledToJvmDefault(context.state.jvmDefaultMode) + callee.isSimpleFunctionCompiledToJvmDefault(context.state.jvmDefaultMode) ) { return super.visitCall(expression) } @@ -171,14 +171,19 @@ private class InterfaceDefaultCallsLowering(val context: JvmBackendContext) : Ir } } -private fun IrSimpleFunction.isDefinitelyNotDefaultImplsMethod(jvmDefaultMode: JvmDefaultMode) = - resolveFakeOverride()?.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB || - origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER || +private fun IrSimpleFunction.isDefinitelyNotDefaultImplsMethod(jvmDefaultMode: JvmDefaultMode): Boolean { + if (resolveFakeOverride()?.run { + origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB || isCompiledToJvmDefault( + jvmDefaultMode + ) + } != false) return true + + return origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER || hasAnnotation(PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME) || - isCompiledToJvmDefault(jvmDefaultMode) || (name.asString() == "clone" && parent.safeAs()?.fqNameWhenAvailable?.asString() == "kotlin.Cloneable" && valueParameters.isEmpty()) +} internal val interfaceObjectCallsPhase = makeIrFilePhase( lowering = ::InterfaceObjectCallsLowering, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InterfaceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InterfaceLowering.kt index be0a0e48144..bf76cc2213e 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InterfaceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InterfaceLowering.kt @@ -23,7 +23,6 @@ import org.jetbrains.kotlin.ir.expressions.IrReturn import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrLocalDelegatedPropertySymbol -import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid @@ -78,7 +77,7 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran * - the implementation is private, or belongs to java.lang.Object, * or is a stub for function with default parameters ($default) * - we're in -Xjvm-default=compatibility|all-compatibility mode, in which case we go via - * accessors on the parent class rather than the DefaultImpls if inherited method compiled to JVM default + * accessors on the parent class rather than the DefaultImpls if inherited method is compiled to JVM default * - we're in -Xjvm-default=enable|all mode, and we have that default implementation, * in which case we simply leave it. * diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt index 1360221b743..d089117a873 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt @@ -493,7 +493,7 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle } } - private fun IrFunction.accessorName(superQualifier: IrClassSymbol?): Name { + private fun IrSimpleFunction.accessorName(superQualifier: IrClassSymbol?): Name { val jvmName = context.methodSignatureMapper.mapFunctionName(this) val suffix = when { // Accessors for top level functions never need a suffix. @@ -501,7 +501,7 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle // The only function accessors placed on interfaces are for private functions and JvmDefault implementations. // The two cannot clash. - parentAsClass.isJvmInterface -> if (!Visibilities.isPrivate(visibility) && isCompiledToJvmDefault(context.state.jvmDefaultMode)) "\$jd" else "" + parentAsClass.isJvmInterface -> if (!Visibilities.isPrivate(visibility)) "\$jd" else "" // Accessor for _s_uper-qualified call superQualifier != null -> "\$s" + superQualifier.descriptor.syntheticAccessorToSuperSuffix()