NI: Analyze lambdas which are return arguments of another lambda

^KT-36044 Fixed
This commit is contained in:
Victor Petukhov
2020-03-11 18:55:55 +03:00
parent 426738b7d4
commit b1c4b5f51b
14 changed files with 156 additions and 30 deletions
@@ -10106,6 +10106,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
runTest("compiler/testData/diagnostics/tests/inference/kt35702.kt");
}
@TestMetadata("kt36044.kt")
public void testKt36044() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt36044.kt");
}
@TestMetadata("kt36819.kt")
public void testKt36819() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt36819.kt");
@@ -150,7 +150,10 @@ class PostponedArgumentsAnalyzer(
val subResolvedKtPrimitives = allReturnArguments.map {
resolveKtPrimitive(
c.getBuilder(), it, lambda.returnType.let(::substitute), diagnosticHolder, ReceiverInfo.notReceiver, convertedType = null
)
).apply {
if (this is LambdaWithTypeVariableAsExpectedTypeAtom)
isReturnArgumentOfAnotherLambda = true
}
}
if (!returnArgumentsInfo.returnArgumentsExist) {
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.types.*
@@ -103,12 +105,12 @@ class KotlinConstraintSystemCompleter(
if (
completionMode == ConstraintSystemCompletionMode.FULL &&
resolveLambdaOrCallableReferenceWithTypeVariableAsExpectedType(
c,
c.resolveLambdaByAdditionalConditions(
variableForFixation,
topLevelAtoms,
diagnosticsHolder,
analyze
analyze,
variableFixationFinder
)
) {
continue
@@ -141,15 +143,67 @@ class KotlinConstraintSystemCompleter(
/*
* returns true -> analyzed
*/
private fun resolveLambdaOrCallableReferenceWithTypeVariableAsExpectedType(
c: Context,
private fun Context.resolveLambdaByAdditionalConditions(
variableForFixation: VariableFixationFinder.VariableForFixation,
topLevelAtoms: List<ResolvedAtom>,
diagnosticsHolder: KotlinDiagnosticsHolder,
analyze: (PostponedResolvedAtom) -> Unit,
fixationFinder: VariableFixationFinder
): Boolean {
val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)
return resolveLambdaOrCallableReferenceWithTypeVariableAsExpectedType(
variableForFixation,
postponedArguments,
diagnosticsHolder,
analyze
) || resolveLambdaWhichIsReturnArgument(postponedArguments, diagnosticsHolder, analyze, fixationFinder)
}
/*
* returns true -> analyzed
*/
private fun Context.resolveLambdaWhichIsReturnArgument(
postponedArguments: List<PostponedResolvedAtom>,
diagnosticsHolder: KotlinDiagnosticsHolder,
analyze: (PostponedResolvedAtom) -> Unit,
fixationFinder: VariableFixationFinder
): Boolean {
if (this !is NewConstraintSystem) return false
val isReturnArgumentOfAnotherLambda = postponedArguments.any {
it is LambdaWithTypeVariableAsExpectedTypeAtom && it.isReturnArgumentOfAnotherLambda
}
val postponedAtom = postponedArguments.firstOrNull() ?: return false
val atomExpectedType = postponedAtom.expectedType
val shouldAnalyzeByPresenceLambdaAsReturnArgument =
isReturnArgumentOfAnotherLambda && atomExpectedType != null &&
with(fixationFinder) { variableHasTrivialOrNonProperConstraints(atomExpectedType.constructor) }
if (!shouldAnalyzeByPresenceLambdaAsReturnArgument)
return false
val expectedTypeVariable =
atomExpectedType?.constructor?.takeIf { it in this.getBuilder().currentStorage().allTypeVariables } ?: return false
analyze(preparePostponedAtom(expectedTypeVariable, postponedAtom, expectedTypeVariable.builtIns, diagnosticsHolder) ?: return false)
return true
}
/*
* returns true -> analyzed
*/
private fun Context.resolveLambdaOrCallableReferenceWithTypeVariableAsExpectedType(
variableForFixation: VariableFixationFinder.VariableForFixation,
postponedArguments: List<PostponedResolvedAtom>,
diagnosticsHolder: KotlinDiagnosticsHolder,
analyze: (PostponedResolvedAtom) -> Unit
): Boolean {
val variable = variableForFixation.variable as TypeConstructor
val postponedArguments = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)
if (this !is NewConstraintSystem) return false
val variable = variableForFixation.variable as? TypeConstructor ?: return false
val hasProperAtom = postponedArguments.any {
when (it) {
is LambdaWithTypeVariableAsExpectedTypeAtom,
@@ -158,21 +212,33 @@ class KotlinConstraintSystemCompleter(
}
}
if (
!hasProperAtom &&
variableForFixation.hasProperConstraint &&
!variableForFixation.hasOnlyTrivialProperConstraint
) return false
val postponedAtom = postponedArguments.firstOrNull() ?: return false
val expectedTypeAtom = postponedAtom.expectedType
val expectedTypeVariable =
expectedTypeAtom?.constructor?.takeIf { it in this.getBuilder().currentStorage().allTypeVariables } ?: variable
val builtIns = (variable as TypeVariableTypeConstructor).builtIns
val csBuilder = (c as NewConstraintSystemImpl).getBuilder()
val shouldAnalyzeByEqualityExpectedTypeToVariable =
hasProperAtom || !variableForFixation.hasProperConstraint || variableForFixation.hasOnlyTrivialProperConstraint
val expectedTypeVariable = postponedAtom.expectedType?.constructor?.takeIf { it in c.allTypeVariables } ?: variable
val atomToAnalyze = when (postponedAtom) {
if (!shouldAnalyzeByEqualityExpectedTypeToVariable)
return false
analyze(preparePostponedAtom(expectedTypeVariable, postponedAtom, variable.builtIns, diagnosticsHolder) ?: return false)
return true
}
private fun Context.preparePostponedAtom(
expectedTypeVariable: TypeConstructor,
postponedAtom: PostponedResolvedAtom,
builtIns: KotlinBuiltIns,
diagnosticsHolder: KotlinDiagnosticsHolder
): PostponedResolvedAtom? {
val csBuilder = (this as? NewConstraintSystem)?.getBuilder() ?: return null
return when (postponedAtom) {
is PostponedCallableReferenceAtom -> postponedAtom.preparePostponedAtomWithTypeVariableAsExpectedType(
c, csBuilder, expectedTypeVariable,
this, csBuilder, expectedTypeVariable,
parameterTypes = null,
isSuitable = KotlinType::isBuiltinFunctionalTypeOrSubtype,
typeVariableCreator = { TypeVariableForCallableReferenceReturnType(builtIns, "_Q") },
@@ -183,7 +249,7 @@ class KotlinConstraintSystemCompleter(
}
)
is LambdaWithTypeVariableAsExpectedTypeAtom -> postponedAtom.preparePostponedAtomWithTypeVariableAsExpectedType(
c, csBuilder, expectedTypeVariable,
this, csBuilder, expectedTypeVariable,
parameterTypes = postponedAtom.atom.parametersTypes,
isSuitable = KotlinType::isBuiltinFunctionalType,
typeVariableCreator = { TypeVariableForLambdaReturnType(postponedAtom.atom, builtIns, "_R") },
@@ -191,10 +257,8 @@ class KotlinConstraintSystemCompleter(
postponedAtom.transformToResolvedLambda(csBuilder, diagnosticsHolder, expectedType, returnVariable)
}
)
else -> return false
else -> null
}
analyze(atomToAnalyze)
return true
}
private inline fun <T : PostponedResolvedAtom, V : NewTypeVariable> T.preparePostponedAtomWithTypeVariableAsExpectedType(
@@ -86,6 +86,13 @@ class VariableFixationFinder(
}
}
fun Context.variableHasTrivialOrNonProperConstraints(variable: TypeConstructorMarker): Boolean {
return notFixedTypeVariables[variable]?.constraints?.all { constraint ->
val isProperConstraint = isProperArgumentConstraint(constraint)
isProperConstraint && trivialConstraintTypeInferenceOracle.isNotInterestingConstraint(constraint) || !isProperConstraint
} ?: false
}
private fun Context.findTypeVariableForFixation(
allTypeVariables: List<TypeConstructorMarker>,
postponedArguments: List<PostponedResolvedAtomMarker>,
@@ -120,13 +127,6 @@ class VariableFixationFinder(
return false
}
private fun Context.variableHasTrivialOrNonProperConstraints(variable: TypeConstructorMarker): Boolean {
return notFixedTypeVariables[variable]?.constraints?.all { constraint ->
val isProperConstraint = isProperArgumentConstraint(constraint)
isProperConstraint && trivialConstraintTypeInferenceOracle.isNotInterestingConstraint(constraint) || !isProperConstraint
} ?: false
}
private fun Context.variableHasProperArgumentConstraints(variable: TypeConstructorMarker): Boolean =
notFixedTypeVariables[variable]?.constraints?.any { isProperArgumentConstraint(it) } ?: false
@@ -109,6 +109,7 @@ class LambdaWithTypeVariableAsExpectedTypeAtom(
) : PostponedResolvedAtom() {
override val inputTypes: Collection<UnwrappedType> get() = listOf(expectedType)
override val outputType: UnwrappedType? get() = null
var isReturnArgumentOfAnotherLambda: Boolean = false
fun setAnalyzed(resolvedLambdaAtom: ResolvedLambdaAtom) {
setAnalyzedResults(listOf(resolvedLambdaAtom))
@@ -0,0 +1,8 @@
Failures detected in FirBodyResolveTransformerAdapter, file: /nestedCallWithOverload.fir.kt
Cause: java.lang.RuntimeException: While resolving call R?C|/baz|(R?C|/id|(<L> = id@fun <implicit>.<anonymous>(): <implicit> {
it#.inv#()
}
), R|/id|<R|(kotlin/Int) -> kotlin/Unit|>(<L> = id@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Unit| {
Unit
}
))
@@ -19,4 +19,6 @@ fun test() {
baz<(Int) -> Unit>(id(::foo), id(id(::foo)))
baz(id(::foo), id(id<(Int) -> Unit>(::foo)))
baz(id(<!UNRESOLVED_REFERENCE!>::foo<!>), id<(Int) -> Unit>(id(::foo)))
baz(id { it.inv() }, id<(Int) -> Unit> { })
}
@@ -19,4 +19,6 @@ fun test() {
baz<(Int) -> Unit>(id(::foo), id(id(::foo)))
baz(id(::foo), id(id<(Int) -> Unit>(::foo)))
baz(id(::foo), id<(Int) -> Unit>(id(::foo)))
baz(id { it.inv() }, id<(Int) -> Unit> { })
}
@@ -0,0 +1,8 @@
Failures detected in FirBodyResolveTransformerAdapter, file: /kt36044.fir.kt
Cause: java.lang.RuntimeException: While resolving call R?C|/select|(Char(a), R?C|/map|(<L> = map@fun <anonymous>(): <ERROR TYPE REF: No type for block> {
^ map@fun <implicit>.<anonymous>(): <implicit> {
String()
}
}
))
@@ -0,0 +1,9 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// Issue: KT-36044
fun <A> select(x: A, f: () -> A) = f()
fun <B> map(f: () -> B) = f()
fun main() {
select('a', map { { "" } })
}
@@ -0,0 +1,9 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// Issue: KT-36044
fun <A> select(x: A, f: () -> A) = f()
fun <B> map(f: () -> B) = f()
fun main() {
select('a', map { { "" } })
}
@@ -0,0 +1,5 @@
package
public fun main(): kotlin.Unit
public fun </*0*/ B> map(/*0*/ f: () -> B): B
public fun </*0*/ A> select(/*0*/ x: A, /*1*/ f: () -> A): A
@@ -10113,6 +10113,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali
runTest("compiler/testData/diagnostics/tests/inference/kt35702.kt");
}
@TestMetadata("kt36044.kt")
public void testKt36044() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt36044.kt");
}
@TestMetadata("kt36819.kt")
public void testKt36819() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt36819.kt");
@@ -10108,6 +10108,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/inference/kt35702.kt");
}
@TestMetadata("kt36044.kt")
public void testKt36044() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt36044.kt");
}
@TestMetadata("kt36819.kt")
public void testKt36819() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/kt36819.kt");