Fix inference for lambdas with with extension function expected type

^KT-49832 Fixed
^KT-49836 Fixed
This commit is contained in:
Denis.Zharkov
2021-11-23 10:25:07 +03:00
committed by teamcityserver
parent 3fb17cfa9a
commit 0d9ad62d4a
16 changed files with 320 additions and 16 deletions
@@ -13453,12 +13453,36 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/inference/returningLambdaInSuspendContext.kt");
}
@Test
@TestMetadata("selectOfLambdaWithExtension.kt")
public void testSelectOfLambdaWithExtension() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtension.kt");
}
@Test
@TestMetadata("selectOfLambdaWithExtensionDisabled.kt")
public void testSelectOfLambdaWithExtensionDisabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtensionDisabled.kt");
}
@Test
@TestMetadata("selectOfLambdaWithExtensionEnabled.kt")
public void testSelectOfLambdaWithExtensionEnabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtensionEnabled.kt");
}
@Test
@TestMetadata("specialCallsWithCallableReferences.kt")
public void testSpecialCallsWithCallableReferences() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/specialCallsWithCallableReferences.kt");
}
@Test
@TestMetadata("specialCallsWithLambdas.kt")
public void testSpecialCallsWithLambdas() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/specialCallsWithLambdas.kt");
}
@Test
@TestMetadata("starApproximation.kt")
public void testStarApproximation() throws Exception {
@@ -13453,12 +13453,36 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/inference/returningLambdaInSuspendContext.kt");
}
@Test
@TestMetadata("selectOfLambdaWithExtension.kt")
public void testSelectOfLambdaWithExtension() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtension.kt");
}
@Test
@TestMetadata("selectOfLambdaWithExtensionDisabled.kt")
public void testSelectOfLambdaWithExtensionDisabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtensionDisabled.kt");
}
@Test
@TestMetadata("selectOfLambdaWithExtensionEnabled.kt")
public void testSelectOfLambdaWithExtensionEnabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtensionEnabled.kt");
}
@Test
@TestMetadata("specialCallsWithCallableReferences.kt")
public void testSpecialCallsWithCallableReferences() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/specialCallsWithCallableReferences.kt");
}
@Test
@TestMetadata("specialCallsWithLambdas.kt")
public void testSpecialCallsWithLambdas() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/specialCallsWithLambdas.kt");
}
@Test
@TestMetadata("starApproximation.kt")
public void testStarApproximation() throws Exception {
@@ -13453,12 +13453,36 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/inference/returningLambdaInSuspendContext.kt");
}
@Test
@TestMetadata("selectOfLambdaWithExtension.kt")
public void testSelectOfLambdaWithExtension() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtension.kt");
}
@Test
@TestMetadata("selectOfLambdaWithExtensionDisabled.kt")
public void testSelectOfLambdaWithExtensionDisabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtensionDisabled.kt");
}
@Test
@TestMetadata("selectOfLambdaWithExtensionEnabled.kt")
public void testSelectOfLambdaWithExtensionEnabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtensionEnabled.kt");
}
@Test
@TestMetadata("specialCallsWithCallableReferences.kt")
public void testSpecialCallsWithCallableReferences() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/specialCallsWithCallableReferences.kt");
}
@Test
@TestMetadata("specialCallsWithLambdas.kt")
public void testSpecialCallsWithLambdas() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/specialCallsWithLambdas.kt");
}
@Test
@TestMetadata("starApproximation.kt")
public void testStarApproximation() throws Exception {
@@ -91,6 +91,11 @@ object ConeConstraintSystemUtilContext : ConstraintSystemUtilContext {
return this is LambdaWithTypeVariableAsExpectedTypeAtom && !this.atom.anonymousFunction.isLambda && this.atom.anonymousFunction.receiverTypeRef?.coneType != null
}
override fun PostponedAtomWithRevisableExpectedType.isLambda(): Boolean {
require(this is PostponedResolvedAtom)
return this is LambdaWithTypeVariableAsExpectedTypeAtom && this.atom.anonymousFunction.isLambda
}
override fun createTypeVariableForLambdaReturnType(): TypeVariableMarker {
return ConeTypeVariableForPostponedAtom(PostponedArgumentInputTypesResolver.TYPE_VARIABLE_NAME_FOR_LAMBDA_RETURN_TYPE)
}
@@ -117,4 +122,7 @@ object ConeConstraintSystemUtilContext : ConstraintSystemUtilContext {
override fun createTypeVariableForCallableReferenceReturnType(): TypeVariableMarker {
return ConeTypeVariableForPostponedAtom(PostponedArgumentInputTypesResolver.TYPE_VARIABLE_NAME_FOR_LAMBDA_RETURN_TYPE)
}
override val isForcedConsiderExtensionReceiverFromConstrainsInLambda: Boolean
get() = true
}
@@ -30,7 +30,9 @@ class InferenceComponents(val session: FirSession) : FirSessionComponent {
val resultTypeResolver = ResultTypeResolver(approximator, trivialConstraintTypeInferenceOracle, session.languageVersionSettings)
val variableFixationFinder = VariableFixationFinder(trivialConstraintTypeInferenceOracle, session.languageVersionSettings)
val postponedArgumentInputTypesResolver =
PostponedArgumentInputTypesResolver(resultTypeResolver, variableFixationFinder, ConeConstraintSystemUtilContext)
PostponedArgumentInputTypesResolver(
resultTypeResolver, variableFixationFinder, ConeConstraintSystemUtilContext, session.languageVersionSettings
)
val constraintSystemFactory = ConstraintSystemFactory()
@@ -29,6 +29,7 @@ interface ConstraintSystemUtilContext {
fun extractLambdaParameterTypesFromDeclaration(declaration: PostponedAtomWithRevisableExpectedType): List<KotlinTypeMarker?>?
fun PostponedAtomWithRevisableExpectedType.isFunctionExpression(): Boolean
fun PostponedAtomWithRevisableExpectedType.isFunctionExpressionWithReceiver(): Boolean
fun PostponedAtomWithRevisableExpectedType.isLambda(): Boolean
fun createTypeVariableForLambdaReturnType(): TypeVariableMarker
fun createTypeVariableForLambdaParameterType(argument: PostponedAtomWithRevisableExpectedType, index: Int): TypeVariableMarker
fun createTypeVariableForCallableReferenceReturnType(): TypeVariableMarker
@@ -36,4 +37,6 @@ interface ConstraintSystemUtilContext {
argument: PostponedAtomWithRevisableExpectedType,
index: Int
): TypeVariableMarker
val isForcedConsiderExtensionReceiverFromConstrainsInLambda get() = false
}
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.types.model.*
@@ -19,6 +21,7 @@ class PostponedArgumentInputTypesResolver(
private val resultTypeResolver: ResultTypeResolver,
private val variableFixationFinder: VariableFixationFinder,
private val resolutionTypeSystemContext: ConstraintSystemUtilContext,
private val languageVersionSettings: LanguageVersionSettings,
) {
private class ParameterTypesInfo(
val parametersFromDeclaration: List<KotlinTypeMarker?>?,
@@ -79,12 +82,19 @@ class PostponedArgumentInputTypesResolver(
val annotations = functionalTypesFromConstraints?.map { it.type.getAnnotations() }?.flatten()?.distinct()
// An extension function flag can only come from a declaration of anonymous function: `select({ this + it }, fun Int.(x: Int) = 10)`
val (parameterTypesFromDeclarationOfRelatedLambdas, isThereExtensionFunctionAmongRelatedLambdas) =
getDeclaredParametersFromRelatedLambdas(argument, postponedArguments, variableDependencyProvider)
val extensionFunctionTypePresentInConstraints = functionalTypesFromConstraints?.any { it.type.isExtensionFunctionType() } == true
// An extension function flag can only come from a declaration of anonymous function: `select({ this + it }, fun Int.(x: Int) = 10)`
val (parameterTypesFromDeclarationOfRelatedLambdas, isThereExtensionFunctionAmongRelatedLambdas, maxParameterCount) =
computeParameterInfoFromRelatedLambdas(
argument,
postponedArguments,
variableDependencyProvider,
extensionFunctionTypePresentInConstraints,
parameterTypesFromConstraints,
parameterTypesFromDeclaration,
)
var isSuspend = false
var isNullable = false
if (!functionalTypesFromConstraints.isNullOrEmpty()) {
@@ -95,23 +105,47 @@ class PostponedArgumentInputTypesResolver(
if (isSuspend && !isNullable) break
}
}
val isLambda = with(resolutionTypeSystemContext) {
argument.isLambda()
}
val isExtensionFunction = isThereExtensionFunctionAmongRelatedLambdas || extensionFunctionTypePresentInConstraints
return ParameterTypesInfo(
parameterTypesFromDeclaration,
if (parameterTypesFromDeclaration != null && isLambda &&
parameterTypesFromDeclaration.size + 1 == maxParameterCount &&
isExtensionFunction && considerExtensionReceiverFromConstrainsInLambda()
)
listOf(null) + parameterTypesFromDeclaration
else
parameterTypesFromDeclaration,
parameterTypesFromDeclarationOfRelatedLambdas,
parameterTypesFromConstraints,
annotations = annotations,
isExtensionFunction = isThereExtensionFunctionAmongRelatedLambdas || extensionFunctionTypePresentInConstraints,
isExtensionFunction,
isSuspend = isSuspend,
isNullable = isNullable
)
}
private fun Context.getDeclaredParametersFromRelatedLambdas(
// Components:
// 1. Set of List of known parameter types (some of them aligned with null-prefix for absent extension receiver)
// 2. isAnyFunctionExpressionWithReceiver
// 3. maxParameterCount
private fun Context.computeParameterInfoFromRelatedLambdas(
argument: PostponedAtomWithRevisableExpectedType,
postponedArguments: List<PostponedAtomWithRevisableExpectedType>,
dependencyProvider: TypeVariableDependencyInformationProvider
): Pair<Set<List<KotlinTypeMarker?>>?, Boolean> = with(resolutionTypeSystemContext) {
val parameterTypesFromDeclarationOfRelatedLambdas = postponedArguments
dependencyProvider: TypeVariableDependencyInformationProvider,
extensionFunctionTypePresentInConstraints: Boolean,
parameterTypesFromConstraints: Set<List<TypeWithKind>>?,
parameterTypesFromDeclaration: List<KotlinTypeMarker?>?,
): Triple<Set<List<KotlinTypeMarker?>>?, Boolean, Int> = with(resolutionTypeSystemContext) {
var isAnyFunctionExpressionWithReceiver = false
// For each lambda/function expression:
// - First component: list of parameter types (for lambdas, it doesn't include receiver)
// - Second component: is lambda
val parameterTypesFromDeclarationOfRelatedLambdas: List<Pair<List<KotlinTypeMarker?>, Boolean>> = postponedArguments
.mapNotNull { anotherArgument ->
when {
anotherArgument !is LambdaWithTypeVariableAsExpectedTypeMarker -> null
@@ -123,19 +157,47 @@ class PostponedArgumentInputTypesResolver(
val areTypeVariablesRelated = dependencyProvider.areVariablesDependentShallowly(
argumentExpectedTypeConstructor, anotherArgumentExpectedTypeConstructor
)
val isAnonymousExtensionFunction = anotherArgument.isFunctionExpressionWithReceiver()
isAnyFunctionExpressionWithReceiver =
isAnyFunctionExpressionWithReceiver or anotherArgument.isFunctionExpressionWithReceiver()
val parameterTypesFromDeclarationOfRelatedLambda = anotherArgument.parameterTypesFromDeclaration
if (areTypeVariablesRelated && parameterTypesFromDeclarationOfRelatedLambda != null) {
parameterTypesFromDeclarationOfRelatedLambda to isAnonymousExtensionFunction
Pair(parameterTypesFromDeclarationOfRelatedLambda, anotherArgument.isLambda())
} else null
}
}
}
return parameterTypesFromDeclarationOfRelatedLambdas.run { mapTo(SmartSet.create()) { it.first } to any { it.second } }
val declaredParameterTypes = mutableSetOf<List<KotlinTypeMarker?>>()
val maxParameterCount = maxOf(
parameterTypesFromConstraints?.map { it.size }?.maxOrNull() ?: 0,
parameterTypesFromDeclarationOfRelatedLambdas.map { it.first.size }.maxOrNull() ?: 0,
parameterTypesFromDeclaration?.size ?: 0
)
val isFeatureEnabled =
considerExtensionReceiverFromConstrainsInLambda()
parameterTypesFromDeclarationOfRelatedLambdas.mapTo(declaredParameterTypes) { (types, isLambda) ->
if (
isFeatureEnabled && isLambda &&
(extensionFunctionTypePresentInConstraints || isAnyFunctionExpressionWithReceiver) &&
types.size + 1 == maxParameterCount
)
listOf(null) + types
else
types
}
return Triple(declaredParameterTypes, isAnyFunctionExpressionWithReceiver, maxParameterCount)
}
private fun considerExtensionReceiverFromConstrainsInLambda() =
resolutionTypeSystemContext.isForcedConsiderExtensionReceiverFromConstrainsInLambda ||
languageVersionSettings.supportsFeature(LanguageFeature.ConsiderExtensionReceiverFromConstrainsInLambda)
private fun Context.createTypeVariableForReturnType(argument: PostponedAtomWithRevisableExpectedType): TypeVariableMarker =
with(resolutionTypeSystemContext) {
return when (argument) {
@@ -443,7 +505,10 @@ class PostponedArgumentInputTypesResolver(
private fun getDeclaredParametersConsideringExtensionFunctionsPresence(parameterTypesInfo: ParameterTypesInfo): List<KotlinTypeMarker?>? =
with(parameterTypesInfo) {
if (parametersFromConstraints.isNullOrEmpty() || parametersFromDeclaration.isNullOrEmpty())
// If the feature is enabled, null for extension parameter has been added in different place already
if (considerExtensionReceiverFromConstrainsInLambda() ||
parametersFromConstraints.isNullOrEmpty() || parametersFromDeclaration.isNullOrEmpty()
)
parametersFromDeclaration
else {
val oneLessParameterInDeclarationThanInConstraints =
@@ -529,4 +594,4 @@ class PostponedArgumentInputTypesResolver(
const val TYPE_VARIABLE_NAME_PREFIX_FOR_CR_PARAMETER_TYPE = "_QP"
const val TYPE_VARIABLE_NAME_FOR_CR_RETURN_TYPE = "_Q"
}
}
}
@@ -83,6 +83,12 @@ class ClassicConstraintSystemUtilContext(
return atom is FunctionExpression && atom.receiverType != null
}
override fun PostponedAtomWithRevisableExpectedType.isLambda(): Boolean {
require(this is ResolvedAtom)
val atom = this.atom
return atom is LambdaKotlinCallArgument && atom !is FunctionExpression
}
override fun createTypeVariableForLambdaReturnType(): TypeVariableMarker {
return TypeVariableForLambdaReturnType(
builtIns,
@@ -0,0 +1,20 @@
// SKIP_TXT
typealias A = CharSequence.(Int) -> Unit
var w: Int = 1
fun myPrint(x: Int) {}
fun <T> select(vararg x: T) = x[0]
val a1: A = select(
{ a: Int -> myPrint(a + this.length + 1) },
{ a: Int -> myPrint(a + this.length + 2) }
)
val a2 = select(
{ a: Int -> myPrint(a + this.length + 1) },
fun CharSequence.(a: Int) { myPrint(a + this.length + 2) },
{ a: Int -> myPrint(a + this.length + 3) }
)
@@ -0,0 +1,20 @@
// SKIP_TXT
typealias A = CharSequence.(Int) -> Unit
var w: Int = 1
fun myPrint(x: Int) {}
fun <T> select(vararg x: T) = x[0]
val a1: A = select(
{ <!EXPECTED_PARAMETER_TYPE_MISMATCH!>a: Int<!> -> myPrint(a + this.length + 1) },
{ a: Int -> myPrint(a + this.length + 2) }
)
val a2 = select(
{ a: Int -> myPrint(a + this.<!UNRESOLVED_REFERENCE!>length<!> <!DEBUG_INFO_MISSING_UNRESOLVED!>+<!> 1) },
fun CharSequence.(a: Int) { myPrint(a + this.length + 2) },
{ a: Int -> myPrint(a + this.<!UNRESOLVED_REFERENCE!>length<!> <!DEBUG_INFO_MISSING_UNRESOLVED!>+<!> 3) }
)
@@ -0,0 +1,21 @@
// !LANGUAGE: -ConsiderExtensionReceiverFromConstrainsInLambda
// SKIP_TXT
typealias A = CharSequence.(Int) -> Unit
var w: Int = 1
fun myPrint(x: Int) {}
fun <T> select(vararg x: T) = x[0]
val a1: A = select(
{ a: Int -> myPrint(a + this.length + 1) },
{ a: Int -> myPrint(a + this.length + 2) }
)
val a2 = select(
{ a: Int -> myPrint(a + this.length + 1) },
fun CharSequence.(a: Int) { myPrint(a + this.length + 2) },
{ a: Int -> myPrint(a + this.length + 3) }
)
@@ -0,0 +1,21 @@
// !LANGUAGE: -ConsiderExtensionReceiverFromConstrainsInLambda
// SKIP_TXT
typealias A = CharSequence.(Int) -> Unit
var w: Int = 1
fun myPrint(x: Int) {}
fun <T> select(vararg x: T) = x[0]
val a1: A = select(
{ <!EXPECTED_PARAMETER_TYPE_MISMATCH!>a: Int<!> -> myPrint(a + this.length + 1) },
{ a: Int -> myPrint(a + this.length + 2) }
)
val a2 = select(
{ a: Int -> myPrint(a + this.<!UNRESOLVED_REFERENCE!>length<!> <!DEBUG_INFO_MISSING_UNRESOLVED!>+<!> 1) },
fun CharSequence.(a: Int) { myPrint(a + this.length + 2) },
{ a: Int -> myPrint(a + this.<!UNRESOLVED_REFERENCE!>length<!> <!DEBUG_INFO_MISSING_UNRESOLVED!>+<!> 3) }
)
@@ -0,0 +1,22 @@
// !LANGUAGE: +ConsiderExtensionReceiverFromConstrainsInLambda
// FIR_IDENTICAL
// SKIP_TXT
typealias A = CharSequence.(Int) -> Unit
var w: Int = 1
fun myPrint(x: Int) {}
fun <T> select(vararg x: T) = x[0]
val a1: A = select(
{ a: Int -> myPrint(a + this.length + 1) },
{ a: Int -> myPrint(a + this.length + 2) }
)
val a2 = select(
{ a: Int -> myPrint(a + this.length + 1) },
fun CharSequence.(a: Int) { myPrint(a + this.length + 2) },
{ a: Int -> myPrint(a + this.length + 3) }
)
@@ -0,0 +1,19 @@
// FIR_IDENTICAL
// SKIP_TXT
typealias A = CharSequence.(Int) -> Unit
var w: Int = 1
fun myPrint(x: Int) {}
val a1: A = when (w) {
1 -> { a: Int -> myPrint(a + this.length + 1) }
else -> { a: Int -> myPrint(a + this.length + 2) }
}
val a2: A = try {
{ a: Int -> myPrint(a + this.length + 1) }
} catch (t: Throwable) {
{ a: Int -> myPrint(a + this.length + 2) }
}
@@ -13459,12 +13459,36 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/inference/returningLambdaInSuspendContext.kt");
}
@Test
@TestMetadata("selectOfLambdaWithExtension.kt")
public void testSelectOfLambdaWithExtension() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtension.kt");
}
@Test
@TestMetadata("selectOfLambdaWithExtensionDisabled.kt")
public void testSelectOfLambdaWithExtensionDisabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtensionDisabled.kt");
}
@Test
@TestMetadata("selectOfLambdaWithExtensionEnabled.kt")
public void testSelectOfLambdaWithExtensionEnabled() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/selectOfLambdaWithExtensionEnabled.kt");
}
@Test
@TestMetadata("specialCallsWithCallableReferences.kt")
public void testSpecialCallsWithCallableReferences() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/specialCallsWithCallableReferences.kt");
}
@Test
@TestMetadata("specialCallsWithLambdas.kt")
public void testSpecialCallsWithLambdas() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/specialCallsWithLambdas.kt");
}
@Test
@TestMetadata("starApproximation.kt")
public void testStarApproximation() throws Exception {
@@ -241,6 +241,7 @@ enum class LanguageFeature(
PartiallySpecifiedTypeArguments(KOTLIN_1_7),
EliminateAmbiguitiesWithExternalTypeParameters(KOTLIN_1_7),
EliminateAmbiguitiesOnInheritedSamInterfaces(KOTLIN_1_7),
ConsiderExtensionReceiverFromConstrainsInLambda(KOTLIN_1_7, kind = BUG_FIX), // KT-49832
// 1.8