From 1ee93553dff7de919ae75d6476374b61f81c2cb9 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 8 Feb 2023 14:27:24 +0100 Subject: [PATCH] K2: extract isSubtypeForSamConversion for SAM arguments check Related to KT-53966 --- .../kotlin/fir/resolve/calls/Arguments.kt | 105 +++++++++++------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index cd472e7200c..ae19d048ea0 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -536,49 +536,76 @@ fun FirExpression.isFunctional( ) { return true } - - val invokeSymbol = - coneType.findContributedInvokeSymbol( - session, scopeSession, classLikeExpectedFunctionType, shouldCalculateReturnTypesOfFakeOverrides = false - ) ?: return false - // Make sure the contributed `invoke` is indeed a wanted functional type by checking if types are compatible. - val expectedReturnType = classLikeExpectedFunctionType.returnType(session).lowerBoundIfFlexible() - val returnTypeCompatible = - expectedReturnType.originalIfDefinitelyNotNullable() is ConeTypeParameterType || - AbstractTypeChecker.isSubtypeOf( - session.typeContext.newTypeCheckerState( - errorTypesEqualToAnything = false, - stubTypesEqualToAnything = true - ), - returnTypeCalculator.tryCalculateReturnType(invokeSymbol.fir).type, - expectedReturnType, - isFromNullabilityConstraint = false - ) - if (!returnTypeCompatible) { - return false - } - if (invokeSymbol.fir.valueParameters.size != classLikeExpectedFunctionType.typeArguments.size - 1) { - return false - } - val parameterPairs = - invokeSymbol.fir.valueParameters.zip(classLikeExpectedFunctionType.valueParameterTypesIncludingReceiver(session)) - return parameterPairs.all { (invokeParameter, expectedParameter) -> - val expectedParameterType = expectedParameter.lowerBoundIfFlexible() - expectedParameterType.originalIfDefinitelyNotNullable() is ConeTypeParameterType || - AbstractTypeChecker.isSubtypeOf( - session.typeContext.newTypeCheckerState( - errorTypesEqualToAnything = false, - stubTypesEqualToAnything = true - ), - invokeParameter.returnTypeRef.coneType, - expectedParameterType, - isFromNullabilityConstraint = false - ) - } + return isSubtypeForSamConversion(session, scopeSession, coneType, classLikeExpectedFunctionType, returnTypeCalculator) } } } +/** + * This function checks whether an actual expression type is a subtype of an expected functional type in context of SAM conversion + * + * In more details, this function searches for an invoke symbol inside the actual expression type, + * and then checks compatibility of invoke symbol parameter types and return type + * with the corresponding functional type parameters and return type. During this check, + * type parameters inside the expected functional type are considered as compatible with everything; + * also, stub types in any positions are considered equal to anything. + * In other aspects, a normal subtype check is used. + */ +private fun isSubtypeForSamConversion( + session: FirSession, + scopeSession: ScopeSession, + actualExpressionType: ConeKotlinType, + classLikeExpectedFunctionType: ConeClassLikeType, + returnTypeCalculator: ReturnTypeCalculator +): Boolean { + // TODO: can we replace the function with a call of ConeKotlinType.isSubtypeOfFunctionType from FunctionalTypeUtils.kt, + // or with a call of AbstractTypeChecker.isSubtypeOf ? + // Relevant tests that can become broken: + // - codegen/box/sam/passSubtypeOfFunctionSamConversion.kt + // - diagnostics/tests/j+k/sam/recursiveSamsAndInvoke.kt + val invokeSymbol = + actualExpressionType.findContributedInvokeSymbol( + session, scopeSession, classLikeExpectedFunctionType, shouldCalculateReturnTypesOfFakeOverrides = false + ) ?: return false + // Make sure the contributed `invoke` is indeed a wanted functional type by checking if types are compatible. + val expectedReturnType = classLikeExpectedFunctionType.returnType(session).lowerBoundIfFlexible() + val returnTypeCompatible = + // TODO: can we remove is ConeTypeParameterType check here? + expectedReturnType.originalIfDefinitelyNotNullable() is ConeTypeParameterType || + AbstractTypeChecker.isSubtypeOf( + session.typeContext.newTypeCheckerState( + errorTypesEqualToAnything = false, + stubTypesEqualToAnything = true + ), + // TODO: can we remove returnTypeCalculatorFrom here + returnTypeCalculator.tryCalculateReturnType(invokeSymbol.fir).type, + expectedReturnType, + isFromNullabilityConstraint = false + ) + if (!returnTypeCompatible) { + return false + } + if (invokeSymbol.fir.valueParameters.size != classLikeExpectedFunctionType.typeArguments.size - 1) { + return false + } + val parameterPairs = + invokeSymbol.fir.valueParameters.zip(classLikeExpectedFunctionType.valueParameterTypesIncludingReceiver(session)) + return parameterPairs.all { (invokeParameter, expectedParameter) -> + val expectedParameterType = expectedParameter.lowerBoundIfFlexible() + // TODO: can we remove is ConeTypeParameterType check here? + expectedParameterType.originalIfDefinitelyNotNullable() is ConeTypeParameterType || + AbstractTypeChecker.isSubtypeOf( + session.typeContext.newTypeCheckerState( + errorTypesEqualToAnything = false, + stubTypesEqualToAnything = true + ), + invokeParameter.returnTypeRef.coneType, + expectedParameterType, + isFromNullabilityConstraint = false + ) + } +} + private fun FirExpression.namedReferenceWithCandidate(): FirNamedReferenceWithCandidate? = when (this) { is FirResolvable -> calleeReference as? FirNamedReferenceWithCandidate