[FIR] Fix enhancement of java array type

This commit is contained in:
Dmitriy Novozhilov
2019-11-18 15:25:54 +03:00
parent d56412e397
commit 950d1f4fe4
21 changed files with 81 additions and 38 deletions
@@ -24,9 +24,7 @@ import org.jetbrains.kotlin.fir.java.toNotNullConeKotlinType
import org.jetbrains.kotlin.fir.java.types.FirJavaTypeRef import org.jetbrains.kotlin.fir.java.types.FirJavaTypeRef
import org.jetbrains.kotlin.fir.references.impl.FirResolvedNamedReferenceImpl import org.jetbrains.kotlin.fir.references.impl.FirResolvedNamedReferenceImpl
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.resolve.constructType import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolve.toTypeProjection
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
@@ -81,7 +79,7 @@ private fun JavaType?.enhancePossiblyFlexible(
): FirResolvedTypeRef { ): FirResolvedTypeRef {
val type = this val type = this
val arguments = this?.typeArguments().orEmpty() val arguments = this?.typeArguments().orEmpty()
return when (type) { val enhanced = when (type) {
is JavaClassifierType -> { is JavaClassifierType -> {
val lowerResult = type.enhanceInflexibleType( val lowerResult = type.enhanceInflexibleType(
session, javaTypeParameterStack, annotations, arguments, TypeComponentPosition.FLEXIBLE_LOWER, qualifiers, index session, javaTypeParameterStack, annotations, arguments, TypeComponentPosition.FLEXIBLE_LOWER, qualifiers, index
@@ -90,20 +88,30 @@ private fun JavaType?.enhancePossiblyFlexible(
session, javaTypeParameterStack, annotations, arguments, TypeComponentPosition.FLEXIBLE_UPPER, qualifiers, index session, javaTypeParameterStack, annotations, arguments, TypeComponentPosition.FLEXIBLE_UPPER, qualifiers, index
) )
FirResolvedTypeRefImpl( coneFlexibleOrSimpleType(session, lowerResult, upperResult)
source = null, }
type = coneFlexibleOrSimpleType(session, lowerResult, upperResult) is JavaArrayType -> {
).apply { val baseEnhanced = type.toNotNullConeKotlinType(session, javaTypeParameterStack)
this.annotations += annotations
val upperBound = if (baseEnhanced.typeArguments.isNotEmpty()) {
val typeArgument = baseEnhanced.typeArguments.first() as ConeKotlinType
baseEnhanced.withArguments(arrayOf(ConeKotlinTypeProjectionOut(typeArgument)))
} else {
baseEnhanced
} }
ConeFlexibleType(
baseEnhanced,
upperBound.withNullability(ConeNullability.NULLABLE)
)
} }
else -> { else -> {
val enhanced = type.toNotNullConeKotlinType(session, javaTypeParameterStack) type.toNotNullConeKotlinType(session, javaTypeParameterStack)
FirResolvedTypeRefImpl(source = null, type = enhanced).apply {
this.annotations += annotations
}
} }
} }
return FirResolvedTypeRefImpl(source = null, type = enhanced).apply {
this.annotations += annotations
}
} }
private fun JavaType?.subtreeSize(): Int { private fun JavaType?.subtreeSize(): Int {
@@ -15,8 +15,9 @@ import org.jetbrains.kotlin.fir.symbols.invoke
fun ConeKotlinType.createArrayOf(session: FirSession, nullable: Boolean = false): ConeKotlinType { fun ConeKotlinType.createArrayOf(session: FirSession, nullable: Boolean = false): ConeKotlinType {
val symbolProvider: FirSymbolProvider = session.firSymbolProvider val symbolProvider: FirSymbolProvider = session.firSymbolProvider
if (this is ConeClassType) { val type = lowerBoundIfFlexible()
val primitiveArrayId = StandardClassIds.primitiveArrayTypeByElementType[lookupTag.classId] if (type is ConeClassType) {
val primitiveArrayId = StandardClassIds.primitiveArrayTypeByElementType[type.lookupTag.classId]
if (primitiveArrayId != null) { if (primitiveArrayId != null) {
return primitiveArrayId.invoke(symbolProvider).constructType(emptyArray(), nullable) return primitiveArrayId.invoke(symbolProvider).constructType(emptyArray(), nullable)
} }
@@ -27,10 +28,11 @@ fun ConeKotlinType.createArrayOf(session: FirSession, nullable: Boolean = false)
fun ConeKotlinType.arrayElementType(session: FirSession): ConeKotlinType? { fun ConeKotlinType.arrayElementType(session: FirSession): ConeKotlinType? {
if (this !is ConeClassType) return null val type = this.lowerBoundIfFlexible()
val classId = this.lookupTag.classId if (type !is ConeClassType) return null
val classId = type.lookupTag.classId
if (classId == StandardClassIds.Array) if (classId == StandardClassIds.Array)
return (typeArguments.first() as ConeTypedProjection).type return (type.typeArguments.first() as ConeTypedProjection).type
val elementType = StandardClassIds.elementTypeByPrimitiveArrayType[classId] val elementType = StandardClassIds.elementTypeByPrimitiveArrayType[classId]
if (elementType != null) { if (elementType != null) {
return elementType.invoke(session.firSymbolProvider).constructType(emptyArray(), isNullable = false) return elementType.invoke(session.firSymbolProvider).constructType(emptyArray(), isNullable = false)
@@ -0,0 +1,18 @@
// FILE: A.java
public class A {
public static void take(A[] array) {}
}
// FILE: B.java
public class B extends A {}
// FILE: main.kt
fun takeA(array: Array<A>) {}
fun takeOutA(array: Array<out A>) {}
fun test(array: Array<B>) {
A.take(array)
<!INAPPLICABLE_CANDIDATE!>takeA<!>(array)
takeOutA(array)
}
@@ -0,0 +1,10 @@
FILE: main.kt
public final fun takeA(array: R|kotlin/Array<A>|): R|kotlin/Unit| {
}
public final fun takeOutA(array: R|kotlin/Array<out A>|): R|kotlin/Unit| {
}
public final fun test(array: R|kotlin/Array<B>|): R|kotlin/Unit| {
Q|A|.R|/A.take|(R|<local>/array|)
<Inapplicable(INAPPLICABLE): [/takeA]>#(R|<local>/array|)
R|/takeOutA|(R|<local>/array|)
}
@@ -255,6 +255,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/resolve/testData/resolve/arguments/invoke.kt"); runTest("compiler/fir/resolve/testData/resolve/arguments/invoke.kt");
} }
@TestMetadata("javaArrayVariance.kt")
public void testJavaArrayVariance() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/arguments/javaArrayVariance.kt");
}
@TestMetadata("lambda.kt") @TestMetadata("lambda.kt")
public void testLambda() throws Exception { public void testLambda() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/arguments/lambda.kt"); runTest("compiler/fir/resolve/testData/resolve/arguments/lambda.kt");
@@ -1,8 +1,8 @@
FILE fqName:<root> fileName:/jdkClassSyntheticProperty.kt FILE fqName:<root> fileName:/jdkClassSyntheticProperty.kt
PROPERTY name:test visibility:public modality:FINAL [val] PROPERTY name:test visibility:public modality:FINAL [val]
FUN name:<get-test> visibility:public modality:FINAL <> () returnType:kotlin.Array<java.lang.reflect.Field?> FUN name:<get-test> visibility:public modality:FINAL <> () returnType:kotlin.Array<out java.lang.reflect.Field?>?
correspondingProperty: PROPERTY name:test visibility:public modality:FINAL [val] correspondingProperty: PROPERTY name:test visibility:public modality:FINAL [val]
BLOCK_BODY BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-test> (): kotlin.Array<java.lang.reflect.Field?> declared in <root>' RETURN type=kotlin.Nothing from='public final fun <get-test> (): kotlin.Array<out java.lang.reflect.Field?>? declared in <root>'
CALL 'public open fun getDeclaredFields (): kotlin.Array<java.lang.reflect.Field?> declared in java.lang.Class' type=kotlin.Array<java.lang.reflect.Field?> origin=GET_PROPERTY CALL 'public open fun getDeclaredFields (): kotlin.Array<out java.lang.reflect.Field?>? declared in java.lang.Class' type=kotlin.Array<out java.lang.reflect.Field?>? origin=GET_PROPERTY
$this: ERROR_CALL 'Unresolved reference: this@R|/test|' type=java.lang.Class<*> $this: ERROR_CALL 'Unresolved reference: this@R|/test|' type=java.lang.Class<*>
@@ -1,5 +1,5 @@
public final class ArrayTypeVariance : R|kotlin/Any| { public final class ArrayTypeVariance : R|kotlin/Any| {
public final operator fun toArray(p0: R|kotlin/Array<ft<kotlin/Any, kotlin/Any?>!>|): R|kotlin/Array<ft<kotlin/Any, kotlin/Any?>!>| public final operator fun toArray(p0: R|ft<kotlin/Array<ft<kotlin/Any, kotlin/Any?>!>, kotlin/Array<out ft<kotlin/Any, kotlin/Any?>!>?>!|): R|ft<kotlin/Array<ft<kotlin/Any, kotlin/Any?>!>, kotlin/Array<out ft<kotlin/Any, kotlin/Any?>!>?>!|
public constructor(): R|test/ArrayTypeVariance| public constructor(): R|test/ArrayTypeVariance|
@@ -1,5 +1,5 @@
public open class FieldOfArrayType : R|kotlin/Any| { public open class FieldOfArrayType : R|kotlin/Any| {
public open field files: R|kotlin/Array<ft<java/io/File, java/io/File?>!>| public open field files: R|ft<kotlin/Array<ft<java/io/File, java/io/File?>!>, kotlin/Array<out ft<java/io/File, java/io/File?>!>?>!|
public constructor(): R|test/FieldOfArrayType| public constructor(): R|test/FieldOfArrayType|
@@ -1,5 +1,5 @@
public open class ArrayType : R|kotlin/Any| { public open class ArrayType : R|kotlin/Any| {
public open operator fun foo(): R|kotlin/Array<ft<kotlin/String, kotlin/String?>!>| public open operator fun foo(): R|ft<kotlin/Array<ft<kotlin/String, kotlin/String?>!>, kotlin/Array<out ft<kotlin/String, kotlin/String?>!>?>!|
public constructor(): R|test/ArrayType| public constructor(): R|test/ArrayType|
@@ -1,5 +1,5 @@
public open class MethodWithVararg : R|kotlin/Any| { public open class MethodWithVararg : R|kotlin/Any| {
public open operator fun foo(vararg s: R|kotlin/Array<ft<kotlin/String, kotlin/String?>!>|): R|kotlin/Unit| public open operator fun foo(vararg s: R|ft<kotlin/Array<ft<kotlin/String, kotlin/String?>!>, kotlin/Array<out ft<kotlin/String, kotlin/String?>!>?>!|): R|kotlin/Unit|
public constructor(): R|test/MethodWithVararg| public constructor(): R|test/MethodWithVararg|
@@ -1,9 +1,9 @@
public open class PropertyArrayTypes<T> : R|kotlin/Any| { public open class PropertyArrayTypes<T> : R|kotlin/Any| {
public open field arrayOfArrays: R|kotlin/Array<ft<kotlin/Array<ft<kotlin/String, kotlin/String?>!>, kotlin/Array<ft<kotlin/String, kotlin/String?>!>?>!>| public open field arrayOfArrays: R|ft<kotlin/Array<ft<kotlin/Array<ft<kotlin/String, kotlin/String?>!>, kotlin/Array<ft<kotlin/String, kotlin/String?>!>?>!>, kotlin/Array<out ft<kotlin/Array<ft<kotlin/String, kotlin/String?>!>, kotlin/Array<ft<kotlin/String, kotlin/String?>!>?>!>?>!|
public open field array: R|kotlin/Array<ft<kotlin/String, kotlin/String?>!>| public open field array: R|ft<kotlin/Array<ft<kotlin/String, kotlin/String?>!>, kotlin/Array<out ft<kotlin/String, kotlin/String?>!>?>!|
public open field genericArray: R|kotlin/Array<ft<T, T?>!>| public open field genericArray: R|ft<kotlin/Array<ft<T, T?>!>, kotlin/Array<out ft<T, T?>!>?>!|
public constructor<T>(): R|test/PropertyArrayTypes<T>| public constructor<T>(): R|test/PropertyArrayTypes<T>|
@@ -1,5 +1,5 @@
public open class WrongProjectionKind : R|kotlin/Any| { public open class WrongProjectionKind : R|kotlin/Any| {
public open operator fun copy(from: R|kotlin/Array<ft<kotlin/Number, kotlin/Number?>!>|, to: R|kotlin/Array<ft<kotlin/Number, kotlin/Number?>!>|): R|ft<kotlin/collections/MutableList<ft<kotlin/Number, kotlin/Number?>!>, kotlin/collections/List<ft<kotlin/Number, kotlin/Number?>!>?>!| public open operator fun copy(from: R|ft<kotlin/Array<ft<kotlin/Number, kotlin/Number?>!>, kotlin/Array<out ft<kotlin/Number, kotlin/Number?>!>?>!|, to: R|ft<kotlin/Array<ft<kotlin/Number, kotlin/Number?>!>, kotlin/Array<out ft<kotlin/Number, kotlin/Number?>!>?>!|): R|ft<kotlin/collections/MutableList<ft<kotlin/Number, kotlin/Number?>!>, kotlin/collections/List<ft<kotlin/Number, kotlin/Number?>!>?>!|
public constructor(): R|test/WrongProjectionKind| public constructor(): R|test/WrongProjectionKind|
@@ -1,5 +1,5 @@
public open class NotNullIntArray : R|kotlin/Any| { public open class NotNullIntArray : R|kotlin/Any| {
@R|org/jetbrains/annotations/NotNull|() public open operator fun hi(): R|kotlin/IntArray| @R|org/jetbrains/annotations/NotNull|() public open operator fun hi(): R|ft<kotlin/IntArray, kotlin/IntArray?>!|
public constructor(): R|test/NotNullIntArray| public constructor(): R|test/NotNullIntArray|
@@ -1,5 +1,5 @@
public open class NotNullObjectArray : R|kotlin/Any| { public open class NotNullObjectArray : R|kotlin/Any| {
@R|org/jetbrains/annotations/NotNull|() public open operator fun hi(): R|kotlin/Array<ft<kotlin/Any, kotlin/Any?>!>| @R|org/jetbrains/annotations/NotNull|() public open operator fun hi(): R|ft<kotlin/Array<ft<kotlin/Any, kotlin/Any?>!>, kotlin/Array<out ft<kotlin/Any, kotlin/Any?>!>?>!|
public constructor(): R|test/NotNullObjectArray| public constructor(): R|test/NotNullObjectArray|
@@ -1,4 +1,4 @@
public abstract interface GenericInterfaceParametersWithBounds<A : R|kotlin/Comparable<A>?|, R|kotlin/Cloneable?|, B : R|kotlin/collections/MutableList<A>?|> : R|kotlin/Any| { public abstract interface GenericInterfaceParametersWithBounds<A : R|kotlin/Comparable<A>?|, R|kotlin/Cloneable?|, B : R|kotlin/collections/MutableList<A>?|> : R|kotlin/Any| {
public abstract operator fun method(a: R|kotlin/Array<ft<A, A?>!>|, b: R|ft<B, B?>!|): R|kotlin/Unit| public abstract operator fun method(a: R|ft<kotlin/Array<ft<A, A?>!>, kotlin/Array<out ft<A, A?>!>?>!|, b: R|ft<B, B?>!|): R|kotlin/Unit|
} }
@@ -1,4 +1,4 @@
public abstract interface GenericMethodParameters : R|kotlin/Any| { public abstract interface GenericMethodParameters : R|kotlin/Any| {
public abstract operator fun <A : R|kotlin/CharSequence?|, B : R|kotlin/collections/MutableList<A>?|> method(a: R|kotlin/Array<ft<A, A?>!>|, b: R|ft<B, B?>!|): R|kotlin/Unit| public abstract operator fun <A : R|kotlin/CharSequence?|, B : R|kotlin/collections/MutableList<A>?|> method(a: R|ft<kotlin/Array<ft<A, A?>!>, kotlin/Array<out ft<A, A?>!>?>!|, b: R|ft<B, B?>!|): R|kotlin/Unit|
} }
@@ -1,4 +1,4 @@
public abstract interface VarargParameter : R|kotlin/Any| { public abstract interface VarargParameter : R|kotlin/Any| {
public abstract operator fun f(vararg strings: R|kotlin/Array<ft<kotlin/String, kotlin/String?>!>|): R|kotlin/Unit| public abstract operator fun f(vararg strings: R|ft<kotlin/Array<ft<kotlin/String, kotlin/String?>!>, kotlin/Array<out ft<kotlin/String, kotlin/String?>!>?>!|): R|kotlin/Unit|
} }
@@ -1,5 +1,5 @@
public open class VarargInt : R|kotlin/Any| { public open class VarargInt : R|kotlin/Any| {
public open operator fun vararg(vararg p: R|kotlin/IntArray|): R|kotlin/Unit| public open operator fun vararg(vararg p: R|ft<kotlin/IntArray, kotlin/IntArray?>!|): R|kotlin/Unit|
public constructor(): R|test/VarargInt| public constructor(): R|test/VarargInt|
@@ -1,5 +1,5 @@
public open class VarargString : R|kotlin/Any| { public open class VarargString : R|kotlin/Any| {
public open operator fun vararg(vararg strings: R|kotlin/Array<ft<kotlin/String, kotlin/String?>!>|): R|kotlin/Unit| public open operator fun vararg(vararg strings: R|ft<kotlin/Array<ft<kotlin/String, kotlin/String?>!>, kotlin/Array<out ft<kotlin/String, kotlin/String?>!>?>!|): R|kotlin/Unit|
public constructor(): R|test/VarargString| public constructor(): R|test/VarargString|
@@ -1,7 +1,7 @@
public open class Some : R|kotlin/Any| { public open class Some : R|kotlin/Any| {
public open operator fun foo(param: R|kotlin/Int|): R|kotlin/Boolean| public open operator fun foo(param: R|kotlin/Int|): R|kotlin/Boolean|
public open operator fun bar(arr: R|kotlin/IntArray|): R|kotlin/Array<ft<kotlin/String, kotlin/String?>!>| public open operator fun bar(arr: R|ft<kotlin/IntArray, kotlin/IntArray?>!|): R|ft<kotlin/Array<ft<kotlin/String, kotlin/String?>!>, kotlin/Array<out ft<kotlin/String, kotlin/String?>!>?>!|
public constructor(): R|Some| public constructor(): R|Some|
@@ -7,7 +7,7 @@ FILE: jvm.kt
public final fun test(): R|kotlin/Unit| { public final fun test(): R|kotlin/Unit| {
lval res1: R|kotlin/Boolean| = this@R|/Some|.R|/Some.foo|(Int(1)) lval res1: R|kotlin/Boolean| = this@R|/Some|.R|/Some.foo|(Int(1))
lval res2: R|kotlin/Boolean| = this@R|/Some|.R|/Some.foo|(Int(1).R|kotlin/Int.unaryMinus|()) lval res2: R|kotlin/Boolean| = this@R|/Some|.R|/Some.foo|(Int(1).R|kotlin/Int.unaryMinus|())
lval res3: R|kotlin/Array<ft<kotlin/String, kotlin/String?>!>| = this@R|/Some|.R|/Some.bar|(R|kotlin/intArrayOf|(Int(0), Int(2), Int(2).R|kotlin/Int.unaryMinus|())) lval res3: R|ft<kotlin/Array<ft<kotlin/String, kotlin/String?>!>, kotlin/Array<out ft<kotlin/String, kotlin/String?>!>?>!| = this@R|/Some|.R|/Some.bar|(R|kotlin/intArrayOf|(Int(0), Int(2), Int(2).R|kotlin/Int.unaryMinus|()))
} }
} }