PSI2IR: Unify behavior for lambda return values with old back-end

See KT-35849.

1. When expected lambda return type is a type parameter, don't generate
introduce implicit casts (even if the corresponding type parameter has
an upper bound that would otherwise require such cast).

2. Do not generate implicit null check for lambda return value of
@EnhancedNullability type.
This commit is contained in:
Dmitry Petrov
2020-01-09 13:47:56 +03:00
parent c47e04ac8d
commit 4cf8203ce7
14 changed files with 368 additions and 22 deletions
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -94,7 +95,9 @@ internal class InsertImplicitCasts(
private fun IrMemberAccessExpression.transformReceiverArguments(substitutedDescriptor: CallableDescriptor) {
dispatchReceiver = dispatchReceiver?.cast(getEffectiveDispatchReceiverType(substitutedDescriptor))
extensionReceiver = extensionReceiver?.cast(substitutedDescriptor.extensionReceiverParameter?.type)
val extensionReceiverType = substitutedDescriptor.extensionReceiverParameter?.type
val originalExtensionReceiverType = substitutedDescriptor.original.extensionReceiverParameter?.type
extensionReceiver = extensionReceiver?.cast(extensionReceiverType, originalExtensionReceiverType)
}
private fun getEffectiveDispatchReceiverType(descriptor: CallableDescriptor): KotlinType? =
@@ -121,6 +124,7 @@ internal class InsertImplicitCasts(
for (index in substitutedDescriptor.valueParameters.indices) {
val argument = getValueArgument(index) ?: continue
val parameterType = substitutedDescriptor.valueParameters[index].type
val originalParameterType = substitutedDescriptor.original.valueParameters[index].type
// Hack to support SAM conversions on out-projected types.
// See SamType#createByValueParameter and genericSamProjectedOut.kt for more details.
@@ -130,7 +134,7 @@ internal class InsertImplicitCasts(
else
parameterType
putValueArgument(index, argument.cast(expectedType))
putValueArgument(index, argument.cast(expectedType, originalExpectedType = originalParameterType))
}
}
}
@@ -168,8 +172,12 @@ internal class InsertImplicitCasts(
value = if (expression.returnTargetSymbol is IrConstructorSymbol) {
value.coerceToUnit()
} else {
returnExpressionsToBePostprocessed.add(expression)
value.cast(expression.returnTarget.returnType)
val returnTargetDescriptor = expression.returnTarget
val isLambdaReturnValue = returnTargetDescriptor is AnonymousFunctionDescriptor
if (isLambdaReturnValue) {
returnExpressionsToBePostprocessed.add(expression)
}
value.cast(returnTargetDescriptor.returnType, isLambdaReturnValue = isLambdaReturnValue)
}
}
@@ -292,15 +300,16 @@ internal class InsertImplicitCasts(
else
null
private fun IrExpression.cast(expectedType: KotlinType?): IrExpression {
private fun IrExpression.cast(
expectedType: KotlinType?,
originalExpectedType: KotlinType? = expectedType,
isLambdaReturnValue: Boolean = false
): IrExpression {
if (expectedType == null) return this
if (expectedType.isError) return this
if (this is IrFunctionExpression) {
val expectedFunctionReturnType = expectedType.getFunctionReturnTypeOrNull()
if (expectedFunctionReturnType != null) {
expectedFunctionExpressionReturnType[function.descriptor] = expectedFunctionReturnType.toIrType()
}
if (this is IrFunctionExpression && originalExpectedType != null) {
recordExpectedLambdaReturnTypeIfAppropriate(expectedType, originalExpectedType)
}
// TODO here we can have non-denotable KotlinTypes (both in 'this@cast.type' and 'expectedType').
@@ -319,8 +328,10 @@ internal class InsertImplicitCasts(
else
implicitCast(expectedType, IrTypeOperator.IMPLICIT_DYNAMIC_CAST)
(valueType.isNullabilityFlexible() && valueType.containsNull() || valueType.hasEnhancedNullability()) &&
!expectedType.containsNull() ->
(valueType.isNullabilityFlexible() && valueType.containsNull()) && !expectedType.containsNull() ->
implicitNonNull(valueType, expectedType)
(valueType.hasEnhancedNullability() && !isLambdaReturnValue) && !expectedType.containsNull() ->
implicitNonNull(valueType, expectedType)
KotlinTypeChecker.DEFAULT.isSubtypeOf(valueType, expectedType.makeNullable()) ->
@@ -336,6 +347,20 @@ internal class InsertImplicitCasts(
}
}
private fun IrFunctionExpression.recordExpectedLambdaReturnTypeIfAppropriate(
expectedType: KotlinType,
originalExpectedType: KotlinType
) {
// TODO see KT-35849
val returnTypeFromExpected = expectedType.getFunctionReturnTypeOrNull() ?: return
val returnTypeFromOriginalExpected = originalExpectedType.getFunctionReturnTypeOrNull()
if (returnTypeFromOriginalExpected?.isTypeParameter() != true) {
expectedFunctionExpressionReturnType[function.descriptor] = returnTypeFromExpected.toIrType()
}
}
private fun KotlinType.hasEnhancedNullability() =
generatorExtensions.enhancedNullability.hasEnhancedNullability(this)