Prohibit @JvnName on functions mangled because of return type

This commit is contained in:
Dmitry Petrov
2020-05-15 16:46:55 +03:00
parent fcf8a91a38
commit de4ebe4395
7 changed files with 62 additions and 14 deletions
@@ -10,7 +10,8 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.isInlineClassType import org.jetbrains.kotlin.resolve.isInlineClassType
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameMangling import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForParameterTypes
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForReturnType
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
import java.security.MessageDigest import java.security.MessageDigest
@@ -23,7 +24,7 @@ fun getManglingSuffixBasedOnKotlinSignature(descriptor: CallableMemberDescriptor
// If a function accepts inline class parameters, mangle its name. // If a function accepts inline class parameters, mangle its name.
val actualValueParameterTypes = listOfNotNull(descriptor.extensionReceiverParameter?.type) + descriptor.valueParameters.map { it.type } val actualValueParameterTypes = listOfNotNull(descriptor.extensionReceiverParameter?.type) + descriptor.valueParameters.map { it.type }
if (requiresFunctionNameMangling(actualValueParameterTypes)) { if (requiresFunctionNameManglingForParameterTypes(actualValueParameterTypes)) {
return "-" + md5base64(collectSignatureForMangling(actualValueParameterTypes)) return "-" + md5base64(collectSignatureForMangling(actualValueParameterTypes))
} }
@@ -32,8 +33,7 @@ fun getManglingSuffixBasedOnKotlinSignature(descriptor: CallableMemberDescriptor
// should unwrap it and take original return type instead. // should unwrap it and take original return type instead.
if (descriptor.containingDeclaration is ClassDescriptor) { if (descriptor.containingDeclaration is ClassDescriptor) {
val returnType = descriptor.unwrapInitialDescriptorForSuspendFunction().returnType!! val returnType = descriptor.unwrapInitialDescriptorForSuspendFunction().returnType!!
// NB functions returning all inline classes (including our special 'kotlin.Result') should be mangled. if (requiresFunctionNameManglingForReturnType(returnType)) {
if (returnType.isInlineClassType()) {
return "-" + md5base64(":" + getSignatureElementForMangling(returnType)) return "-" + md5base64(":" + getSignatureElementForMangling(returnType))
} }
} }
@@ -42,7 +42,9 @@ import org.jetbrains.kotlin.resolve.jvm.annotations.findSynchronizedAnnotation
import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmFieldAnnotation import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmFieldAnnotation
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.resolve.jvm.isInlineClassThatRequiresMangling import org.jetbrains.kotlin.resolve.jvm.isInlineClassThatRequiresMangling
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameMangling import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForParameterTypes
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForReturnType
import javax.rmi.CORBA.ClassDesc
class LocalFunInlineChecker : DeclarationChecker { class LocalFunInlineChecker : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
@@ -148,7 +150,8 @@ class JvmNameAnnotationChecker : DeclarationChecker {
if (DescriptorUtils.isOverride(descriptor) || descriptor.isOverridable) { if (DescriptorUtils.isOverride(descriptor) || descriptor.isOverridable) {
diagnosticHolder.report(ErrorsJvm.INAPPLICABLE_JVM_NAME.on(annotationEntry)) diagnosticHolder.report(ErrorsJvm.INAPPLICABLE_JVM_NAME.on(annotationEntry))
} else if (descriptor.containingDeclaration.isInlineClassThatRequiresMangling() || } else if (descriptor.containingDeclaration.isInlineClassThatRequiresMangling() ||
requiresFunctionNameMangling(descriptor.valueParameters.map { it.type }) requiresFunctionNameManglingForParameterTypes(descriptor.valueParameters.map { it.type }) ||
descriptor.containingDeclaration is ClassDescriptor && requiresFunctionNameManglingForReturnType(descriptor.returnType)
) { ) {
diagnosticHolder.report(ErrorsJvm.INAPPLICABLE_JVM_NAME.on(annotationEntry)) diagnosticHolder.report(ErrorsJvm.INAPPLICABLE_JVM_NAME.on(annotationEntry))
} }
@@ -11,3 +11,17 @@ fun bar(f: Foo) {}
@JvmName("good") @JvmName("good")
fun baz(r: Result<Int>) {} fun baz(r: Result<Int>) {}
@JvmName("test")
fun returnsInlineClass() = Foo(1)
@JvmName("test")
fun returnsKotlinResult(a: Result<Int>): Result<Int> = a
class C {
@JvmName("test")
fun returnsInlineClass() = Foo(1)
@JvmName("test")
fun returnsKotlinResult(a: Result<Int>): Result<Int> = a
}
@@ -11,3 +11,17 @@ fun bar(f: Foo) {}
@JvmName("good") @JvmName("good")
fun baz(r: Result<Int>) {} fun baz(r: Result<Int>) {}
@JvmName("test")
fun returnsInlineClass() = Foo(1)
@JvmName("test")
fun returnsKotlinResult(a: Result<Int>): Result<Int> = a
class C {
<!INAPPLICABLE_JVM_NAME!>@JvmName("test")<!>
fun returnsInlineClass() = Foo(1)
<!INAPPLICABLE_JVM_NAME!>@JvmName("test")<!>
fun returnsKotlinResult(a: Result<Int>): Result<Int> = a
}
@@ -2,6 +2,17 @@ package
@kotlin.jvm.JvmName(name = "bad") public fun bar(/*0*/ f: Foo): kotlin.Unit @kotlin.jvm.JvmName(name = "bad") public fun bar(/*0*/ f: Foo): kotlin.Unit
@kotlin.jvm.JvmName(name = "good") public fun baz(/*0*/ r: kotlin.Result<kotlin.Int>): kotlin.Unit @kotlin.jvm.JvmName(name = "good") public fun baz(/*0*/ r: kotlin.Result<kotlin.Int>): kotlin.Unit
@kotlin.jvm.JvmName(name = "test") public fun returnsInlineClass(): Foo
@kotlin.jvm.JvmName(name = "test") public fun returnsKotlinResult(/*0*/ a: kotlin.Result<kotlin.Int>): kotlin.Result<kotlin.Int>
public final class C {
public constructor C()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
@kotlin.jvm.JvmName(name = "test") public final fun returnsInlineClass(): Foo
@kotlin.jvm.JvmName(name = "test") public final fun returnsKotlinResult(/*0*/ a: kotlin.Result<kotlin.Int>): kotlin.Result<kotlin.Int>
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final inline class Foo { public final inline class Foo {
public constructor Foo(/*0*/ x: kotlin.Int) public constructor Foo(/*0*/ x: kotlin.Int)
@@ -272,7 +272,8 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
private fun checkOriginalAndFirTestdataIdentity(testDataFile: File, firTestDataFile: File) { private fun checkOriginalAndFirTestdataIdentity(testDataFile: File, firTestDataFile: File) {
val originalTestData = loadTestDataWithoutDiagnostics(testDataFile) val originalTestData = loadTestDataWithoutDiagnostics(testDataFile)
val firTestData = loadTestDataWithoutDiagnostics(firTestDataFile) val firTestData = loadTestDataWithoutDiagnostics(firTestDataFile)
val message = "Original and fir test data doesn't identical. Please, add changes from ${testDataFile.name} to ${firTestDataFile.name}" val message = "Original and fir test data aren't identical. " +
"Please, add changes from ${testDataFile.name} to ${firTestDataFile.name}"
TestCase.assertEquals(message, originalTestData, firTestData) TestCase.assertEquals(message, originalTestData, firTestData)
} }
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.isInlineClass import org.jetbrains.kotlin.resolve.isInlineClass
import org.jetbrains.kotlin.resolve.isInlineClassType
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
@@ -20,20 +21,24 @@ fun shouldHideConstructorDueToInlineClassTypeValueParameters(descriptor: Callabl
// TODO inner class in inline class // TODO inner class in inline class
return constructorDescriptor.valueParameters.any { it.type.requiresFunctionNameMangling() } return constructorDescriptor.valueParameters.any { it.type.requiresFunctionNameManglingInParameterTypes() }
} }
fun requiresFunctionNameMangling(valueParameterTypes: List<KotlinType>): Boolean { fun requiresFunctionNameManglingForParameterTypes(valueParameterTypes: List<KotlinType>): Boolean {
return valueParameterTypes.any { it.requiresFunctionNameMangling() } return valueParameterTypes.any { it.requiresFunctionNameManglingInParameterTypes() }
} }
// NB functions returning all inline classes (including our special 'kotlin.Result') should be mangled.
fun requiresFunctionNameManglingForReturnType(returnType: KotlinType?) =
returnType != null && returnType.isInlineClassType()
fun DeclarationDescriptor.isInlineClassThatRequiresMangling(): Boolean = fun DeclarationDescriptor.isInlineClassThatRequiresMangling(): Boolean =
isInlineClass() && !isDontMangleClass(this as ClassDescriptor) isInlineClass() && !isDontMangleClass(this as ClassDescriptor)
fun KotlinType.isInlineClassThatRequiresMangling() = fun KotlinType.isInlineClassThatRequiresMangling() =
constructor.declarationDescriptor?.isInlineClassThatRequiresMangling() == true constructor.declarationDescriptor?.isInlineClassThatRequiresMangling() == true
private fun KotlinType.requiresFunctionNameMangling() = private fun KotlinType.requiresFunctionNameManglingInParameterTypes() =
isInlineClassThatRequiresMangling() || isTypeParameterWithUpperBoundThatRequiresMangling() isInlineClassThatRequiresMangling() || isTypeParameterWithUpperBoundThatRequiresMangling()
private fun isDontMangleClass(classDescriptor: ClassDescriptor) = private fun isDontMangleClass(classDescriptor: ClassDescriptor) =
@@ -41,5 +46,5 @@ private fun isDontMangleClass(classDescriptor: ClassDescriptor) =
private fun KotlinType.isTypeParameterWithUpperBoundThatRequiresMangling(): Boolean { private fun KotlinType.isTypeParameterWithUpperBoundThatRequiresMangling(): Boolean {
val descriptor = constructor.declarationDescriptor as? TypeParameterDescriptor ?: return false val descriptor = constructor.declarationDescriptor as? TypeParameterDescriptor ?: return false
return descriptor.representativeUpperBound.requiresFunctionNameMangling() return descriptor.representativeUpperBound.requiresFunctionNameManglingInParameterTypes()
} }