[FIR] Fix incorrect inference of return type of anonymous functions

Also remove incorrect subtype check checkers

Test unsafeVarianceInAliasedFunctionalType.kt started to fail because
  of KT-54894. This bug existed before, changes from this commit just
  unhided it (previously it was hidden because incorrect subtype check
  in `isSubtypeForTypeMismatch` which is used by FirFunctionReturnTypeMismatchChecker
This commit is contained in:
Dmitriy Novozhilov
2022-11-10 15:08:44 +02:00
committed by Space Team
parent 1b42298025
commit 991d8c18aa
12 changed files with 49 additions and 53 deletions
@@ -272,35 +272,6 @@ fun isSubtypeForTypeMismatch(context: ConeInferenceContext, subtype: ConeKotlinT
val subtypeFullyExpanded = subtype.fullyExpandedType(context.session)
val supertypeFullyExpanded = supertype.fullyExpandedType(context.session)
return AbstractTypeChecker.isSubtypeOf(context, subtypeFullyExpanded, supertypeFullyExpanded)
|| isSubtypeOfForFunctionalTypeReturningUnit(context.session.typeContext, subtypeFullyExpanded, supertypeFullyExpanded)
}
private fun isSubtypeOfForFunctionalTypeReturningUnit(
context: ConeInferenceContext,
subtype: ConeKotlinType,
supertype: ConeKotlinType
): Boolean {
if (!supertype.isBuiltinFunctionalType(context.session)) return false
val functionalTypeReturnType = supertype.typeArguments.lastOrNull()
if ((functionalTypeReturnType as? ConeClassLikeType)?.isUnit == true) {
// We don't try to match return type for this case
// Dropping the return type (getting only the lambda args)
val superTypeArgs = supertype.typeArguments.dropLast(1)
val subTypeArgs = subtype.typeArguments.dropLast(1)
if (superTypeArgs.size != subTypeArgs.size) return false
for (i in superTypeArgs.indices) {
val subTypeArg = subTypeArgs[i].type ?: return false
val superTypeArg = superTypeArgs[i].type ?: return false
if (!AbstractTypeChecker.isSubtypeOf(context.session.typeContext, subTypeArg, superTypeArg)) {
return false
}
}
return true
}
return false
}
fun FirCallableDeclaration.isVisibleInClass(parentClass: FirClass): Boolean {
@@ -54,6 +54,11 @@ fun FirTypeRef.resolvedTypeFromPrototype(
buildResolvedTypeRef {
source = this@resolvedTypeFromPrototype.source
this.type = type
delegatedTypeRef = when (val original = this@resolvedTypeFromPrototype) {
is FirResolvedTypeRef -> original.delegatedTypeRef
is FirUserTypeRef -> original
else -> null
}
annotations += this@resolvedTypeFromPrototype.annotations
}
}
@@ -79,6 +79,11 @@ fun ConeKotlinType.isFunctionalType(session: FirSession): Boolean {
return isFunctionalType(session) { it == FunctionClassKind.Function }
}
// Function, SuspendFunction
fun ConeKotlinType.isFunctionalOrSuspendFunctionalType(session: FirSession): Boolean {
return isFunctionalType(session) { it == FunctionClassKind.Function || it == FunctionClassKind.SuspendFunction }
}
// SuspendFunction, KSuspendFunction
fun ConeKotlinType.isSuspendFunctionType(session: FirSession): Boolean {
return isFunctionalType(session) { it.isSuspendType }
@@ -25,8 +25,7 @@ import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.builder.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.fir.types.lowerBoundIfFlexible
@@ -426,15 +425,15 @@ private fun ConeKotlinType.approximateToOnlySupertype(session: FirSession): Cone
}
fun shouldApproximateLocalTypesOfNonLocalDeclaration(containingCallableVisibility: Visibility?, isInlineFunction: Boolean): Boolean {
if (containingCallableVisibility == null) {
return false
}
// Approximate types for non-private (all but package private or private) members.
// Also private inline functions, as per KT-33917.
return containingCallableVisibility == Visibilities.Public ||
containingCallableVisibility == Visibilities.Protected ||
containingCallableVisibility == Visibilities.Internal ||
containingCallableVisibility == Visibilities.Private && isInlineFunction
return when (containingCallableVisibility) {
Visibilities.Public,
Visibilities.Protected,
Visibilities.Internal -> true
Visibilities.Private -> isInlineFunction
else -> false
}
}
fun FirDeclaration.visibilityForApproximation(container: FirDeclaration?): Visibility {
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildStarProjection
import org.jetbrains.kotlin.fir.types.builder.buildTypeProjectionWithVariance
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.fir.types.impl.FirImplicitUnitTypeRef
import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.transformSingle
@@ -635,8 +636,10 @@ class FirCallCompletionResultsWriterTransformer(
}
if (finalType != null) {
val resultType = anonymousFunction.returnTypeRef.withReplacedConeType(finalType)
anonymousFunction.transformReturnTypeRef(StoreType, resultType)
if (anonymousFunction.returnTypeRef !is FirImplicitUnitTypeRef) {
val resultType = anonymousFunction.returnTypeRef.withReplacedConeType(finalType)
anonymousFunction.transformReturnTypeRef(StoreType, resultType)
}
needUpdateLambdaType = true
}
@@ -876,9 +876,10 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
initialReturnTypeRef is FirResolvedTypeRef -> {
initialReturnTypeRef.coneType
}
implicitReturns.isNotEmpty() || lambda.returnType?.isUnit == true -> {
implicitReturns.isNotEmpty() || (lambda.returnType?.isUnit == true && lambda.isLambda) -> {
// i.e., early return, e.g., l@{ ... return@l ... }
// Note that the last statement will be coerced to Unit if needed.
// also we don't coerce to Unit anonymous functions, only lambdas
session.builtinTypes.unitType.type
}
else -> {
@@ -888,11 +889,13 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
) ?: session.builtinTypes.unitType.type
}
}
lambda.replaceReturnTypeRef(
lambda.returnTypeRef.resolvedTypeFromPrototype(returnType).also {
session.lookupTracker?.recordTypeResolveAsLookup(it, lambda.source, components.file.source)
}
)
if (lambda.returnTypeRef !is FirImplicitUnitTypeRef) {
lambda.replaceReturnTypeRef(
initialReturnTypeRef.resolvedTypeFromPrototype(returnType).also {
session.lookupTracker?.recordTypeResolveAsLookup(it, lambda.source, components.file.source)
}
)
}
lambda.replaceTypeRef(
lambda.constructFunctionalTypeRef(
isSuspend = expectedTypeRef.coneTypeSafe<ConeKotlinType>()?.isSuspendFunctionType(session) == true
@@ -10,7 +10,7 @@ fun unitUnitReturn() : Unit {return Unit}
fun test1() : Any = {<!RETURN_NOT_ALLOWED!>return<!>}
fun test2() : Any = a@ {return@a 1}
fun test3() : Any { return }
fun test4(): ()-> Unit = { <!RETURN_NOT_ALLOWED!>return@test4<!> }
fun test4(): ()-> Unit = { <!RETURN_NOT_ALLOWED, RETURN_TYPE_MISMATCH!>return@test4<!> }
fun test5(): Any = l@{ return@l }
fun test6(): Any = {<!RETURN_NOT_ALLOWED!>return<!> 1}
@@ -11,8 +11,8 @@ fun main() {
val a0: () -> Int = <!INITIALIZER_TYPE_MISMATCH!>fun(): String = "1"<!>
val a1: () -> Int = <!INITIALIZER_TYPE_MISMATCH!>(fun() = "1")<!>
val a2: () -> Unit = (fun() = <!RETURN_TYPE_MISMATCH!>"1"<!>)
val a3: Unit = <!INITIALIZER_TYPE_MISMATCH!>(fun() = <!RETURN_TYPE_MISMATCH!>"1"<!>)<!>
val a2: () -> Unit = <!INITIALIZER_TYPE_MISMATCH!>(fun() = "1")<!>
val a3: Unit = <!INITIALIZER_TYPE_MISMATCH!>(fun() = "1")<!>
val a4 = (fun() = "1")
val a5 = (fun(): String = "1")
val a6: () -> Int = (fun() = 1)
@@ -36,5 +36,5 @@ fun main() {
val a18: () -> Int = <!INITIALIZER_TYPE_MISMATCH!>fun() {}<!>
val a19: () -> () -> Int = <!INITIALIZER_TYPE_MISMATCH!>fun() = fun() {}<!>
val a20: () -> () -> () -> Unit = fun() = fun() = {}
val a21: () -> () -> () -> Int = fun() = fun() = {}
val a21: () -> () -> () -> Int = <!INITIALIZER_TYPE_MISMATCH!>fun() = fun() = {}<!>
}
@@ -0,0 +1,11 @@
class Foo<out T>(val baz: Baz<T>)
class Bar {
val foo: Foo<*> = TODO()
fun <T> bar(): Baz<T> {
return <!RETURN_TYPE_MISMATCH!>foo.baz<!>
}
}
typealias Baz<T> = (@UnsafeVariance T) -> Unit
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
class Foo<out T>(val baz: Baz<T>)
class Bar {
@@ -14,7 +14,7 @@ class A() {
//more tests
val g : () -> Unit = { 42 }
val gFunction : () -> Unit = fun(): Int = 1
val gFunction : () -> Unit = <!INITIALIZER_TYPE_MISMATCH!>fun(): Int = 1<!>
val h : () -> Unit = { doSmth() }
@@ -19,7 +19,7 @@ fun main() {
val x1: suspend (Int) -> Unit = takeSuspend(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>id { it }<!>, <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>{ x -> x }<!>)
// Here, the error should be
val x2: (Int) -> Unit = takeSuspend(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>id <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH!>{ it }<!><!>, <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>{ x -> x }<!>)
val x2: (Int) -> Unit = <!INITIALIZER_TYPE_MISMATCH!>takeSuspend(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>id <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH!>{ it }<!><!>, <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>{ x -> x }<!>)<!>
val x3: suspend (Int) -> Unit = takeSimpleFunction(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>id <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH!>{ it }<!><!>, <!ARGUMENT_TYPE_MISMATCH, ARGUMENT_TYPE_MISMATCH, DEBUG_INFO_EXPRESSION_TYPE("kotlin.coroutines.SuspendFunction1<kotlin.Int, kotlin.Unit>")!>{ x -> x }<!>)
val x4: (Int) -> Unit = takeSimpleFunction(<!ARGUMENT_TYPE_MISMATCH!>id<suspend (Int) -> Unit> {}<!>, <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.Int, kotlin.Unit>")!>{}<!>)
}