Set resolved type for lambdas properly during FIR resolve

Partially done by semoro
This commit is contained in:
Mikhail Glukhikh
2019-05-20 17:57:43 +03:00
parent 963ed44ce1
commit 05e4539019
29 changed files with 272 additions and 145 deletions
@@ -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)
}
@@ -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
@@ -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
)
@@ -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))
@@ -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)
}
}
+7
View File
@@ -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
View File
@@ -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>|
+1 -1
View File
@@ -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>#
}
@@ -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");