Support mapping for inline classes based on type variables

This commit is contained in:
Mikhail Zarechenskiy
2018-02-05 18:43:38 +03:00
parent 0fd80ce980
commit 4eb64fb4b9
8 changed files with 159 additions and 14 deletions
@@ -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);
}
@@ -0,0 +1,30 @@
// !LANGUAGE: +InlineClasses
inline class Foo<T>(val x: List<T>)
object Test {
fun nonNullTypeArgument(f: Foo<Int>) {}
fun nullableTypeArgument(f: Foo<String?>) {}
fun nullableValue(f: Foo<Long>?) {}
}
// method: Test::nonNullTypeArgument
// jvm signature: (Ljava/util/List;)V
// generic signature: (Ljava/util/List<Ljava/lang/Integer;>;)V
// method: Test::nullableTypeArgument
// jvm signature: (Ljava/util/List;)V
// generic signature: (Ljava/util/List<Ljava/lang/String;>;)V
// method: Test::nullableValue
// jvm signature: (Ljava/util/List;)V
// generic signature: (Ljava/util/List<Ljava/lang/Long;>;)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<TT;>;
@@ -0,0 +1,43 @@
// !LANGUAGE: +InlineClasses
inline class Default<T>(val x: T)
class Inv<T>
object Test {
fun withNotNullPrimitive(a: Default<Int>) {}
fun withAdditionalGenericParameter(x: Inv<String>, y: Default<String>) {}
fun asNullable(a: Default<Int>?) {}
fun asNullableTypeArgument(a: Default<Int?>) {}
fun asNullableAndNullableTypeArgument(a: Default<Int?>?) {}
}
// 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/String;>;Ljava/lang/Object;)V
// method: Test::asNullable
// jvm signature: (LDefault;)V
// generic signature: (LDefault<Ljava/lang/Integer;>;)V
// method: Test::asNullableTypeArgument
// jvm signature: (Ljava/lang/Object;)V
// generic signature: null
// method: Test::asNullableAndNullableTypeArgument
// jvm signature: (LDefault;)V
// generic signature: (LDefault<Ljava/lang/Integer;>;)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
@@ -0,0 +1,28 @@
// !LANGUAGE: +InlineClasses
inline class NonNull<T : Any>(val x: T)
inline class NullableValue<T : Any>(val x: T?)
object Test {
fun withNotNullPrimitive(a: NonNull<Int>) {}
fun asNullable(a: NonNull<Int>?) {}
fun withNotNullForNullableValue(a: NullableValue<Int>) {}
fun asNullableForNullableValue(a: NullableValue<Int>?) {}
}
// 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<Ljava/lang/Integer;>;)V
@@ -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");
@@ -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");
@@ -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 <T : Any> 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 <T : Any> 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
@@ -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<ClassDescriptor>()?.underlyingRepresentation()?.type
fun KotlinType.unsubstitutedUnderlyingParameter(): ValueParameterDescriptor? {
return constructor.declarationDescriptor.safeAs<ClassDescriptor>()?.underlyingRepresentation()
}
fun KotlinType.isInlineClassType(): Boolean = constructor.declarationDescriptor?.isInlineClass() ?: false
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
}