K2: fix enhancement building for Java methods with type parameters

Before this commit, we copied each type parameter during method
enhancement, while not copying the symbol. This led to symbol clashes
in MPP scenarios and various other problems.

Now we create a fully-functional type parameter copy in enhancement
and perform a substitution of old type parameters with new ones
in receiver type, value parameter types, return type,
and type parameter upper bounds.

#KT-59766 Fixed
#KT-59738 Fixed
This commit is contained in:
Mikhail Glukhikh
2023-07-12 15:59:05 +02:00
committed by Space Team
parent 52068e11ee
commit c350280e64
7 changed files with 110 additions and 10 deletions
@@ -33460,6 +33460,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
runTest("compiler/testData/codegen/box/multiplatform/k2/anonymousObjectAndSpecificImplementationInDeserializedIr.kt"); runTest("compiler/testData/codegen/box/multiplatform/k2/anonymousObjectAndSpecificImplementationInDeserializedIr.kt");
} }
@Test
@TestMetadata("javaMethodWithTypeParameter.kt")
public void testJavaMethodWithTypeParameter() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/k2/javaMethodWithTypeParameter.kt");
}
@Nested @Nested
@TestMetadata("compiler/testData/codegen/box/multiplatform/k2/annotations") @TestMetadata("compiler/testData/codegen/box/multiplatform/k2/annotations")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@@ -33460,6 +33460,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
runTest("compiler/testData/codegen/box/multiplatform/k2/anonymousObjectAndSpecificImplementationInDeserializedIr.kt"); runTest("compiler/testData/codegen/box/multiplatform/k2/anonymousObjectAndSpecificImplementationInDeserializedIr.kt");
} }
@Test
@TestMetadata("javaMethodWithTypeParameter.kt")
public void testJavaMethodWithTypeParameter() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/k2/javaMethodWithTypeParameter.kt");
}
@Nested @Nested
@TestMetadata("compiler/testData/codegen/box/multiplatform/k2/annotations") @TestMetadata("compiler/testData/codegen/box/multiplatform/k2/annotations")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.fir.java.resolveIfJavaType
import org.jetbrains.kotlin.fir.java.symbols.FirJavaOverriddenSyntheticPropertySymbol import org.jetbrains.kotlin.fir.java.symbols.FirJavaOverriddenSyntheticPropertySymbol
import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible import org.jetbrains.kotlin.fir.java.toConeKotlinTypeProbablyFlexible
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor import org.jetbrains.kotlin.fir.scopes.jvm.computeJvmDescriptor
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
@@ -251,6 +252,9 @@ class FirSignatureEnhancement(
val functionSymbol: FirFunctionSymbol<*> val functionSymbol: FirFunctionSymbol<*>
var isJavaRecordComponent = false var isJavaRecordComponent = false
val typeParameterSubstitutionMap = mutableMapOf<FirTypeParameterSymbol, ConeKotlinType>()
var typeParameterSubstitutor: ConeSubstitutorByMap? = null
val function = when (firMethod) { val function = when (firMethod) {
is FirJavaConstructor -> { is FirJavaConstructor -> {
val symbol = FirConstructorSymbol(methodId).also { functionSymbol = it } val symbol = FirConstructorSymbol(methodId).also { functionSymbol = it }
@@ -288,6 +292,7 @@ class FirSignatureEnhancement(
moduleData = this@FirSignatureEnhancement.moduleData moduleData = this@FirSignatureEnhancement.moduleData
resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES
origin = FirDeclarationOrigin.Enhancement origin = FirDeclarationOrigin.Enhancement
// TODO: we should set a new origin / containing declaration to type parameters (KT-60440)
this.typeParameters += (enhancedTypeParameters ?: firMethod.typeParameters) this.typeParameters += (enhancedTypeParameters ?: firMethod.typeParameters)
} }
} }
@@ -297,14 +302,6 @@ class FirSignatureEnhancement(
source = firMethod.source source = firMethod.source
moduleData = this@FirSignatureEnhancement.moduleData moduleData = this@FirSignatureEnhancement.moduleData
origin = FirDeclarationOrigin.Enhancement origin = FirDeclarationOrigin.Enhancement
returnTypeRef = newReturnTypeRef
receiverParameter = newReceiverTypeRef?.let { receiverType ->
buildReceiverParameter {
typeRef = receiverType
annotations += firMethod.valueParameters.first().annotations
source = receiverType.source?.fakeElement(KtFakeSourceElementKind.ReceiverFromType)
}
}
this.name = name!! this.name = name!!
status = firMethod.status status = firMethod.status
@@ -316,10 +313,39 @@ class FirSignatureEnhancement(
"Unexpected type parameter type: ${typeParameter::class.simpleName}" "Unexpected type parameter type: ${typeParameter::class.simpleName}"
} }
buildTypeParameterCopy(typeParameter) { // TODO: we probably shouldn't build a copy second time. See performFirstRoundOfBoundsResolution (KT-60446)
val newTypeParameter = buildTypeParameterCopy(typeParameter) {
origin = FirDeclarationOrigin.Enhancement origin = FirDeclarationOrigin.Enhancement
symbol = FirTypeParameterSymbol()
containingDeclarationSymbol = functionSymbol containingDeclarationSymbol = functionSymbol
} }
typeParameterSubstitutionMap[typeParameter.symbol] = ConeTypeParameterTypeImpl(
newTypeParameter.symbol.toLookupTag(), isNullable = false
)
newTypeParameter
}
if (typeParameterSubstitutionMap.isNotEmpty()) {
typeParameterSubstitutor = ConeSubstitutorByMap(typeParameterSubstitutionMap, session)
}
returnTypeRef = newReturnTypeRef.withReplacedConeType(
typeParameterSubstitutor?.substituteOrNull(newReturnTypeRef.coneType)
)
val substitutedReceiverTypeRef = newReceiverTypeRef?.withReplacedConeType(
typeParameterSubstitutor?.substituteOrNull(newReturnTypeRef.coneType)
)
receiverParameter = substitutedReceiverTypeRef?.let { receiverType ->
buildReceiverParameter {
typeRef = receiverType
annotations += firMethod.valueParameters.first().annotations
source = receiverType.source?.fakeElement(KtFakeSourceElementKind.ReceiverFromType)
}
}
typeParameters.forEach { typeParameter ->
typeParameter.replaceBounds(
typeParameter.bounds.map { boundTypeRef ->
boundTypeRef.withReplacedConeType(typeParameterSubstitutor?.substituteOrNull(boundTypeRef.coneType))
}
)
} }
dispatchReceiverType = firMethod.dispatchReceiverType dispatchReceiverType = firMethod.dispatchReceiverType
@@ -336,7 +362,9 @@ class FirSignatureEnhancement(
containingFunctionSymbol = functionSymbol containingFunctionSymbol = functionSymbol
moduleData = this@FirSignatureEnhancement.moduleData moduleData = this@FirSignatureEnhancement.moduleData
origin = FirDeclarationOrigin.Enhancement origin = FirDeclarationOrigin.Enhancement
returnTypeRef = enhancedReturnType returnTypeRef = enhancedReturnType.withReplacedConeType(
typeParameterSubstitutor?.substituteOrNull(enhancedReturnType.coneType)
)
this.name = valueParameter.name this.name = valueParameter.name
symbol = FirValueParameterSymbol(this.name) symbol = FirValueParameterSymbol(this.name)
defaultValue = valueParameter.defaultValue defaultValue = valueParameter.defaultValue
@@ -388,6 +416,7 @@ class FirSignatureEnhancement(
typeParametersCopy += if (typeParameter is FirTypeParameter) { typeParametersCopy += if (typeParameter is FirTypeParameter) {
initialBounds.add(typeParameter.bounds.toList()) initialBounds.add(typeParameter.bounds.toList())
buildTypeParameterCopy(typeParameter) { buildTypeParameterCopy(typeParameter) {
// TODO: we should create a new symbol to avoid clashing (KT-60445)
bounds.clear() bounds.clear()
typeParameter.bounds.mapTo(bounds) { typeParameter.bounds.mapTo(bounds) {
it.resolveIfJavaType(session, javaTypeParameterStack, FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND_FIRST_ROUND) it.resolveIfJavaType(session, javaTypeParameterStack, FirJavaTypeConversionMode.TYPE_PARAMETER_BOUND_FIRST_ROUND)
@@ -0,0 +1,42 @@
// LANGUAGE: +MultiPlatformProjects
// ISSUE: KT-59766
// WITH_STDLIB
// TARGET_BACKEND: JVM_IR
// FULL_JDK
// MODULE: lib-jvm
// FILE: lib.kt
import java.util.concurrent.*
fun <T> CompletionStage<T>.foo() {
handle { value, exception ->
}
}
// MODULE: jvm()()(lib-jvm)
// FILE: jvm.kt
package kotlinx.coroutines.future
import java.util.concurrent.*
import java.util.function.*
fun <T> CompletionStage<T>.asDeferred() {
handle { value, exception ->
}
}
class ContinuationHandler<T> : BiFunction<T?, Throwable?, Unit> {
override fun apply(result: T?, exception: Throwable?) {
}
}
fun box(): String {
val handler = ContinuationHandler<String>()
CompletableFuture<String>().asDeferred()
return "OK"
}
@@ -33460,6 +33460,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/multiplatform/k2/anonymousObjectAndSpecificImplementationInDeserializedIr.kt"); runTest("compiler/testData/codegen/box/multiplatform/k2/anonymousObjectAndSpecificImplementationInDeserializedIr.kt");
} }
@Test
@TestMetadata("javaMethodWithTypeParameter.kt")
public void testJavaMethodWithTypeParameter() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/k2/javaMethodWithTypeParameter.kt");
}
@Nested @Nested
@TestMetadata("compiler/testData/codegen/box/multiplatform/k2/annotations") @TestMetadata("compiler/testData/codegen/box/multiplatform/k2/annotations")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@@ -33460,6 +33460,12 @@ public class IrBlackBoxCodegenWithIrInlinerTestGenerated extends AbstractIrBlack
runTest("compiler/testData/codegen/box/multiplatform/k2/anonymousObjectAndSpecificImplementationInDeserializedIr.kt"); runTest("compiler/testData/codegen/box/multiplatform/k2/anonymousObjectAndSpecificImplementationInDeserializedIr.kt");
} }
@Test
@TestMetadata("javaMethodWithTypeParameter.kt")
public void testJavaMethodWithTypeParameter() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/k2/javaMethodWithTypeParameter.kt");
}
@Nested @Nested
@TestMetadata("compiler/testData/codegen/box/multiplatform/k2/annotations") @TestMetadata("compiler/testData/codegen/box/multiplatform/k2/annotations")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@@ -28487,6 +28487,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/multiplatform/k2/anonymousObjectAndSpecificImplementationInDeserializedIr.kt"); runTest("compiler/testData/codegen/box/multiplatform/k2/anonymousObjectAndSpecificImplementationInDeserializedIr.kt");
} }
@TestMetadata("javaMethodWithTypeParameter.kt")
public void testJavaMethodWithTypeParameter() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/k2/javaMethodWithTypeParameter.kt");
}
@TestMetadata("compiler/testData/codegen/box/multiplatform/k2/annotations") @TestMetadata("compiler/testData/codegen/box/multiplatform/k2/annotations")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)