Support cross-module usages of @JvmRecord classes

The problem is that JvmRecord has SOURCE retention
Probably, increasing its retention might be a more reliable solution
(or in some other way serializing that the class is a record)

Just checking supertypes seems like a reasonable approximation:
only records kotlin are allowed to extend j.l.Record.
But the relevant diagnostic has been added only since 1.4.30,
so potentially there could have been exist a non-record class with
such supertype compiled by 1.4.20, but this case seems to be ill-formed
and marginal anyway.

For Java classes, it's irrelevant since they don't have member properties
(only synthetic extensions)

^KT-43677 In Progress
This commit is contained in:
Denis.Zharkov
2020-12-03 17:59:14 +03:00
parent ac0604377d
commit dc1a1c5821
10 changed files with 156 additions and 8 deletions
@@ -61,9 +61,9 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isPublishedApi
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.DEFAULT_CONSTRUCTOR_MARKER
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
import org.jetbrains.kotlin.resolve.jvm.JAVA_LANG_RECORD_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.annotations.isCompiledToJvmDefault
import org.jetbrains.kotlin.resolve.jvm.annotations.isJvmRecord
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
@@ -592,7 +592,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
return property.name.asString()
}
if ((containingDeclaration as? ClassDescriptor)?.isJvmRecord() == true) return property.name.asString()
if ((containingDeclaration as? ClassDescriptor)?.hasJavaLangRecordSupertype() == true) return property.name.asString()
val isAccessor = property is AccessorForPropertyDescriptor
val propertyName = if (isAccessor)
@@ -628,6 +628,9 @@ class KotlinTypeMapper @JvmOverloads constructor(
}
}
private fun ClassDescriptor.hasJavaLangRecordSupertype() =
typeConstructor.supertypes.any { KotlinBuiltIns.isConstructedFromGivenClass(it, JAVA_LANG_RECORD_FQ_NAME) }
private val shouldMangleByReturnType =
languageVersionSettings.supportsFeature(LanguageFeature.MangleClassMembersReturningInlineClasses)
@@ -41,7 +41,7 @@ import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.NameUtils
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_RECORD_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.JAVA_LANG_RECORD_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
@@ -84,8 +84,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
if (property != null) {
val propertyName = property.name.asString()
val propertyParent = property.parentAsClass
if (propertyParent.isAnnotationClass || propertyParent.hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME))
return propertyName
if (propertyParent.isAnnotationClass || propertyParent.superTypes.any { it.isJavaLangRecord() }) return propertyName
// The enum property getters <get-name> and <get-ordinal> have special names which also
// apply to their fake overrides. Unfortunately, getJvmMethodNameIfSpecial does not handle
@@ -102,6 +101,8 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
return mangleMemberNameIfRequired(function.name.asString(), function)
}
private fun IrType.isJavaLangRecord() = getClass()!!.hasEqualFqName(JAVA_LANG_RECORD_FQ_NAME)
private fun mangleMemberNameIfRequired(name: String, function: IrSimpleFunction): String {
val newName = JvmCodegenUtil.sanitizeNameIfNeeded(name, context.state.languageVersionSettings)
@@ -17,7 +17,9 @@ import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.impl.*
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.isPropertyAccessor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.types.typeUtil.makeNullable
@@ -74,6 +76,9 @@ val IrType.classifierOrNull: IrClassifierSymbol?
val IrType.classOrNull: IrClassSymbol?
get() = classifierOrNull as? IrClassSymbol
val IrType.classFqName: FqName?
get() = classOrNull?.owner?.fqNameWhenAvailable
val IrTypeArgument.typeOrNull: IrType? get() = (this as? IrTypeProjection)?.type
fun IrType.makeNotNull() =
@@ -0,0 +1,16 @@
// !API_VERSION: 1.5
// !LANGUAGE: +JvmRecordSupport
// JVM_TARGET: 15_PREVIEW
// FILE: A.kt
@JvmRecord
data class MyRecord(val foo: String, val bar: String)
// FILE: B.kt
fun main() {
val myRecord = MyRecord("O", "K")
val s = myRecord.foo + myRecord.bar
if (s != "OK") {
throw AssertionError("fail: $s")
}
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind
abstract class AbstractCompileKotlinAgainstKotlinJdk15Test : AbstractCompileKotlinAgainstKotlinTest() {
override fun invokeBox(className: String) {
runJvmInstance(
KotlinTestUtils.getJdk15Home(),
additionalArgs = listOf("--enable-preview"),
classPath = listOfNotNull(
aDir, bDir, ForTestCompileRuntime.runtimeJarForTests(),
),
className
)
}
override fun getTestJdkKind(files: List<TestFile>): TestJdkKind {
return TestJdkKind.FULL_JDK_15
}
}
@@ -35,8 +35,8 @@ import java.util.stream.Collectors;
public abstract class AbstractCompileKotlinAgainstKotlinTest extends CodegenTestCase {
private File tmpdir;
private File aDir;
private File bDir;
protected File aDir;
protected File bDir;
@Override
protected void setUp() throws Exception {
@@ -80,7 +80,7 @@ public abstract class AbstractCompileKotlinAgainstKotlinTest extends CodegenTest
return new Pair<>(factoryA, factoryB);
}
private void invokeBox(@NotNull String className) throws Exception {
protected void invokeBox(@NotNull String className) throws Exception {
callBoxMethodAndCheckResult(createGeneratedClassLoader(), className);
}
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.test.TargetBackend
abstract class AbstractIrCompileKotlinAgainstKotlinJdk15Test : AbstractCompileKotlinAgainstKotlinJdk15Test() {
override fun getBackendA(): TargetBackend {
return TargetBackend.JVM_IR
}
override fun getBackendB(): TargetBackend {
return TargetBackend.JVM_IR
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/testData/compileKotlinAgainstKotlinJdk15")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class CompileKotlinAgainstKotlinJdk15TestGenerated extends AbstractCompileKotlinAgainstKotlinJdk15Test {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInCompileKotlinAgainstKotlinJdk15() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/compileKotlinAgainstKotlinJdk15"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("jvmRecordBinary.kt")
public void testJvmRecordBinary() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlinJdk15/jvmRecordBinary.kt");
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/testData/compileKotlinAgainstKotlinJdk15")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class IrCompileKotlinAgainstKotlinJdk15TestGenerated extends AbstractIrCompileKotlinAgainstKotlinJdk15Test {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInCompileKotlinAgainstKotlinJdk15() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/compileKotlinAgainstKotlinJdk15"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("jvmRecordBinary.kt")
public void testJvmRecordBinary() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlinJdk15/jvmRecordBinary.kt");
}
}
@@ -368,6 +368,14 @@ fun main(args: Array<String>) {
model("compileKotlinAgainstKotlin")
}
testClass<AbstractCompileKotlinAgainstKotlinJdk15Test> {
model("compileKotlinAgainstKotlinJdk15")
}
testClass<AbstractIrCompileKotlinAgainstKotlinJdk15Test> {
model("compileKotlinAgainstKotlinJdk15")
}
testClass<AbstractDescriptorRendererTest> {
model("renderer")
}