Set resolved type for lambdas properly during FIR resolve
Partially done by semoro
This commit is contained in:
@@ -6,14 +6,16 @@
|
||||
package org.jetbrains.kotlin.fir.resolve
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.expandedConeType
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.fir.service
|
||||
import org.jetbrains.kotlin.fir.symbols.*
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeAbbreviatedTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
@@ -136,3 +138,22 @@ fun <T : ConeKotlinType> T.withArguments(arguments: Array<ConeKotlinTypeProjecti
|
||||
else -> error("Not supported: $this: ${this.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
fun FirFunction.constructFunctionalTypeRef(session: FirSession): FirResolvedTypeRef {
|
||||
val receiverTypeRef = when (this) {
|
||||
is FirNamedFunction -> receiverTypeRef
|
||||
is FirAnonymousFunction -> receiverTypeRef
|
||||
else -> null
|
||||
}
|
||||
val receiverType = receiverTypeRef?.coneTypeUnsafe<ConeKotlinType>()
|
||||
val parameters = valueParameters.map {
|
||||
it.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: ConeKotlinErrorType("No type for parameter")
|
||||
}
|
||||
val rawReturnType = (this as FirTypedDeclaration).returnTypeRef.coneTypeUnsafe<ConeKotlinType>()
|
||||
val receiverAndParameterTypes = listOfNotNull(receiverType) + parameters + listOf(rawReturnType)
|
||||
|
||||
val functionalTypeId = StandardClassIds.byName("Function${receiverAndParameterTypes.size - 1}")
|
||||
val functionalType = functionalTypeId(session.service()).constructType(receiverAndParameterTypes.toTypedArray(), isNullable = false)
|
||||
|
||||
return FirResolvedTypeRefImpl(session, psi, functionalType)
|
||||
}
|
||||
|
||||
+1
-1
@@ -129,7 +129,7 @@ private fun extraLambdaInfo(
|
||||
return ResolvedLambdaAtom(argument, isSuspend, receiverType, parameters, returnType, typeVariable.takeIf { newTypeVariableUsed })
|
||||
}
|
||||
|
||||
private fun extractLambdaInfoFromFunctionalType(
|
||||
internal fun extractLambdaInfoFromFunctionalType(
|
||||
expectedType: ConeKotlinType?,
|
||||
expectedTypeRef: FirTypeRef,
|
||||
argument: FirAnonymousFunction
|
||||
|
||||
+2
@@ -27,6 +27,7 @@ interface LambdaAnalyzer {
|
||||
receiverType: ConeKotlinType?,
|
||||
parameters: List<ConeKotlinType>,
|
||||
expectedReturnType: ConeKotlinType?, // null means, that return type is not proper i.e. it depends on some type variables
|
||||
rawReturnType: ConeKotlinType,
|
||||
stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>
|
||||
): Pair<List<FirExpression>, InferenceSession>
|
||||
}
|
||||
@@ -93,6 +94,7 @@ class PostponedArgumentsAnalyzer(
|
||||
receiver,
|
||||
parameters,
|
||||
expectedTypeForReturnArguments,
|
||||
rawReturnType,
|
||||
stubsForPostponedVariables
|
||||
)
|
||||
|
||||
|
||||
+73
-6
@@ -423,12 +423,78 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn
|
||||
}
|
||||
|
||||
override fun transformAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: Any?): CompositeTransformResult<FirDeclaration> {
|
||||
if (data == null) return anonymousFunction.compose()
|
||||
if (data is LambdaResolution) return transformAnonymousFunction(anonymousFunction, data).compose()
|
||||
return super.transformAnonymousFunction(anonymousFunction, data)
|
||||
return when (data) {
|
||||
null -> {
|
||||
anonymousFunction.compose()
|
||||
}
|
||||
is LambdaResolution -> {
|
||||
transformAnonymousFunctionWithLambdaResolution(anonymousFunction, data).compose()
|
||||
}
|
||||
is FirTypeRef -> {
|
||||
val resolvedLambdaAtom = (data as? FirResolvedTypeRef)?.let {
|
||||
extractLambdaInfoFromFunctionalType(
|
||||
it.type, it, anonymousFunction
|
||||
)
|
||||
}
|
||||
var af = super.transformAnonymousFunction(anonymousFunction, data).single as FirAnonymousFunction
|
||||
val valueParameters =
|
||||
if (resolvedLambdaAtom == null) af.valueParameters
|
||||
else {
|
||||
val singleParameterType = resolvedLambdaAtom.parameters.singleOrNull()
|
||||
val itParam = when {
|
||||
af.valueParameters.isEmpty() && singleParameterType != null ->
|
||||
FirValueParameterImpl(
|
||||
session,
|
||||
null,
|
||||
Name.identifier("it"),
|
||||
FirResolvedTypeRefImpl(session, null, singleParameterType, emptyList()),
|
||||
defaultValue = null,
|
||||
isCrossinline = false,
|
||||
isNoinline = false,
|
||||
isVararg = false
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
if (itParam != null) {
|
||||
listOf(itParam)
|
||||
} else {
|
||||
af.valueParameters.mapIndexed { index, param ->
|
||||
if (param.returnTypeRef is FirResolvedTypeRef) {
|
||||
param
|
||||
} else {
|
||||
param.transformReturnTypeRef(
|
||||
StoreType,
|
||||
param.returnTypeRef.resolvedTypeFromPrototype(
|
||||
resolvedLambdaAtom.parameters[index]
|
||||
)
|
||||
)
|
||||
param
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
af = af.copy(
|
||||
receiverTypeRef = af.receiverTypeRef?.takeIf { it !is FirImplicitTypeRef }
|
||||
?: resolvedLambdaAtom?.receiver?.let { af.receiverTypeRef?.resolvedTypeFromPrototype(it) },
|
||||
valueParameters = valueParameters,
|
||||
returnTypeRef = (af.returnTypeRef as? FirResolvedTypeRef)
|
||||
?: resolvedLambdaAtom?.returnType?.let { af.returnTypeRef.resolvedTypeFromPrototype(it) }
|
||||
?: af.body?.resultType?.takeIf { af.returnTypeRef is FirImplicitTypeRef }
|
||||
?: FirErrorTypeRefImpl(session, af.psi, "No result type for lambda")
|
||||
)
|
||||
af.replaceTypeRef(af.constructFunctionalTypeRef(session))
|
||||
af.compose()
|
||||
}
|
||||
else -> {
|
||||
super.transformAnonymousFunction(anonymousFunction, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun transformAnonymousFunction(anonymousFunction: FirAnonymousFunction, lambdaResolution: LambdaResolution): FirAnonymousFunction {
|
||||
private fun transformAnonymousFunctionWithLambdaResolution(
|
||||
anonymousFunction: FirAnonymousFunction, lambdaResolution: LambdaResolution
|
||||
): FirAnonymousFunction {
|
||||
val receiverTypeRef = anonymousFunction.receiverTypeRef
|
||||
fun transform(): FirAnonymousFunction {
|
||||
return withScopeCleanup(scopes) {
|
||||
@@ -559,6 +625,7 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn
|
||||
receiverType: ConeKotlinType?,
|
||||
parameters: List<ConeKotlinType>,
|
||||
expectedReturnType: ConeKotlinType?,
|
||||
rawReturnType: ConeKotlinType,
|
||||
stubsForPostponedVariables: Map<TypeVariableMarker, StubTypeMarker>
|
||||
): Pair<List<FirExpression>, InferenceSession> {
|
||||
|
||||
@@ -582,10 +649,10 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn
|
||||
valueParameters = lambdaArgument.valueParameters.mapIndexed { index, parameter ->
|
||||
parameter.transformReturnTypeRef(StoreType, parameter.returnTypeRef.resolvedTypeFromPrototype(parameters[index]))
|
||||
parameter
|
||||
} + listOfNotNull(itParam)
|
||||
} + listOfNotNull(itParam),
|
||||
returnTypeRef = lambdaArgument.returnTypeRef.resolvedTypeFromPrototype(rawReturnType)
|
||||
)
|
||||
|
||||
|
||||
val expectedReturnTypeRef = expectedReturnType?.let { newLambdaExpression.returnTypeRef.resolvedTypeFromPrototype(it) }
|
||||
replacements[lambdaArgument] =
|
||||
newLambdaExpression.transformSingle(this@FirBodyResolveTransformer, LambdaResolution(expectedReturnTypeRef))
|
||||
|
||||
+21
-2
@@ -7,16 +7,18 @@ package org.jetbrains.kotlin.fir.resolve.transformers
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.copy
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
|
||||
import org.jetbrains.kotlin.fir.resolve.constructFunctionalTypeRef
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.substituteOrNull
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.withReplacedConeType
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeProjectionWithVariance
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirTypeProjectionWithVarianceImpl
|
||||
@@ -82,4 +84,21 @@ class FirCallCompleterTransformer(
|
||||
|
||||
}
|
||||
|
||||
override fun transformAnonymousFunction(
|
||||
anonymousFunction: FirAnonymousFunction,
|
||||
data: Nothing?
|
||||
): CompositeTransformResult<FirDeclaration> {
|
||||
val initialType = anonymousFunction.returnTypeRef.coneTypeSafe<ConeKotlinType>()
|
||||
if (initialType != null) {
|
||||
val finalType = finalSubstitutor.substituteOrNull(initialType)
|
||||
|
||||
val resultType = anonymousFunction.returnTypeRef.withReplacedConeType(session, finalType)
|
||||
|
||||
anonymousFunction.transformReturnTypeRef(StoreType, resultType)
|
||||
|
||||
anonymousFunction.replaceTypeRef(anonymousFunction.constructFunctionalTypeRef(session))
|
||||
}
|
||||
return super.transformAnonymousFunction(anonymousFunction, data)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
val x = 1
|
||||
val y = 2 as Any
|
||||
|
||||
val f = fun() = 3 as Any
|
||||
val g = {}
|
||||
val h: (String) -> Boolean = { _ -> false }
|
||||
val hError = { _ -> true }
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
FILE: cast.kt
|
||||
public final val x: R|kotlin/Int| = Int(1)
|
||||
public get(): R|kotlin/Int|
|
||||
public final val y: R|kotlin/Any| = (Int(2) as R|kotlin/Any|)
|
||||
public get(): R|kotlin/Any|
|
||||
public final val f: R|kotlin/Function0<kotlin/Any>| = fun <anonymous>(): R|kotlin/Any| {
|
||||
^ (Int(3) as R|kotlin/Any|)
|
||||
}
|
||||
|
||||
public get(): R|kotlin/Function0<kotlin/Any>|
|
||||
public final val g: R|kotlin/Function0<kotlin/Unit>| = fun <anonymous>(): R|kotlin/Unit| {
|
||||
Unit
|
||||
}
|
||||
|
||||
public get(): R|kotlin/Function0<kotlin/Unit>|
|
||||
public final val h: R|kotlin/Function1<kotlin/String, kotlin/Boolean>| = fun R|kotlin/Function1<kotlin/String, kotlin/Boolean>|.<anonymous>(_: R|kotlin/String|): R|kotlin/Function1<kotlin/String, kotlin/Boolean>| {
|
||||
Boolean(false)
|
||||
}
|
||||
|
||||
public get(): R|kotlin/Function1<kotlin/String, kotlin/Boolean>|
|
||||
public final val hError: R|kotlin/Function1<class error: No type for parameter, kotlin/Boolean>| = fun <anonymous>(_: R|class error: No type for parameter|): R|kotlin/Boolean| {
|
||||
Boolean(true)
|
||||
}
|
||||
|
||||
public get(): R|kotlin/Function1<class error: No type for parameter, kotlin/Boolean>|
|
||||
@@ -4,7 +4,7 @@ FILE: functionX.kt
|
||||
}
|
||||
|
||||
public get(): R|kotlin/jvm/functions/Function0<kotlin/Int>|
|
||||
public final val y: R|kotlin/Function1<kotlin/String, kotlin/String>| = fun R|kotlin/Function1<kotlin/String, kotlin/String>|.<anonymous>(): R|kotlin/Function1<kotlin/String, kotlin/String>| {
|
||||
public final val y: R|kotlin/Function1<kotlin/String, kotlin/String>| = fun R|kotlin/Function1<kotlin/String, kotlin/String>|.<anonymous>(it: R|kotlin/String|): R|kotlin/Function1<kotlin/String, kotlin/String>| {
|
||||
<Unresolved name: it>#
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -29,6 +29,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/resolve"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true, "stdlib");
|
||||
}
|
||||
|
||||
@TestMetadata("cast.kt")
|
||||
public void testCast() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/cast.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("companion.kt")
|
||||
public void testCompanion() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/resolve/companion.kt");
|
||||
|
||||
Reference in New Issue
Block a user