diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java index aa259473047..7f51eada70c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java @@ -477,7 +477,7 @@ public class KotlinTypeMapper { @NotNull public static Type mapUnderlyingTypeOfInlineClassType(@NotNull KotlinType kotlinType) { - KotlinType underlyingType = InlineClassesUtilsKt.underlyingTypeOfInlineClassType(kotlinType); + KotlinType underlyingType = InlineClassesUtilsKt.unsubstitutedUnderlyingType(kotlinType); if (underlyingType == null) { throw new IllegalStateException("There should be underlying type for inline class type: " + kotlinType); } diff --git a/compiler/testData/writeSignature/inlineClasses/genericInlineClassBasedOnGenericType.kt b/compiler/testData/writeSignature/inlineClasses/genericInlineClassBasedOnGenericType.kt new file mode 100644 index 00000000000..4de2af91a78 --- /dev/null +++ b/compiler/testData/writeSignature/inlineClasses/genericInlineClassBasedOnGenericType.kt @@ -0,0 +1,30 @@ +// !LANGUAGE: +InlineClasses + +inline class Foo(val x: List) + +object Test { + fun nonNullTypeArgument(f: Foo) {} + fun nullableTypeArgument(f: Foo) {} + + fun nullableValue(f: Foo?) {} +} + +// method: Test::nonNullTypeArgument +// jvm signature: (Ljava/util/List;)V +// generic signature: (Ljava/util/List;)V + +// method: Test::nullableTypeArgument +// jvm signature: (Ljava/util/List;)V +// generic signature: (Ljava/util/List;)V + +// method: Test::nullableValue +// jvm signature: (Ljava/util/List;)V +// generic signature: (Ljava/util/List;)V + +// method: Foo$Erased::box +// jvm signature: (Ljava/util/List;)LFoo; +// generic signature: null + +// method: Foo::unbox +// jvm signature: ()Ljava/util/List; +// generic signature: ()Ljava/util/List; diff --git a/compiler/testData/writeSignature/inlineClasses/genericInlineClassWithDefaultTypeParameter.kt b/compiler/testData/writeSignature/inlineClasses/genericInlineClassWithDefaultTypeParameter.kt new file mode 100644 index 00000000000..6a43795701a --- /dev/null +++ b/compiler/testData/writeSignature/inlineClasses/genericInlineClassWithDefaultTypeParameter.kt @@ -0,0 +1,43 @@ +// !LANGUAGE: +InlineClasses + +inline class Default(val x: T) + +class Inv + +object Test { + fun withNotNullPrimitive(a: Default) {} + fun withAdditionalGenericParameter(x: Inv, y: Default) {} + + fun asNullable(a: Default?) {} + + fun asNullableTypeArgument(a: Default) {} + fun asNullableAndNullableTypeArgument(a: Default?) {} +} + +// method: Test::withNotNullPrimitive +// jvm signature: (Ljava/lang/Object;)V +// generic signature: null + +// method: Test::withAdditionalGenericParameter +// jvm signature: (LInv;Ljava/lang/Object;)V +// generic signature: (LInv;Ljava/lang/Object;)V + +// method: Test::asNullable +// jvm signature: (LDefault;)V +// generic signature: (LDefault;)V + +// method: Test::asNullableTypeArgument +// jvm signature: (Ljava/lang/Object;)V +// generic signature: null + +// method: Test::asNullableAndNullableTypeArgument +// jvm signature: (LDefault;)V +// generic signature: (LDefault;)V + +// method: Default$Erased:box +// jvm signature: (Ljava/lang/Object;)LDefault; +// generic signature: null + +// method: Default:unbox +// jvm signature: ()Ljava/lang/Object +// generic signature: null diff --git a/compiler/testData/writeSignature/inlineClasses/genericInlineClassWithNotNullTypeParameter.kt b/compiler/testData/writeSignature/inlineClasses/genericInlineClassWithNotNullTypeParameter.kt new file mode 100644 index 00000000000..f3632c16adb --- /dev/null +++ b/compiler/testData/writeSignature/inlineClasses/genericInlineClassWithNotNullTypeParameter.kt @@ -0,0 +1,28 @@ +// !LANGUAGE: +InlineClasses + +inline class NonNull(val x: T) +inline class NullableValue(val x: T?) + +object Test { + fun withNotNullPrimitive(a: NonNull) {} + fun asNullable(a: NonNull?) {} + + fun withNotNullForNullableValue(a: NullableValue) {} + fun asNullableForNullableValue(a: NullableValue?) {} +} + +// method: Test::withNotNullPrimitive +// jvm signature: (Ljava/lang/Object;)V +// generic signature: null + +// method: Test::asNullable +// jvm signature: (Ljava/lang/Object;)V +// generic signature: null + +// method: Test::withNotNullForNullableValue +// jvm signature: (Ljava/lang/Object;)V +// generic signature: null + +// method: Test::asNullableForNullableValue +// jvm signature: (LNullableValue;)V +// generic signature: (LNullableValue;)V \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 7836bfa7a77..e7b4019a26b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1944,6 +1944,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } + @TestMetadata("inlineClassCallsDefaultValue.kt") + public void testInlineClassCallsDefaultValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassCallsDefaultValue.kt"); + doTest(fileName); + } + @TestMetadata("unboxInlineClassAfterSafeCall.kt") public void testUnboxInlineClassAfterSafeCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/unboxInlineClassAfterSafeCall.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/WriteSignatureTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/WriteSignatureTestGenerated.java index 029a56857f8..31df9867097 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/WriteSignatureTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/WriteSignatureTestGenerated.java @@ -564,6 +564,24 @@ public class WriteSignatureTestGenerated extends AbstractWriteSignatureTest { doTest(fileName); } + @TestMetadata("genericInlineClassBasedOnGenericType.kt") + public void testGenericInlineClassBasedOnGenericType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/inlineClasses/genericInlineClassBasedOnGenericType.kt"); + doTest(fileName); + } + + @TestMetadata("genericInlineClassWithDefaultTypeParameter.kt") + public void testGenericInlineClassWithDefaultTypeParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/inlineClasses/genericInlineClassWithDefaultTypeParameter.kt"); + doTest(fileName); + } + + @TestMetadata("genericInlineClassWithNotNullTypeParameter.kt") + public void testGenericInlineClassWithNotNullTypeParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/inlineClasses/genericInlineClassWithNotNullTypeParameter.kt"); + doTest(fileName); + } + @TestMetadata("nullableInlineClassType.kt") public void testNullableInlineClassType() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/inlineClasses/nullableInlineClassType.kt"); diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/typeSignatureMapping.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/typeSignatureMapping.kt index 1570aeb618b..17d6bccb74e 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/typeSignatureMapping.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/typeSignatureMapping.kt @@ -19,7 +19,8 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType -import org.jetbrains.kotlin.resolve.underlyingRepresentation +import org.jetbrains.kotlin.resolve.substitutedUnderlyingType +import org.jetbrains.kotlin.resolve.unsubstitutedUnderlyingType import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections import org.jetbrains.kotlin.utils.DO_NOTHING_3 @@ -130,15 +131,9 @@ fun mapType( descriptor is ClassDescriptor -> { if (descriptor.isInline && !mode.needInlineClassWrapping) { - val underlyingType = descriptor.underlyingRepresentation()?.type - if (underlyingType != null) { - if (!kotlinType.isMarkedNullable) { - return mapType(underlyingType, factory, mode, typeMappingConfiguration, descriptorTypeWriter, writeGenericType) - } - - if (!underlyingType.isMarkedNullable && !KotlinBuiltIns.isPrimitiveType(underlyingType)) { - return mapType(underlyingType, factory, mode, typeMappingConfiguration, descriptorTypeWriter, writeGenericType) - } + val typeForMapping = computeUnderlyingType(kotlinType) + if (typeForMapping != null) { + return mapType(typeForMapping, factory, mode, typeMappingConfiguration, descriptorTypeWriter, writeGenericType) } } @@ -214,6 +209,23 @@ private fun mapBuiltInType(type: KotlinType, typeFactory: JvmTypeFacto return null } +private fun computeUnderlyingType(inlineClassType: KotlinType): KotlinType? { + if (!shouldUseUnderlyingType(inlineClassType)) return null + + val descriptor = inlineClassType.unsubstitutedUnderlyingType()?.constructor?.declarationDescriptor ?: return null + return if (descriptor is TypeParameterDescriptor) + getRepresentativeUpperBound(descriptor) + else + inlineClassType.substitutedUnderlyingType() +} + +private fun shouldUseUnderlyingType(inlineClassType: KotlinType): Boolean { + val underlyingType = inlineClassType.unsubstitutedUnderlyingType() ?: return false + + return !inlineClassType.isMarkedNullable || + !TypeUtils.isNullableType(underlyingType) && !KotlinBuiltIns.isPrimitiveType(underlyingType) +} + fun computeInternalName( klass: ClassDescriptor, typeMappingConfiguration: TypeMappingConfiguration<*> = TypeMappingConfigurationImpl diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt index 471229e37e0..6c37934860e 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.resolve import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -18,8 +19,15 @@ fun ClassDescriptor.underlyingRepresentation(): ValueParameterDescriptor? { fun DeclarationDescriptor.isInlineClass() = this is ClassDescriptor && this.isInline -fun KotlinType.underlyingTypeOfInlineClassType(): KotlinType? { - return constructor.declarationDescriptor.safeAs()?.underlyingRepresentation()?.type +fun KotlinType.unsubstitutedUnderlyingParameter(): ValueParameterDescriptor? { + return constructor.declarationDescriptor.safeAs()?.underlyingRepresentation() } -fun KotlinType.isInlineClassType(): Boolean = constructor.declarationDescriptor?.isInlineClass() ?: false \ No newline at end of file +fun KotlinType.unsubstitutedUnderlyingType(): KotlinType? = unsubstitutedUnderlyingParameter()?.type + +fun KotlinType.isInlineClassType(): Boolean = constructor.declarationDescriptor?.isInlineClass() ?: false + +fun KotlinType.substitutedUnderlyingType(): KotlinType? { + val parameter = unsubstitutedUnderlyingParameter() ?: return null + return memberScope.getContributedVariables(parameter.name, NoLookupLocation.FOR_ALREADY_TRACKED).singleOrNull()?.type +} \ No newline at end of file