[NI] Don't process lambda until expected type will be fixed
This commit is contained in:
+59
-31
@@ -45,52 +45,80 @@ fun resolveKtPrimitive(
|
|||||||
private fun preprocessLambdaArgument(
|
private fun preprocessLambdaArgument(
|
||||||
csBuilder: ConstraintSystemBuilder,
|
csBuilder: ConstraintSystemBuilder,
|
||||||
argument: LambdaKotlinCallArgument,
|
argument: LambdaKotlinCallArgument,
|
||||||
expectedType: UnwrappedType?
|
expectedType: UnwrappedType?,
|
||||||
|
forceResolution: Boolean = false
|
||||||
): ResolvedAtom {
|
): ResolvedAtom {
|
||||||
|
if (expectedType != null && !forceResolution && csBuilder.isTypeVariable(expectedType)) {
|
||||||
|
return LambdaWithTypeVariableAsExpectedTypeAtom(argument, expectedType)
|
||||||
|
}
|
||||||
|
|
||||||
|
val resolvedArgument = extractLambdaInfoFromFunctionalType(expectedType, argument) ?: extraLambdaInfo(expectedType, argument, csBuilder)
|
||||||
|
|
||||||
|
if (expectedType != null) {
|
||||||
|
val lambdaType = createFunctionType(csBuilder.builtIns, Annotations.EMPTY, resolvedArgument.receiver,
|
||||||
|
resolvedArgument.parameters, null, resolvedArgument.returnType, resolvedArgument.isSuspend)
|
||||||
|
csBuilder.addSubtypeConstraint(lambdaType, expectedType, ArgumentConstraintPosition(argument))
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedArgument
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extraLambdaInfo(
|
||||||
|
expectedType: UnwrappedType?,
|
||||||
|
argument: LambdaKotlinCallArgument,
|
||||||
|
csBuilder: ConstraintSystemBuilder
|
||||||
|
): ResolvedLambdaAtom {
|
||||||
val builtIns = csBuilder.builtIns
|
val builtIns = csBuilder.builtIns
|
||||||
val isSuspend = expectedType?.isSuspendFunctionType ?: false
|
val isSuspend = expectedType?.isSuspendFunctionType ?: false
|
||||||
|
|
||||||
val receiverType: UnwrappedType? // null means that there is no receiver
|
val isFunctionSupertype = expectedType != null && KotlinBuiltIns.isNotNullOrNullableFunctionSupertype(expectedType)
|
||||||
val parameters: List<UnwrappedType>
|
val argumentAsFunctionExpression = argument.safeAs<FunctionExpression>()
|
||||||
val returnType: UnwrappedType
|
|
||||||
val typeVariable = TypeVariableForLambdaReturnType(argument, builtIns, "_L")
|
val typeVariable = TypeVariableForLambdaReturnType(argument, builtIns, "_L")
|
||||||
|
|
||||||
if (expectedType?.isBuiltinFunctionalType == true) {
|
val receiverType = argumentAsFunctionExpression?.receiverType
|
||||||
receiverType = if (argument is FunctionExpression) argument.receiverType else expectedType.getReceiverTypeFromFunctionType()?.unwrap()
|
val returnType = argumentAsFunctionExpression?.returnType ?:
|
||||||
|
|
||||||
val expectedParameters = expectedType.getValueParameterTypesFromFunctionType()
|
|
||||||
parameters = if (argument.parametersTypes != null) {
|
|
||||||
argument.parametersTypes!!.mapIndexed {
|
|
||||||
index, type ->
|
|
||||||
type ?: expectedParameters.getOrNull(index)?.type?.unwrap() ?: builtIns.nullableAnyType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// lambda without explicit parameters: { }
|
|
||||||
expectedParameters.map { it.type.unwrap() }
|
|
||||||
}
|
|
||||||
returnType = argument.safeAs<FunctionExpression>()?.returnType ?: expectedType.getReturnTypeFromFunctionType().unwrap()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val isFunctionSupertype = expectedType != null && KotlinBuiltIns.isNotNullOrNullableFunctionSupertype(expectedType)
|
|
||||||
receiverType = argument.safeAs<FunctionExpression>()?.receiverType
|
|
||||||
parameters = argument.parametersTypes?.map { it ?: builtIns.nothingType } ?: emptyList()
|
|
||||||
returnType = argument.safeAs<FunctionExpression>()?.returnType ?:
|
|
||||||
expectedType?.arguments?.singleOrNull()?.type?.unwrap()?.takeIf { isFunctionSupertype } ?:
|
expectedType?.arguments?.singleOrNull()?.type?.unwrap()?.takeIf { isFunctionSupertype } ?:
|
||||||
typeVariable.defaultType
|
typeVariable.defaultType
|
||||||
|
|
||||||
// what about case where expected type is type variable? In old TY such cases was not supported. => do nothing for now. todo design
|
val parameters = argument.parametersTypes?.map { it ?: builtIns.nothingType } ?: emptyList()
|
||||||
}
|
|
||||||
|
|
||||||
val newTypeVariableUsed = returnType == typeVariable.defaultType
|
val newTypeVariableUsed = returnType == typeVariable.defaultType
|
||||||
if (newTypeVariableUsed) csBuilder.registerVariable(typeVariable)
|
if (newTypeVariableUsed) csBuilder.registerVariable(typeVariable)
|
||||||
|
|
||||||
if (expectedType != null) {
|
return ResolvedLambdaAtom(argument, isSuspend, receiverType, parameters, returnType, typeVariable.takeIf { newTypeVariableUsed })
|
||||||
val lambdaType = createFunctionType(returnType.builtIns, Annotations.EMPTY, receiverType, parameters, null, returnType, isSuspend)
|
}
|
||||||
csBuilder.addSubtypeConstraint(lambdaType, expectedType, ArgumentConstraintPosition(argument))
|
|
||||||
|
private fun extractLambdaInfoFromFunctionalType(expectedType: UnwrappedType?, argument: LambdaKotlinCallArgument): ResolvedLambdaAtom? {
|
||||||
|
if (expectedType == null || !expectedType.isBuiltinFunctionalType) return null
|
||||||
|
val parameters = extractLambdaParameters(expectedType, argument)
|
||||||
|
|
||||||
|
val argumentAsFunctionExpression = argument.safeAs<FunctionExpression>()
|
||||||
|
val receiverType = argumentAsFunctionExpression?.receiverType ?: expectedType.getReceiverTypeFromFunctionType()?.unwrap()
|
||||||
|
val returnType = argumentAsFunctionExpression?.returnType ?: expectedType.getReturnTypeFromFunctionType().unwrap()
|
||||||
|
|
||||||
|
return ResolvedLambdaAtom(argument, expectedType.isSuspendFunctionType, receiverType, parameters, returnType, typeVariableForLambdaReturnType = null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractLambdaParameters(expectedType: UnwrappedType, argument: LambdaKotlinCallArgument): List<UnwrappedType> {
|
||||||
|
val parametersTypes = argument.parametersTypes
|
||||||
|
val expectedParameters = expectedType.getValueParameterTypesFromFunctionType()
|
||||||
|
if (parametersTypes == null) {
|
||||||
|
return expectedParameters.map { it.type.unwrap() }
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResolvedLambdaAtom(argument, isSuspend, receiverType, parameters, returnType, typeVariable.takeIf { newTypeVariableUsed })
|
return parametersTypes.mapIndexed { index, type ->
|
||||||
|
type ?: expectedParameters.getOrNull(index)?.type?.unwrap() ?: expectedType.builtIns.nullableAnyType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LambdaWithTypeVariableAsExpectedTypeAtom.transformToResolvedLambda(csBuilder: ConstraintSystemBuilder): ResolvedLambdaAtom {
|
||||||
|
val fixedExpectedType = csBuilder.buildCurrentSubstitutor().safeSubstitute(expectedType)
|
||||||
|
val resolvedLambdaAtom = preprocessLambdaArgument(csBuilder, atom, fixedExpectedType, forceResolution = true) as ResolvedLambdaAtom
|
||||||
|
|
||||||
|
setAnalyzed(resolvedLambdaAtom)
|
||||||
|
|
||||||
|
return resolvedLambdaAtom
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun preprocessCallableReference(
|
private fun preprocessCallableReference(
|
||||||
|
|||||||
+1
@@ -42,6 +42,7 @@ class PostponedArgumentsAnalyzer(
|
|||||||
fun analyze(c: Context, resolutionCallbacks: KotlinResolutionCallbacks, argument: ResolvedAtom) {
|
fun analyze(c: Context, resolutionCallbacks: KotlinResolutionCallbacks, argument: ResolvedAtom) {
|
||||||
when (argument) {
|
when (argument) {
|
||||||
is ResolvedLambdaAtom -> analyzeLambda(c, resolutionCallbacks, argument)
|
is ResolvedLambdaAtom -> analyzeLambda(c, resolutionCallbacks, argument)
|
||||||
|
is LambdaWithTypeVariableAsExpectedTypeAtom -> analyzeLambda(c, resolutionCallbacks, argument.transformToResolvedLambda(c.getBuilder()))
|
||||||
is ResolvedCallableReferenceAtom -> callableReferenceResolver.processCallableReferenceArgument(c.getBuilder(), argument)
|
is ResolvedCallableReferenceAtom -> callableReferenceResolver.processCallableReferenceArgument(c.getBuilder(), argument)
|
||||||
is ResolvedCollectionLiteralAtom -> TODO("Not supported")
|
is ResolvedCollectionLiteralAtom -> TODO("Not supported")
|
||||||
else -> error("Unexpected resolved primitive: ${argument.javaClass.canonicalName}")
|
else -> error("Unexpected resolved primitive: ${argument.javaClass.canonicalName}")
|
||||||
|
|||||||
+1
@@ -37,6 +37,7 @@ interface ConstraintSystemOperation {
|
|||||||
fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition)
|
fun addEqualityConstraint(a: UnwrappedType, b: UnwrappedType, position: ConstraintPosition)
|
||||||
|
|
||||||
fun isProperType(type: UnwrappedType): Boolean
|
fun isProperType(type: UnwrappedType): Boolean
|
||||||
|
fun isTypeVariable(type: UnwrappedType): Boolean
|
||||||
|
|
||||||
fun getProperSuperTypeConstructors(type: UnwrappedType): List<TypeConstructor>
|
fun getProperSuperTypeConstructors(type: UnwrappedType): List<TypeConstructor>
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -61,8 +61,9 @@ class KotlinConstraintSystemCompleter(
|
|||||||
val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
|
val variableForFixation = variableFixationFinder.findFirstVariableForFixation(
|
||||||
c, allTypeVariables, postponedKtPrimitives, completionMode, topLevelType)
|
c, allTypeVariables, postponedKtPrimitives, completionMode, topLevelType)
|
||||||
|
|
||||||
if (shouldWeForceCallableReferenceResolution(completionMode, variableForFixation)) {
|
if (shouldForceCallableReferenceOrLambdaResolution(completionMode, variableForFixation)) {
|
||||||
if (forceCallableReferenceResolution(topLevelPrimitive, analyze)) continue
|
if (forcePostponedAtomResolution<ResolvedCallableReferenceAtom>(topLevelPrimitive, analyze)) continue
|
||||||
|
if (forcePostponedAtomResolution<LambdaWithTypeVariableAsExpectedTypeAtom>(topLevelPrimitive, analyze)) continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (variableForFixation != null) {
|
if (variableForFixation != null) {
|
||||||
@@ -86,7 +87,7 @@ class KotlinConstraintSystemCompleter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shouldWeForceCallableReferenceResolution(
|
private fun shouldForceCallableReferenceOrLambdaResolution(
|
||||||
completionMode: ConstraintSystemCompletionMode,
|
completionMode: ConstraintSystemCompletionMode,
|
||||||
variableForFixation: VariableFixationFinder.VariableForFixation?
|
variableForFixation: VariableFixationFinder.VariableForFixation?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
@@ -108,11 +109,12 @@ class KotlinConstraintSystemCompleter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// true if we find some callable reference and run resolution for it. Note that such resolution can be unsuccessful
|
// true if we find some callable reference and run resolution for it. Note that such resolution can be unsuccessful
|
||||||
private fun forceCallableReferenceResolution(topLevelPrimitive: ResolvedAtom, analyze: (PostponedResolvedAtom) -> Unit): Boolean {
|
private inline fun <reified T : PostponedResolvedAtom> forcePostponedAtomResolution(
|
||||||
val callableReferenceArgument = getOrderedNotAnalyzedPostponedArguments(topLevelPrimitive).
|
topLevelPrimitive: ResolvedAtom,
|
||||||
firstIsInstanceOrNull<ResolvedCallableReferenceAtom>() ?: return false
|
analyze: (PostponedResolvedAtom) -> Unit
|
||||||
|
): Boolean {
|
||||||
analyze(callableReferenceArgument)
|
val postponedArgument = getOrderedNotAnalyzedPostponedArguments(topLevelPrimitive).firstIsInstanceOrNull<T>() ?: return false
|
||||||
|
analyze(postponedArgument)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
@@ -174,6 +174,11 @@ class NewConstraintSystemImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isTypeVariable(type: UnwrappedType): Boolean {
|
||||||
|
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||||
|
return notFixedTypeVariables.containsKey(type.constructor)
|
||||||
|
}
|
||||||
|
|
||||||
// ConstraintInjector.Context
|
// ConstraintInjector.Context
|
||||||
override val allTypeVariables: Map<TypeConstructor, NewTypeVariable> get() {
|
override val allTypeVariables: Map<TypeConstructor, NewTypeVariable> get() {
|
||||||
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION)
|
||||||
|
|||||||
@@ -82,6 +82,18 @@ sealed class PostponedResolvedAtom : ResolvedAtom() {
|
|||||||
abstract val outputType: UnwrappedType?
|
abstract val outputType: UnwrappedType?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LambdaWithTypeVariableAsExpectedTypeAtom(
|
||||||
|
override val atom: LambdaKotlinCallArgument,
|
||||||
|
val expectedType: UnwrappedType
|
||||||
|
) : PostponedResolvedAtom() {
|
||||||
|
override val inputTypes: Collection<UnwrappedType> get() = listOf(expectedType)
|
||||||
|
override val outputType: UnwrappedType? get() = null
|
||||||
|
|
||||||
|
fun setAnalyzed(resolvedLambdaAtom: ResolvedLambdaAtom) {
|
||||||
|
setAnalyzedResults(listOf(resolvedLambdaAtom), listOf())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ResolvedLambdaAtom(
|
class ResolvedLambdaAtom(
|
||||||
override val atom: LambdaKotlinCallArgument,
|
override val atom: LambdaKotlinCallArgument,
|
||||||
val isSuspend: Boolean,
|
val isSuspend: Boolean,
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
class MyList<T>
|
||||||
|
|
||||||
|
operator fun <T> MyList<T>.plusAssign(element: T) {}
|
||||||
|
|
||||||
|
val listOfFunctions = MyList<(Int) -> Int>()
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOfFunctions.plusAssign({ it -> it })
|
||||||
|
listOfFunctions += { it -> it }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fun test() = foo({ line: String -> line })
|
||||||
|
|
||||||
|
fun <T> foo(x: T): T = TODO()
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+12
@@ -17018,6 +17018,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaPostponeConstruction.kt")
|
||||||
|
public void testLambdaPostponeConstruction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaWrongReturnType.kt")
|
||||||
|
public void testLambdaWrongReturnType() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaWrongReturnType.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nestedIntersection.kt")
|
@TestMetadata("nestedIntersection.kt")
|
||||||
public void testNestedIntersection() throws Exception {
|
public void testNestedIntersection() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt");
|
||||||
|
|||||||
@@ -17018,6 +17018,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaPostponeConstruction.kt")
|
||||||
|
public void testLambdaPostponeConstruction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaWrongReturnType.kt")
|
||||||
|
public void testLambdaWrongReturnType() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaWrongReturnType.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nestedIntersection.kt")
|
@TestMetadata("nestedIntersection.kt")
|
||||||
public void testNestedIntersection() throws Exception {
|
public void testNestedIntersection() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt");
|
||||||
|
|||||||
@@ -17018,6 +17018,18 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaPostponeConstruction.kt")
|
||||||
|
public void testLambdaPostponeConstruction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaWrongReturnType.kt")
|
||||||
|
public void testLambdaWrongReturnType() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaWrongReturnType.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nestedIntersection.kt")
|
@TestMetadata("nestedIntersection.kt")
|
||||||
public void testNestedIntersection() throws Exception {
|
public void testNestedIntersection() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt");
|
||||||
|
|||||||
+12
@@ -20798,6 +20798,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaPostponeConstruction.kt")
|
||||||
|
public void testLambdaPostponeConstruction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaWrongReturnType.kt")
|
||||||
|
public void testLambdaWrongReturnType() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/lambdaWrongReturnType.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nestedIntersection.kt")
|
@TestMetadata("nestedIntersection.kt")
|
||||||
public void testNestedIntersection() throws Exception {
|
public void testNestedIntersection() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user