FIR2IR: Repeat the K1 behavior: SAM conversion with 'in' projection
It would be more consistently to prohibit the behavior from the unmuted test (see KT-52428), but it was decided to postpone the breaking change. Unfortunately, it didn't work to make a test where for computing star projections we would need to substitute other type parameters because effectively, it's not allowed to have SAM conversion when star projections/wildcard is based on a type parameter which bounds use other type parameters. ^KT-53552 In progress
This commit is contained in:
committed by
Space Team
parent
45eefab811
commit
05ca001310
@@ -101,27 +101,17 @@ internal enum class ConversionTypeOrigin {
|
||||
SETTER
|
||||
}
|
||||
|
||||
class ConversionTypeContext internal constructor(
|
||||
internal val invariantProjection: Boolean = false,
|
||||
internal val origin: ConversionTypeOrigin = ConversionTypeOrigin.DEFAULT,
|
||||
) {
|
||||
class ConversionTypeContext internal constructor(internal val origin: ConversionTypeOrigin) {
|
||||
fun inSetter() = ConversionTypeContext(
|
||||
invariantProjection = invariantProjection,
|
||||
origin = ConversionTypeOrigin.SETTER
|
||||
)
|
||||
|
||||
fun withInvariantProjections() = ConversionTypeContext(
|
||||
invariantProjection = true,
|
||||
origin = origin
|
||||
)
|
||||
|
||||
companion object {
|
||||
internal val DEFAULT = ConversionTypeContext(
|
||||
invariantProjection = false, origin = ConversionTypeOrigin.DEFAULT
|
||||
origin = ConversionTypeOrigin.DEFAULT
|
||||
)
|
||||
internal val WITH_INVARIANT = DEFAULT.withInvariantProjections()
|
||||
internal val IN_SETTER = ConversionTypeContext(
|
||||
invariantProjection = false, origin = ConversionTypeOrigin.SETTER
|
||||
origin = ConversionTypeOrigin.SETTER
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ class Fir2IrTypeConverter(
|
||||
private fun ConeTypeProjection.toIrTypeArgument(typeContext: ConversionTypeContext): IrTypeArgument {
|
||||
fun toIrTypeArgument(type: ConeKotlinType, variance: Variance): IrTypeProjection {
|
||||
val irType = type.toIrType(typeContext)
|
||||
return makeTypeProjection(irType, if (typeContext.invariantProjection) Variance.INVARIANT else variance)
|
||||
return makeTypeProjection(irType, variance)
|
||||
}
|
||||
|
||||
return when (this) {
|
||||
|
||||
+73
-6
@@ -26,7 +26,9 @@ import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorForFullBodyResolve
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.UNDEFINED_PARAMETER_INDEX
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -44,6 +46,7 @@ import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.model.TypeVariance
|
||||
|
||||
/**
|
||||
* A generator that converts callable references or arguments that needs an adapter in between. This covers:
|
||||
@@ -428,17 +431,81 @@ internal class AdapterGenerator(
|
||||
return this
|
||||
}
|
||||
val parameterType = parameter.returnTypeRef.coneType
|
||||
val substitutedParameterType = starProjectionApproximator.substituteOrSelf(substitutor.substituteOrSelf(parameterType))
|
||||
val samFirType = if (substitutedParameterType is ConeRawType) substitutedParameterType.lowerBound else substitutedParameterType
|
||||
var samType = samFirType.toIrType(ConversionTypeContext.WITH_INVARIANT)
|
||||
if (shouldUnwrapVarargType) {
|
||||
samType = samType.getArrayElementType(irBuiltIns)
|
||||
}
|
||||
val substitutedParameterType =
|
||||
starProjectionApproximator.substituteOrSelf(substitutor.substituteOrSelf(parameterType)).let {
|
||||
if (shouldUnwrapVarargType)
|
||||
it.arrayElementType() ?: it
|
||||
else
|
||||
it
|
||||
}
|
||||
|
||||
val samFirType = substitutedParameterType.removeExternalProjections() ?: substitutedParameterType
|
||||
val samType = samFirType.toIrType(ConversionTypeContext.DEFAULT)
|
||||
// Make sure the converted IrType owner indeed has a single abstract method, since FunctionReferenceLowering relies on it.
|
||||
if (!samType.isSamType) return this
|
||||
return IrTypeOperatorCallImpl(this.startOffset, this.endOffset, samType, IrTypeOperator.SAM_CONVERSION, samType, this)
|
||||
}
|
||||
|
||||
// This function is mostly a mirror of org.jetbrains.kotlin.backend.common.SamTypeApproximator.removeExternalProjections
|
||||
// First attempts, to share the code between K1 and K2 via type contexts stumbled upon the absence of star-projection-type in K2
|
||||
// and the possibility of incorrectly mapped details that might break some code when using K1.
|
||||
private fun ConeKotlinType.removeExternalProjections(): ConeKotlinType? =
|
||||
when (this) {
|
||||
is ConeSimpleKotlinType -> removeExternalProjections()
|
||||
is ConeFlexibleType -> ConeFlexibleType(
|
||||
lowerBound.removeExternalProjections() ?: lowerBound,
|
||||
upperBound.removeExternalProjections() ?: upperBound,
|
||||
)
|
||||
}
|
||||
|
||||
private fun ConeSimpleKotlinType.removeExternalProjections(): ConeSimpleKotlinType? =
|
||||
with(session.typeContext) {
|
||||
val typeConstructor = typeConstructor()
|
||||
val parameters = typeConstructor.getParameters()
|
||||
val parameterSet = parameters.toSet()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val newArguments = getArguments().mapIndexed { i, argument ->
|
||||
val parameter = parameters.getOrNull(i) ?: return null
|
||||
|
||||
when {
|
||||
argument.getVariance() == TypeVariance.IN -> {
|
||||
// Just erasing `in` from the type projection would lead to an incorrect type for the SAM adapter,
|
||||
// and error at runtime on JVM if invokedynamic + LambdaMetafactory is used, see KT-51868.
|
||||
// So we do it "carefully". If we have a class `A<T>` and a method that takes e.g. `A<in String>`, we check
|
||||
// if `T` has a non-trivial upper bound. If it has one, we don't attempt to perform a SAM conversion at all.
|
||||
// Otherwise we erase the type to `Any?`, so `A<in String>` becomes `A<Any?>`, which is the computed SAM type.
|
||||
val upperBound = parameter.getUpperBounds().singleOrNull()?.upperBoundIfFlexible() ?: return null
|
||||
if (!upperBound.isNullableAny()) return null
|
||||
|
||||
upperBound
|
||||
}
|
||||
!argument.isStarProjection() -> argument.getType()
|
||||
else -> parameter.typeParameterSymbol.starProjectionTypeRepresentation(parameterSet)
|
||||
}
|
||||
} as List<ConeTypeProjection>
|
||||
|
||||
withArguments(newArguments.toTypedArray())
|
||||
}
|
||||
|
||||
// See the definition from K1 at org.jetbrains.kotlin.types.StarProjectionImpl.get_type
|
||||
// In K1, it's used more frequently because of not-nullable TypeProjection::getType, but in K2 we almost got rid of it
|
||||
// But here, we still need it to more-or-less fully reproduce the semantics of K1 when generating SAM conversions
|
||||
private fun FirTypeParameterSymbol.starProjectionTypeRepresentation(containingParameterSet: Set<ConeTypeParameterLookupTag>): ConeKotlinType {
|
||||
val substitutor = object : AbstractConeSubstitutor(session.typeContext) {
|
||||
// We don't substitute types
|
||||
override fun substituteType(type: ConeKotlinType): ConeKotlinType? = null
|
||||
|
||||
override fun substituteArgument(projection: ConeTypeProjection, index: Int): ConeTypeProjection? {
|
||||
// But we substitute type parameters from the class-owner of this@FirTypeParameterSymbol as it's done in K1
|
||||
if (projection is ConeTypeParameterType && projection.lookupTag in containingParameterSet) return ConeStarProjection
|
||||
return super.substituteArgument(projection, index)
|
||||
}
|
||||
}
|
||||
|
||||
return substitutor.substituteOrSelf(resolvedBounds.first().type)
|
||||
}
|
||||
|
||||
private fun IrVararg.applyConversionOnVararg(
|
||||
argument: FirExpression,
|
||||
conversion: IrExpression.(FirExpression) -> IrExpression
|
||||
|
||||
+6
@@ -27416,6 +27416,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndySam.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("starProjectionSam.kt")
|
||||
public void testStarProjectionSam() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/invokedynamic/sam/starProjectionSam.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("streamApi1.kt")
|
||||
public void testStreamApi1() throws Exception {
|
||||
|
||||
+6
@@ -27416,6 +27416,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
|
||||
runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndySam.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("starProjectionSam.kt")
|
||||
public void testStarProjectionSam() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/invokedynamic/sam/starProjectionSam.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("streamApi1.kt")
|
||||
public void testStreamApi1() throws Exception {
|
||||
|
||||
@@ -268,7 +268,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
|
||||
}
|
||||
}
|
||||
|
||||
override fun TypeConstructorMarker.getParameters(): List<TypeParameterMarker> {
|
||||
override fun TypeConstructorMarker.getParameters(): List<ConeTypeParameterLookupTag> {
|
||||
return when (val symbol = toClassLikeSymbol()) {
|
||||
is FirAnonymousObjectSymbol -> symbol.fir.typeParameters.map { it.symbol.toLookupTag() }
|
||||
is FirRegularClassSymbol -> symbol.fir.typeParameters.map { it.symbol.toLookupTag() }
|
||||
|
||||
Reference in New Issue
Block a user