Support generic underlying type of inline class

Use upper bound in JVM representation.
 #KT-32162
This commit is contained in:
Ilmir Usmanov
2021-12-09 03:37:17 +01:00
parent 732b8d4067
commit 9060168542
22 changed files with 326 additions and 12 deletions
@@ -17036,6 +17036,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/inlineClasses/functionsJvmSignaturesConflictOnInheritance.kt");
}
@Test
@TestMetadata("genericInlineClass.kt")
public void testGenericInlineClass() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/genericInlineClass.kt");
}
@Test
@TestMetadata("identityComparisonWithInlineClasses.kt")
public void testIdentityComparisonWithInlineClasses() throws Exception {
@@ -17132,6 +17138,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/inlineClasses/recursiveInlineClasses.kt");
}
@Test
@TestMetadata("reifiedGenericUnderlyingType.kt")
public void testReifiedGenericUnderlyingType() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/reifiedGenericUnderlyingType.kt");
}
@Test
@TestMetadata("reservedMembersAndConstructsInsideInlineClass.kt")
public void testReservedMembersAndConstructsInsideInlineClass() throws Exception {
@@ -17036,6 +17036,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/inlineClasses/functionsJvmSignaturesConflictOnInheritance.kt");
}
@Test
@TestMetadata("genericInlineClass.kt")
public void testGenericInlineClass() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/genericInlineClass.kt");
}
@Test
@TestMetadata("identityComparisonWithInlineClasses.kt")
public void testIdentityComparisonWithInlineClasses() throws Exception {
@@ -17132,6 +17138,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/inlineClasses/recursiveInlineClasses.kt");
}
@Test
@TestMetadata("reifiedGenericUnderlyingType.kt")
public void testReifiedGenericUnderlyingType() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/reifiedGenericUnderlyingType.kt");
}
@Test
@TestMetadata("reservedMembersAndConstructsInsideInlineClass.kt")
public void testReservedMembersAndConstructsInsideInlineClass() throws Exception {
@@ -21663,6 +21663,28 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue")
@TestDataPath("$PROJECT_ROOT")
public class GenericUnderlyingValue {
@Test
public void testAllFilesPresentInGenericUnderlyingValue() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/simple.kt");
}
@Test
@TestMetadata("upperBound.kt")
public void testUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/upperBound.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/hiddenConstructor")
@TestDataPath("$PROJECT_ROOT")
@@ -95,7 +95,6 @@ object InlineClassDeclarationChecker : DeclarationChecker {
val baseParameterTypes = descriptor.safeAs<ClassDescriptor>()?.defaultType?.substitutedUnderlyingTypes() ?: emptyList()
for ((baseParameter, baseParameterType) in primaryConstructor.valueParameters zip baseParameterTypes) {
if (!isParameterAcceptableForInlineClass(baseParameter)) {
trace.report(Errors.VALUE_CLASS_CONSTRUCTOR_NOT_FINAL_READ_ONLY_PARAMETER.on(baseParameter))
baseParametersOk = false
@@ -104,7 +103,10 @@ object InlineClassDeclarationChecker : DeclarationChecker {
val baseParameterTypeReference = baseParameter.typeReference
if (baseParameterType != null && baseParameterTypeReference != null) {
if (baseParameterType.isInapplicableParameterType()) {
if (baseParameterType.isInapplicableParameterType() &&
!(context.languageVersionSettings.supportsFeature(LanguageFeature.GenericInlineClassParameter) &&
(baseParameterType.isTypeParameter() || baseParameterType.isGenericArrayOfTypeParameter()))
) {
trace.report(Errors.VALUE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE.on(baseParameterTypeReference, baseParameterType))
baseParametersOk = false
continue
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.jvm.*
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
@@ -29,10 +30,7 @@ import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.transformStatement
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.makeNotNull
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.load.java.JvmAbi
@@ -68,7 +66,9 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
}
}
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid()
}
override fun visitClassNew(declaration: IrClass): IrStatement {
// The arguments to the primary constructor are in scope in the initializers of IrFields.
@@ -325,12 +325,24 @@ private class JvmInlineClassLowering(private val context: JvmBackendContext) : F
}
}
private fun coerceInlineClasses(argument: IrExpression, from: IrType, to: IrType) =
IrCallImpl.fromSymbolOwner(UNDEFINED_OFFSET, UNDEFINED_OFFSET, to, context.ir.symbols.unsafeCoerceIntrinsic).apply {
putTypeArgument(0, from)
putTypeArgument(1, to)
putValueArgument(0, argument)
private fun coerceInlineClasses(argument: IrExpression, from: IrType, to: IrType): IrExpression {
return IrCallImpl.fromSymbolOwner(UNDEFINED_OFFSET, UNDEFINED_OFFSET, to, context.ir.symbols.unsafeCoerceIntrinsic).apply {
val underlyingType = from.erasedUpperBound.inlineClassRepresentation?.underlyingType
if (underlyingType?.isTypeParameter() == true) {
putTypeArgument(0, from)
putTypeArgument(1, underlyingType)
putValueArgument(
0, IrTypeOperatorCallImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, to, IrTypeOperator.IMPLICIT_CAST, underlyingType, argument
)
)
} else {
putTypeArgument(0, from)
putTypeArgument(1, to)
putValueArgument(0, argument)
}
}
}
private fun IrExpression.coerceToUnboxed() =
coerceInlineClasses(this, this.type, this.type.unboxInlineClass())
@@ -0,0 +1,6 @@
// CHECK_BYTECODE_LISTING
// LANGUAGE: -JvmInlineValueClasses, +GenericInlineClassParameter
inline class ICAny<T>(val value: T)
fun box(): String = ICAny("OK").value
@@ -0,0 +1,24 @@
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class ICAny {
// source: 'simple.kt'
private final field value: java.lang.Object
private synthetic method <init>(p0: java.lang.Object): void
public synthetic final static method box-impl(p0: java.lang.Object): ICAny
public static @org.jetbrains.annotations.NotNull method constructor-impl(p0: java.lang.Object): java.lang.Object
public method equals(p0: java.lang.Object): boolean
public static method equals-impl(p0: java.lang.Object, p1: java.lang.Object): boolean
public final static method equals-impl0(p0: java.lang.Object, p1: java.lang.Object): boolean
public final method getValue(): java.lang.Object
public method hashCode(): int
public static method hashCode-impl(p0: java.lang.Object): int
public method toString(): java.lang.String
public static method toString-impl(p0: java.lang.Object): java.lang.String
public synthetic final method unbox-impl(): java.lang.Object
}
@kotlin.Metadata
public final class SimpleKt {
// source: 'simple.kt'
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
}
@@ -0,0 +1,6 @@
// CHECK_BYTECODE_LISTING
// LANGUAGE: -JvmInlineValueClasses, +GenericInlineClassParameter
inline class ICString<T: String>(val value: T)
fun box(): String = ICString("OK").value
@@ -0,0 +1,24 @@
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class ICString {
// source: 'upperBound.kt'
private final @org.jetbrains.annotations.NotNull field value: java.lang.String
private synthetic method <init>(p0: java.lang.String): void
public synthetic final static method box-impl(p0: java.lang.String): ICString
public static @org.jetbrains.annotations.NotNull method constructor-impl(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String
public method equals(p0: java.lang.Object): boolean
public static method equals-impl(p0: java.lang.String, p1: java.lang.Object): boolean
public final static method equals-impl0(p0: java.lang.String, p1: java.lang.String): boolean
public final @org.jetbrains.annotations.NotNull method getValue(): java.lang.String
public method hashCode(): int
public static method hashCode-impl(p0: java.lang.String): int
public method toString(): java.lang.String
public static method toString-impl(p0: java.lang.String): java.lang.String
public synthetic final method unbox-impl(): java.lang.String
}
@kotlin.Metadata
public final class UpperBoundKt {
// source: 'upperBound.kt'
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
}
@@ -0,0 +1,8 @@
// !LANGUAGE: +InlineClasses, -JvmInlineValueClasses, +GenericInlineClassParameter
// SKIP_TXT
inline class ICAny<T>(val value: T)
inline class ICArray<T>(val value: Array<T>)
inline class ICList<T>(val value: List<T>)
@@ -0,0 +1,8 @@
// !LANGUAGE: +InlineClasses, -JvmInlineValueClasses, +GenericInlineClassParameter
// SKIP_TXT
inline class ICAny<T>(val value: T)
inline class ICArray<T>(val value: Array<T>)
inline class ICList<T>(val value: List<T>)
@@ -0,0 +1,4 @@
// !LANGUAGE: +InlineClasses, -JvmInlineValueClasses
// SKIP_TXT
inline class ICAny<reified T>(val value: T)
@@ -0,0 +1,4 @@
// !LANGUAGE: +InlineClasses, -JvmInlineValueClasses
// SKIP_TXT
inline class ICAny<<!REIFIED_TYPE_PARAMETER_NO_INLINE, REIFIED_TYPE_PARAMETER_NO_INLINE!>reified<!> T>(val value: <!VALUE_CLASS_HAS_INAPPLICABLE_PARAMETER_TYPE!>T<!>)
@@ -17042,6 +17042,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/inlineClasses/functionsJvmSignaturesConflictOnInheritance.kt");
}
@Test
@TestMetadata("genericInlineClass.kt")
public void testGenericInlineClass() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/genericInlineClass.kt");
}
@Test
@TestMetadata("identityComparisonWithInlineClasses.kt")
public void testIdentityComparisonWithInlineClasses() throws Exception {
@@ -17138,6 +17144,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/inlineClasses/recursiveInlineClasses.kt");
}
@Test
@TestMetadata("reifiedGenericUnderlyingType.kt")
public void testReifiedGenericUnderlyingType() throws Exception {
runTest("compiler/testData/diagnostics/tests/inlineClasses/reifiedGenericUnderlyingType.kt");
}
@Test
@TestMetadata("reservedMembersAndConstructsInsideInlineClass.kt")
public void testReservedMembersAndConstructsInsideInlineClass() throws Exception {
@@ -21243,6 +21243,28 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue")
@TestDataPath("$PROJECT_ROOT")
public class GenericUnderlyingValue {
@Test
public void testAllFilesPresentInGenericUnderlyingValue() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/simple.kt");
}
@Test
@TestMetadata("upperBound.kt")
public void testUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/upperBound.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/hiddenConstructor")
@TestDataPath("$PROJECT_ROOT")
@@ -21663,6 +21663,28 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue")
@TestDataPath("$PROJECT_ROOT")
public class GenericUnderlyingValue {
@Test
public void testAllFilesPresentInGenericUnderlyingValue() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/simple.kt");
}
@Test
@TestMetadata("upperBound.kt")
public void testUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/upperBound.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/hiddenConstructor")
@TestDataPath("$PROJECT_ROOT")
@@ -17768,6 +17768,29 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
}
}
@TestMetadata("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class GenericUnderlyingValue extends AbstractLightAnalysisModeTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInGenericUnderlyingValue() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/simple.kt");
}
@TestMetadata("upperBound.kt")
public void testUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/upperBound.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/inlineClasses/hiddenConstructor")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -291,6 +291,7 @@ enum class LanguageFeature(
JsAllowValueClassesInExternals(sinceVersion = null, defaultState = State.DISABLED, kind = UNSTABLE_FEATURE),
ContextReceivers(sinceVersion = null, defaultState = State.DISABLED, kind = UNSTABLE_FEATURE),
KotlinFunInterfaceConstructorReference(sinceVersion = null, defaultState = State.DISABLED, kind = UNSTABLE_FEATURE),
GenericInlineClassParameter(sinceVersion = null, defaultState = State.ENABLED_WITH_WARNING, kind = UNSTABLE_FEATURE),
ValueClasses(sinceVersion = null, defaultState = State.DISABLED, kind = UNSTABLE_FEATURE),
;
@@ -16835,6 +16835,28 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue")
@TestDataPath("$PROJECT_ROOT")
public class GenericUnderlyingValue {
@Test
public void testAllFilesPresentInGenericUnderlyingValue() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/simple.kt");
}
@Test
@TestMetadata("upperBound.kt")
public void testUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/upperBound.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/hiddenConstructor")
@TestDataPath("$PROJECT_ROOT")
@@ -16799,6 +16799,28 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue")
@TestDataPath("$PROJECT_ROOT")
public class GenericUnderlyingValue {
@Test
public void testAllFilesPresentInGenericUnderlyingValue() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/simple.kt");
}
@Test
@TestMetadata("upperBound.kt")
public void testUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/upperBound.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/hiddenConstructor")
@TestDataPath("$PROJECT_ROOT")
@@ -14227,6 +14227,29 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
}
}
@TestMetadata("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class GenericUnderlyingValue extends AbstractIrCodegenBoxWasmTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath);
}
public void testAllFilesPresentInGenericUnderlyingValue() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/simple.kt");
}
@TestMetadata("upperBound.kt")
public void testUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/upperBound.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/inlineClasses/hiddenConstructor")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -18484,6 +18484,29 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue")
@TestDataPath("$PROJECT_ROOT")
@NativeBlackBoxTestCaseGroupProvider(ExtTestCaseGroupProvider.class)
public class GenericUnderlyingValue {
@Test
public void testAllFilesPresentInGenericUnderlyingValue() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true);
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/simple.kt");
}
@Test
@TestMetadata("upperBound.kt")
public void testUpperBound() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/genericUnderlyingValue/upperBound.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/inlineClasses/hiddenConstructor")
@TestDataPath("$PROJECT_ROOT")