From 4b5172cda31f5d281fc2e6f1be03ca9d80abe60d Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Tue, 16 Apr 2019 21:50:08 +0300 Subject: [PATCH] FIR (WIP): Initial support for proper lambda analysis --- .../src/org/jetbrains/kotlin/fir/CopyUtils.kt | 35 ++++ .../kotlin/fir/resolve/calls/Arguments.kt | 46 ++++- .../fir/resolve/calls/InferenceCompletion.kt | 62 ++++++- .../fir/resolve/calls/PostponedArguments.kt | 167 ++++++++++++++++++ .../calls/PostponedArgumentsAnalyzer.kt | 153 ++++++++++++++++ .../transformers/FirBodyResolveTransformer.kt | 131 +++++++++++++- .../testData/resolve/arguments/lambda.txt | 103 +++-------- .../testData/resolve/expresssions/lambda.kt | 12 ++ .../testData/resolve/expresssions/lambda.txt | 27 +++ .../testData/resolve/stdlib/functionX.txt | 10 +- .../testData/resolve/stdlib/mapList.kt | 5 + .../testData/resolve/stdlib/mapList.txt | 7 + .../fir/FirResolveTestCaseGenerated.java | 5 + ...FirResolveTestCaseWithStdlibGenerated.java | 5 + 14 files changed, 670 insertions(+), 98 deletions(-) create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArguments.kt create mode 100644 compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArgumentsAnalyzer.kt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/lambda.kt create mode 100644 compiler/fir/resolve/testData/resolve/expresssions/lambda.txt create mode 100644 compiler/fir/resolve/testData/resolve/stdlib/mapList.kt create mode 100644 compiler/fir/resolve/testData/resolve/stdlib/mapList.txt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/CopyUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/CopyUtils.kt index 709f2f7c541..f9578db2788 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/CopyUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/CopyUtils.kt @@ -6,12 +6,19 @@ package org.jetbrains.kotlin.fir import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction +import org.jetbrains.kotlin.fir.declarations.FirValueParameter +import org.jetbrains.kotlin.fir.declarations.impl.FirAnonymousFunctionImpl import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.fir.expressions.FirBlock import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.impl.FirFunctionCallImpl +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirTypeProjection import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl fun FirFunctionCall.copy( annotations: List = this.annotations, @@ -36,3 +43,31 @@ fun FirFunctionCall.copy( } } +fun FirAnonymousFunction.copy( + receiverTypeRef: FirTypeRef? = this.receiverTypeRef, + psi: PsiElement? = this.psi, + session: FirSession = this.session, + returnTypeRef: FirTypeRef = this.returnTypeRef, + valueParameters: List = this.valueParameters, + body: FirBlock? = this.body, + annotations: List = this.annotations, + typeRef: FirTypeRef = this.typeRef, + label: FirLabel? = this.label +): FirAnonymousFunction { + return FirAnonymousFunctionImpl(session, psi, returnTypeRef, receiverTypeRef).apply { + this.valueParameters.addAll(valueParameters) + this.body = body + this.annotations.addAll(annotations) + this.typeRef = typeRef + this.label = label + } +} + + +fun FirTypeRef.resolvedTypeFromPrototype( + type: ConeKotlinType +): FirResolvedTypeRef { + return FirResolvedTypeRefImpl( + session, psi, type, false, annotations + ) +} \ No newline at end of file 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 1821e287324..e7a6b534f28 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 @@ -7,15 +7,13 @@ package org.jetbrains.kotlin.fir.resolve.calls import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.declarations.FirValueParameter -import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess -import org.jetbrains.kotlin.fir.expressions.FirExpression -import org.jetbrains.kotlin.fir.expressions.FirFunctionCall -import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.resolve.withNullability import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition +import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker fun resolveArgumentExpression( @@ -31,16 +29,42 @@ fun resolveArgumentExpression( expectedType: ConeKotlinType, sink: CheckerSink, isReceiver: Boolean, + acceptLambdaAtoms: (PostponedResolvedAtomMarker) -> Unit, typeProvider: (FirExpression) -> FirTypeRef? ) { return when (argument) { - is FirQualifiedAccessExpression, is FirFunctionCall -> checkPlainExpressionArgument(csBuilder, argument, expectedType, sink, isReceiver, typeProvider) + is FirQualifiedAccessExpression, is FirFunctionCall -> checkPlainExpressionArgument( + csBuilder, + argument, + expectedType, + sink, + isReceiver, + typeProvider + ) // TODO:! - is FirAnonymousFunction -> Unit + is FirAnonymousFunction -> preprocessLambdaArgument(csBuilder, argument, expectedType, acceptLambdaAtoms) // TODO:! is FirCallableReferenceAccess -> Unit // TODO:! //TODO: Collection literal + is FirLambdaArgumentExpression -> resolveArgumentExpression( + csBuilder, + argument.expression, + expectedType, + sink, + isReceiver, + acceptLambdaAtoms, + typeProvider + ) + is FirNamedArgumentExpression -> resolveArgumentExpression( + csBuilder, + argument.expression, + expectedType, + sink, + isReceiver, + acceptLambdaAtoms, + typeProvider + ) else -> checkPlainExpressionArgument(csBuilder, argument, expectedType, sink, isReceiver, typeProvider) } } @@ -79,7 +103,15 @@ internal fun Candidate.resolveArgument( ) { val expectedType = prepareExpectedType(argument, parameter) - resolveArgumentExpression(this.system.getBuilder(), argument, expectedType, sink, isReceiver, typeProvider) + resolveArgumentExpression( + this.system.getBuilder(), + argument, + expectedType, + sink, + isReceiver, + { this.postponedAtoms += it }, + typeProvider + ) } private fun Candidate.prepareExpectedType(argument: FirExpression, parameter: FirValueParameter): ConeKotlinType { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceCompletion.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceCompletion.kt index e4c8618641c..03f105fcb3b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceCompletion.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceCompletion.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.fir.resolve.calls +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter @@ -12,9 +14,12 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirec import org.jetbrains.kotlin.resolve.calls.inference.components.VariableFixationFinder import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtom +import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.isIntegerLiteralTypeConstructor import org.jetbrains.kotlin.types.model.typeConstructor +import org.jetbrains.kotlin.utils.addIfNotNull +import org.jetbrains.kotlin.utils.addToStdlib.safeAs fun Candidate.computeCompletionMode( @@ -68,19 +73,21 @@ class ConstraintSystemCompleter(val components: InferenceComponents) { fun complete( c: KotlinConstraintSystemCompleter.Context, completionMode: KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode, - candidateReturnType: ConeKotlinType + topLevelAtoms: List, + candidateReturnType: ConeKotlinType, + analyze: (PostponedResolvedAtomMarker) -> Unit ) { while (true) { -// if (analyzePostponeArgumentIfPossible(c, topLevelAtoms, analyze)) continue + if (analyzePostponeArgumentIfPossible(c, topLevelAtoms, analyze)) continue // val allTypeVariables = getOrderedAllTypeVariables(c, collectVariablesFromContext, topLevelAtoms) val allTypeVariables = c.notFixedTypeVariables.keys.toList() -// val postponedKtPrimitives = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) + val postponedKtPrimitives = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms) val variableForFixation = variableFixationFinder.findFirstVariableForFixation( - c, allTypeVariables, emptyList(), completionMode, candidateReturnType + c, allTypeVariables, postponedKtPrimitives, completionMode, candidateReturnType ) ?: break // if (shouldForceCallableReferenceOrLambdaResolution(completionMode, variableForFixation)) { @@ -104,7 +111,7 @@ class ConstraintSystemCompleter(val components: InferenceComponents) { if (completionMode == KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL) { // force resolution for all not-analyzed argument's -// getOrderedNotAnalyzedPostponedArguments(topLevelAtoms).forEach(analyze) + getOrderedNotAnalyzedPostponedArguments(topLevelAtoms).forEach(analyze) // // if (c.notFixedTypeVariables.isNotEmpty() && c.postponedTypeVariables.isEmpty()) { // runCompletion(c, completionMode, topLevelAtoms, topLevelType, analyze) @@ -131,4 +138,49 @@ class ConstraintSystemCompleter(val components: InferenceComponents) { c.fixVariable(variableWithConstraints.typeVariable, resultType) } + private fun analyzePostponeArgumentIfPossible( + c: KotlinConstraintSystemCompleter.Context, + topLevelAtoms: List, + analyze: (PostponedResolvedAtomMarker) -> Unit + ): Boolean { + for (argument in getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)) { + if (canWeAnalyzeIt(c, argument)) { + analyze(argument) + return true + } + } + return false + } + + private fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List): List { + fun FirExpression.process(to: MutableList) { + when (this) { + is FirFunctionCall -> { + val candidate = (this.calleeReference as? FirNamedReferenceWithCandidate)?.candidate + candidate?.postponedAtoms?.forEach { + to.addIfNotNull(it.safeAs()?.takeUnless { it.analyzed }) + } + this.arguments.forEach { it.process(to) } + } + // TOOD: WTF? + } +// if (analyzed) { +// subResolvedAtoms.forEach { it.process(to) } +// } + } + + val notAnalyzedArguments = arrayListOf() + for (primitive in topLevelAtoms) { + primitive.process(notAnalyzedArguments) + } + + return notAnalyzedArguments + } + + private fun canWeAnalyzeIt(c: KotlinConstraintSystemCompleter.Context, argument: PostponedResolvedAtomMarker): Boolean { + if (argument.analyzed) return false + + return argument.inputTypes.all { c.containsOnlyFixedOrPostponedVariables(it) } + } + } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArguments.kt new file mode 100644 index 00000000000..e9d06d4a296 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArguments.kt @@ -0,0 +1,167 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.resolve.calls + +import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder +import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +fun preprocessLambdaArgument( + csBuilder: ConstraintSystemBuilder, + argument: FirAnonymousFunction, + expectedType: ConeKotlinType?, + acceptLambdaAtoms: (PostponedResolvedAtomMarker) -> Unit, + forceResolution: Boolean = false +) { + 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)) + } + + acceptLambdaAtoms(resolvedArgument) +} + + +private val ConeKotlinType.isBuiltinFunctionalType: Boolean + get () { + val type = this + return when (type) { + is ConeClassType -> type.lookupTag.classId.asString().startsWith("kotlin/Function") + else -> false + } + } + + +private val ConeKotlinType.isSuspendFunctionType: Boolean + get() { + val type = this + return when (type) { + is ConeClassType -> { + val classId = type.lookupTag.classId + classId.packageFqName.asString() == "kotlin" && classId.relativeClassName.asString().startsWith("SuspendFunction") + } + else -> false + } + } + +private val ConeKotlinType.receiverType: ConeKotlinType? get() = null +private val ConeKotlinType.returnType: ConeKotlinType + get() { + require(this is ConeClassType) + val projection = typeArguments.last() + require(projection is ConeTypedProjection) + return projection.type + } + +private val ConeKotlinType.valueParameterTypes: List + get() { + require(this is ConeClassType) + return typeArguments.dropLast(1).map { + (it as? ConeTypedProjection)?.type + } + } + +private val FirAnonymousFunction.returnType get() = returnTypeRef.coneTypeSafe() +private val FirAnonymousFunction.receiverType get() = receiverTypeRef?.coneTypeSafe() + + +private fun extraLambdaInfo( + expectedType: ConeKotlinType?, + argument: FirAnonymousFunction, + csBuilder: ConstraintSystemBuilder +): ResolvedLambdaAtom { +// val builtIns = csBuilder.builtIns + val isSuspend = expectedType?.isSuspendFunctionType ?: false + + val isFunctionSupertype = + expectedType != null && expectedType.isBuiltinFunctionalType//isNotNullOrNullableFunctionSupertype(expectedType) + val argumentAsFunctionExpression = argument//.safeAs() + + val typeVariable = TypeVariableForLambdaReturnType(argument, "_L") + + val receiverType = null//argumentAsFunctionExpression?.receiverType + val returnType = + argumentAsFunctionExpression?.returnType + ?: expectedType?.typeArguments?.singleOrNull()?.safeAs()?.type?.takeIf { isFunctionSupertype } + ?: typeVariable.defaultType + + val parameters = argument.valueParameters?.map { it.returnTypeRef.coneTypeSafe() ?: TODO("!nothing") } ?: emptyList() + + val newTypeVariableUsed = returnType == typeVariable.defaultType + if (newTypeVariableUsed) csBuilder.registerVariable(typeVariable) + + return ResolvedLambdaAtom(argument, isSuspend, receiverType, parameters, returnType, typeVariable.takeIf { newTypeVariableUsed }) +} + +private fun extractLambdaInfoFromFunctionalType(expectedType: ConeKotlinType?, argument: FirAnonymousFunction): ResolvedLambdaAtom? { + if (expectedType == null || !expectedType.isBuiltinFunctionalType) return null + val parameters = extractLambdaParameters(expectedType, argument) + + val argumentAsFunctionExpression = argument//.safeAs() + val receiverType = argumentAsFunctionExpression?.receiverType ?: expectedType.receiverType + val returnType = argumentAsFunctionExpression?.returnType ?: expectedType.returnType + + return ResolvedLambdaAtom( + argument, + expectedType.isSuspendFunctionType, + receiverType, + parameters, + returnType, + typeVariableForLambdaReturnType = null + ) +} + +private fun extractLambdaParameters(expectedType: ConeKotlinType, argument: FirAnonymousFunction): List { + val parameters = argument.valueParameters + val expectedParameters = expectedType.valueParameterTypes + if (parameters.isEmpty()) { + return expectedParameters.map { it?.type ?: TODO("any?") } + } + + return parameters.mapIndexed { index, parameter -> + parameter.returnTypeRef.coneTypeSafe() ?: expectedParameters.getOrNull(index) ?: TODO("any?")//expectedType.builtIns.nullableAnyType + } +} + +class TypeVariableForLambdaReturnType(val argument: FirAnonymousFunction, name: String) : ConeTypeVariable(name) + + +class ResolvedLambdaAtom( + val atom: FirAnonymousFunction, + val isSuspend: Boolean, + val receiver: ConeKotlinType?, + val parameters: List, + val returnType: ConeKotlinType, + val typeVariableForLambdaReturnType: TypeVariableForLambdaReturnType? +) : PostponedResolvedAtomMarker { + + override var analyzed: Boolean = false + +// lateinit var resultArguments: List +// private set + +// fun setAnalyzedResults( +// resultArguments: List, +// subResolvedAtoms: List +// ) { +// this.resultArguments = resultArguments +// setAnalyzedResults(subResolvedAtoms) +// } + + override val inputTypes: Collection get() = receiver?.let { parameters + it } ?: parameters + override val outputType: ConeKotlinType get() = returnType +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArgumentsAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArgumentsAnalyzer.kt new file mode 100644 index 00000000000..7c24b3b389e --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArgumentsAnalyzer.kt @@ -0,0 +1,153 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.resolve.calls + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.resolve.constructType +import org.jetbrains.kotlin.fir.service +import org.jetbrains.kotlin.fir.symbols.StandardClassIds.Unit +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.resolve.calls.components.InferenceSession +import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzer +import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition +import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker +import org.jetbrains.kotlin.types.model.* +import org.jetbrains.kotlin.fir.symbols.invoke + +interface LambdaAnalyzer { + fun analyzeAndGetLambdaReturnArguments( + lambdaArgument: FirAnonymousFunction, + isSuspend: Boolean, + receiverType: ConeKotlinType?, + parameters: List, + expectedReturnType: ConeKotlinType?, // null means, that return type is not proper i.e. it depends on some type variables + stubsForPostponedVariables: Map + ): Pair, InferenceSession> +} + + +class PostponedArgumentsAnalyzer( + val lambdaAnalyzer: LambdaAnalyzer, + val typeProvider: (FirExpression) -> FirTypeRef?, + val session: FirSession +) { + + fun analyze( + c: PostponedArgumentsAnalyzer.Context, +// resolutionCallbacks: KotlinResolutionCallbacks, + argument: PostponedResolvedAtomMarker + //diagnosticsHolder: KotlinDiagnosticsHolder + ) { + when (argument) { + is ResolvedLambdaAtom -> + analyzeLambda(c, argument/*, diagnosticsHolder*/) + +// is LambdaWithTypeVariableAsExpectedTypeAtom -> +// analyzeLambda( +// c, resolutionCallbacks, argument.transformToResolvedLambda(c.getBuilder()), diagnosticsHolder +// ) + +// is ResolvedCallableReferenceAtom -> +// callableReferenceResolver.processCallableReferenceArgument(c.getBuilder(), argument, diagnosticsHolder) +// +// is ResolvedCollectionLiteralAtom -> TODO("Not supported") + + else -> error("Unexpected resolved primitive: ${argument.javaClass.canonicalName}") + } + } + + + private fun analyzeLambda( + c: PostponedArgumentsAnalyzer.Context, + lambda: ResolvedLambdaAtom//, + //diagnosticHolder: KotlinDiagnosticsHolder + ) { + val unitType = Unit(session.service()).constructType(emptyArray(), false) + val stubsForPostponedVariables = c.bindingStubsForPostponedVariables() + val currentSubstitutor = c.buildCurrentSubstitutor(stubsForPostponedVariables.mapKeys { it.key.freshTypeConstructor(c) }) + + fun substitute(type: ConeKotlinType) = currentSubstitutor.safeSubstitute(c, type) as ConeKotlinType + + val receiver = lambda.receiver?.let(::substitute) + val parameters = lambda.parameters.map(::substitute) + val rawReturnType = lambda.returnType + + val expectedTypeForReturnArguments = when { + c.canBeProper(rawReturnType) -> substitute(rawReturnType) + + // For Unit-coercion + c.hasUpperOrEqualUnitConstraint(rawReturnType) -> unitType + + else -> null + } + + val (returnArguments, inferenceSession) = lambdaAnalyzer.analyzeAndGetLambdaReturnArguments( + lambda.atom, + lambda.isSuspend, + receiver, + parameters, + expectedTypeForReturnArguments, + stubsForPostponedVariables + ) + + returnArguments.forEach { c.addSubsystemFromExpression(it) } + + val checkerSink: CheckerSink = CheckerSinkImpl() + + val subResolvedKtPrimitives = returnArguments.map { + var atom: PostponedResolvedAtomMarker? = null + resolveArgumentExpression( + c.getBuilder(), + it, + lambda.returnType.let(::substitute), + checkerSink, + false, + { atom = it }, + typeProvider + ) +// resolveKtPrimitive( +// c.getBuilder(), it, lambda.returnType.let(::substitute), diagnosticHolder, isReceiver = false +// ) + } + + if (returnArguments.isEmpty()) { +// val unitType = + val lambdaReturnType = lambda.returnType.let(::substitute) + c.getBuilder().addSubtypeConstraint( + lambdaReturnType, + unitType, /*LambdaArgumentConstraintPosition(lambda)*/ + SimpleConstraintSystemConstraintPosition + ) + c.getBuilder().addSubtypeConstraint( + unitType, + lambdaReturnType, /*LambdaArgumentConstraintPosition(lambda)*/ + SimpleConstraintSystemConstraintPosition + ) + } + + lambda.analyzed = true + //lambda.setAnalyzedResults(returnArguments, subResolvedKtPrimitives) + +// if (inferenceSession != null) { +// val storageSnapshot = c.getBuilder().currentStorage() +// +// val postponedVariables = inferenceSession.inferPostponedVariables(lambda, storageSnapshot) +// +// for ((constructor, resultType) in postponedVariables) { +// val variableWithConstraints = storageSnapshot.notFixedTypeVariables[constructor] ?: continue +// val variable = variableWithConstraints.typeVariable +// +// c.getBuilder().unmarkPostponedVariable(variable) +// c.getBuilder().addEqualityConstraint(variable.defaultType(c), resultType, CoroutinePosition()) +// } +// } + } +} + + diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt index 984e24c9033..5548a7c33af 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.resolve.transformers import com.google.common.collect.LinkedHashMultimap import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.impl.FirValueParameterImpl import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.references.FirErrorNamedReference import org.jetbrains.kotlin.fir.references.FirSimpleNamedReference @@ -29,6 +30,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConstKind import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator +import org.jetbrains.kotlin.resolve.calls.components.InferenceSession import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator @@ -249,6 +251,40 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn return transformCallee(variableAssignment).compose() } + override fun transformAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: Any?): CompositeTransformResult { + if (data == null) return anonymousFunction.compose() + if (data is LambdaResolution) return transformAnonymousFunction(anonymousFunction, data).compose() + return super.transformAnonymousFunction(anonymousFunction, data) + } + + fun transformAnonymousFunction(anonymousFunction: FirAnonymousFunction, lambdaResolution: LambdaResolution): FirAnonymousFunction { + val receiverTypeRef = anonymousFunction.receiverTypeRef + fun transform(): FirAnonymousFunction { + return withScopeCleanup(scopes) { + scopes.addIfNotNull(receiverTypeRef?.coneTypeSafe()?.scope(session, ScopeSession())) + val result = + super.transformAnonymousFunction( + anonymousFunction, + lambdaResolution.expectedReturnTypeRef ?: anonymousFunction.returnTypeRef + ).single as FirAnonymousFunction + val body = result.body + if (result.returnTypeRef is FirImplicitTypeRef && body != null) { + result.transformReturnTypeRef(this, body.resultType) + result + } else { + result + } + } + } + + val label = anonymousFunction.label + return if (label != null && receiverTypeRef != null) { + withLabel(Name.identifier(label.name), receiverTypeRef.coneTypeUnsafe()) { transform() } + } else { + transform() + } + } + private fun resolveCallAndSelectCandidate(functionCall: FirFunctionCall, expectedTypeRef: FirTypeRef?): FirFunctionCall { val functionCall = functionCall.transformChildren(this, null) as FirFunctionCall @@ -314,6 +350,8 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn return resultExpression } + data class LambdaResolution(val expectedReturnTypeRef: FirResolvedTypeRef?) + private fun completeTypeInference(functionCall: FirFunctionCall, expectedTypeRef: FirTypeRef?): FirFunctionCall { val typeRef = typeFromCallee(functionCall) if (typeRef.type is ConeKotlinErrorType) { @@ -327,7 +365,61 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn val completionMode = candidate.computeCompletionMode(inferenceComponents, expectedTypeRef, initialType) val completer = ConstraintSystemCompleter(inferenceComponents) - completer.complete(candidate.system.asConstraintSystemCompleterContext(), completionMode, initialType) + val replacements = mutableMapOf() + + val analyzer = PostponedArgumentsAnalyzer(object : LambdaAnalyzer { + override fun analyzeAndGetLambdaReturnArguments( + lambdaArgument: FirAnonymousFunction, + isSuspend: Boolean, + receiverType: ConeKotlinType?, + parameters: List, + expectedReturnType: ConeKotlinType?, + stubsForPostponedVariables: Map + ): Pair, InferenceSession> { + + val itParam = when { + lambdaArgument.valueParameters.isEmpty() && parameters.size == 1 -> + FirValueParameterImpl( + session, + null, + Name.identifier("it"), + FirResolvedTypeRefImpl(session, null, parameters.single(), false, emptyList()), + null, + false, + false, + false + ) + else -> null + } + + val newLambdaExpression = lambdaArgument.copy( + receiverTypeRef = receiverType?.let { lambdaArgument.receiverTypeRef!!.resolvedTypeFromPrototype(it) }, + valueParameters = lambdaArgument.valueParameters.mapIndexed { index, parameter -> + parameter.transformReturnTypeRef(StoreType, parameter.returnTypeRef.resolvedTypeFromPrototype(parameters[index])) + parameter + } + listOfNotNull(itParam) + ) + + + val expectedReturnTypeRef = expectedReturnType?.let { newLambdaExpression.returnTypeRef.resolvedTypeFromPrototype(it) } + replacements[lambdaArgument] = + newLambdaExpression.transformSingle(this@FirBodyResolveTransformer, LambdaResolution(expectedReturnTypeRef)) + + + return listOfNotNull(newLambdaExpression.body?.statements?.lastOrNull() as? FirExpression) to InferenceSession.default + } + + }, { it.resultType }, session) + + completer.complete(candidate.system.asConstraintSystemCompleterContext(), completionMode, listOf(functionCall), initialType) { + analyzer.analyze( + candidate.system.asPostponedArgumentsAnalyzerContext(), + it +// diagnosticsHolder + ) + } + + functionCall.transformChildren(ReplaceInArguments, replacements.toMap()) if (completionMode == KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL) { @@ -712,4 +804,41 @@ private object StoreNameReference : FirTransformer() { ): CompositeTransformResult { return data.compose() } +} + +private object StoreType : FirTransformer() { + override fun transformElement(element: E, data: FirResolvedTypeRef): CompositeTransformResult { + return element.compose() + } + + override fun transformTypeRef(typeRef: FirTypeRef, data: FirResolvedTypeRef): CompositeTransformResult { + return data.compose() + } +} + +private object ReplaceInArguments : FirTransformer>() { + override fun transformElement(element: E, data: Map): CompositeTransformResult { + return ((data[element] ?: element) as E).compose() + } + + override fun transformFunctionCall( + functionCall: FirFunctionCall, + data: Map + ): CompositeTransformResult { + return (functionCall.transformChildren(this, data) as FirStatement).compose() + } + + override fun transformNamedArgumentExpression( + namedArgumentExpression: FirNamedArgumentExpression, + data: Map + ): CompositeTransformResult { + return (namedArgumentExpression.transformChildren(this, data) as FirStatement).compose() + } + + override fun transformLambdaArgumentExpression( + lambdaArgumentExpression: FirLambdaArgumentExpression, + data: Map + ): CompositeTransformResult { + return (lambdaArgumentExpression.transformChildren(this, data) as FirStatement).compose() + } } \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/arguments/lambda.txt b/compiler/fir/resolve/testData/resolve/arguments/lambda.txt index 3e9f03d753e..65b9430265d 100644 --- a/compiler/fir/resolve/testData/resolve/arguments/lambda.txt +++ b/compiler/fir/resolve/testData/resolve/arguments/lambda.txt @@ -6,122 +6,71 @@ FILE: lambda.kt public final fun baz(f: R|kotlin/Function0|, other: R|kotlin/Boolean| = Boolean(true)): R|kotlin/Unit| { } public final fun test(): R|kotlin/Unit| { - R|/foo|( = foo@fun .(): { - ^ { - Unit - } - + R|/foo|( = foo@fun (): R|kotlin/Unit| { + Unit } ) - R|/foo|( = foo@fun .(): { - ^ { - Unit - } - + R|/foo|( = foo@fun (): R|kotlin/Unit| { + Unit } ) - R|/foo|(foo@fun .(): { - ^ { - Unit - } - + R|/foo|(foo@fun (): R|kotlin/Unit| { + Unit } ) #(Int(1), = foo@fun .(): { - ^ { - Unit - } - + Unit } ) #(f = foo@fun .(): { - ^ { - Unit - } - + Unit } , = foo@fun .(): { - ^ { - Unit - } - + Unit } ) - R|/bar|(Int(1), = bar@fun .(): { - ^ { - Unit - } - + R|/bar|(Int(1), = bar@fun (): R|kotlin/Unit| { + Unit } ) - R|/bar|(x = Int(1), = bar@fun .(): { - ^ { - Unit - } - + R|/bar|(x = Int(1), = bar@fun (): R|kotlin/Unit| { + Unit } ) - R|/bar|(Int(1), bar@fun .(): { - ^ { - Unit - } - + R|/bar|(Int(1), bar@fun (): R|kotlin/Unit| { + Unit } ) - R|/bar|(x = Int(1), f = bar@fun .(): { - ^ { - Unit - } - + R|/bar|(x = Int(1), f = bar@fun (): R|kotlin/Unit| { + Unit } ) #( = bar@fun .(): { - ^ { - Unit - } - + Unit } ) #(bar@fun .(): { - ^ { - Unit - } - + Unit } ) - R|/baz|(other = Boolean(false), f = baz@fun .(): { - ^ { - Unit - } - + R|/baz|(other = Boolean(false), f = baz@fun (): R|kotlin/Unit| { + Unit } ) - R|/baz|(baz@fun .(): { - ^ { - Unit - } - + R|/baz|(baz@fun (): R|kotlin/Unit| { + Unit } , Boolean(false)) #( = baz@fun .(): { - ^ { - Unit - } - + Unit } ) #( = baz@fun .(): { - ^ { - Unit - } - + Unit } ) #(other = Boolean(false), = baz@fun .(): { - ^ { - Unit - } - + Unit } ) } diff --git a/compiler/fir/resolve/testData/resolve/expresssions/lambda.kt b/compiler/fir/resolve/testData/resolve/expresssions/lambda.kt new file mode 100644 index 00000000000..71bd7ab4784 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/lambda.kt @@ -0,0 +1,12 @@ +fun foo(block: () -> Unit) {} +fun bar(block: () -> String) {} +fun itIs(block: (String) -> String) {} +fun multipleArgs(block: (String, String) -> String) {} + +fun main() { + foo { "This is test" } + bar { "This is also test" } + itIs { "this is $it test" } + multipleArgs { a, b -> "This is test of $a, $b" } +} + diff --git a/compiler/fir/resolve/testData/resolve/expresssions/lambda.txt b/compiler/fir/resolve/testData/resolve/expresssions/lambda.txt new file mode 100644 index 00000000000..1f0e228efc1 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/expresssions/lambda.txt @@ -0,0 +1,27 @@ +FILE: lambda.kt + public final fun foo(block: R|kotlin/Function0|): R|kotlin/Unit| { + } + public final fun bar(block: R|kotlin/Function0|): R|kotlin/Unit| { + } + public final fun itIs(block: R|kotlin/Function1|): R|kotlin/Unit| { + } + public final fun multipleArgs(block: R|kotlin/Function2|): R|kotlin/Unit| { + } + public final fun main(): R|kotlin/Unit| { + R|/foo|( = foo@fun (): R|kotlin/Unit| { + String(This is test) + } + ) + R|/bar|( = bar@fun (): R|kotlin/String| { + String(This is also test) + } + ) + R|/itIs|( = itIs@fun (it: R|kotlin/String|): R|kotlin/String| { + (String(this is ), R|/it|, String( test)) + } + ) + R|/multipleArgs|( = multipleArgs@fun (a: R|kotlin/String|, b: R|kotlin/String|): R|kotlin/String| { + (String(This is test of ), R|/a|, String(, ), R|/b|) + } + ) + } diff --git a/compiler/fir/resolve/testData/resolve/stdlib/functionX.txt b/compiler/fir/resolve/testData/resolve/stdlib/functionX.txt index 62627337fda..ee2b6312de4 100644 --- a/compiler/fir/resolve/testData/resolve/stdlib/functionX.txt +++ b/compiler/fir/resolve/testData/resolve/stdlib/functionX.txt @@ -1,17 +1,11 @@ FILE: functionX.kt public final val x: R|kotlin/jvm/functions/Function0| = fun R|kotlin/jvm/functions/Function0|.(): R|kotlin/jvm/functions/Function0| { - ^ { - Int(42) - } - + Int(42) } public get(): R|kotlin/jvm/functions/Function0| public final val y: R|kotlin/Function1| = fun R|kotlin/Function1|.(): R|kotlin/Function1| { - ^ { - # - } - + # } public get(): R|kotlin/Function1| diff --git a/compiler/fir/resolve/testData/resolve/stdlib/mapList.kt b/compiler/fir/resolve/testData/resolve/stdlib/mapList.kt new file mode 100644 index 00000000000..c403f81697d --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/stdlib/mapList.kt @@ -0,0 +1,5 @@ +fun main() { + val x = List(10) { + "number = $it" + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/testData/resolve/stdlib/mapList.txt b/compiler/fir/resolve/testData/resolve/stdlib/mapList.txt new file mode 100644 index 00000000000..0e97df86f39 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/stdlib/mapList.txt @@ -0,0 +1,7 @@ +FILE: mapList.kt + public final fun main(): R|kotlin/Unit| { + lval x: R|kotlin/collections/List| = R|kotlin/collections/List|(Int(10), = List@fun (it: R|kotlin/Int|): R|kotlin/String| { + (String(number = ), R|/it|) + } + ) + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java index 9aa99991a1f..dae6d941285 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseGenerated.java @@ -212,6 +212,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase { runTest("compiler/fir/resolve/testData/resolve/expresssions/dispatchReceiver.kt"); } + @TestMetadata("lambda.kt") + public void testLambda() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/expresssions/lambda.kt"); + } + @TestMetadata("localImplicitBodies.kt") public void testLocalImplicitBodies() throws Exception { runTest("compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.kt"); diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java index 8893b6983b4..9d4f903043a 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirResolveTestCaseWithStdlibGenerated.java @@ -44,6 +44,11 @@ public class FirResolveTestCaseWithStdlibGenerated extends AbstractFirResolveTes runTest("compiler/fir/resolve/testData/resolve/stdlib/helloWorld.kt"); } + @TestMetadata("mapList.kt") + public void testMapList() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/stdlib/mapList.kt"); + } + @TestMetadata("reflectionClass.kt") public void testReflectionClass() throws Exception { runTest("compiler/fir/resolve/testData/resolve/stdlib/reflectionClass.kt");