From be5b7adb5a56a813b776f318eb9ab2ac2afe44ee Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 2 Dec 2019 18:16:43 +0300 Subject: [PATCH] FIR: Support captured types for sub-arguments --- .../kotlin/fir/resolve/calls/Arguments.kt | 21 ++++++++++---- .../kotlin/fir/resolve/calls/InferenceUtil.kt | 6 +--- .../kotlin/fir/resolve/calls/ResolverParts.kt | 2 +- .../fir/resolve/inference/FirCallCompleter.kt | 24 ++++++++++++---- ...rCallCompletionResultsWriterTransformer.kt | 11 ++++++-- .../receiverWithCapturedType.kt | 4 +-- .../receiverWithCapturedType.txt | 8 +++--- .../resolve/inference/simpleCapturedTypes.kt | 21 ++++++++++++++ .../resolve/inference/simpleCapturedTypes.txt | 28 +++++++++++++++++++ .../fir/FirDiagnosticsTestGenerated.java | 28 +++++++++++++++---- 10 files changed, 123 insertions(+), 30 deletions(-) rename compiler/fir/resolve/testData/resolve/{problems => inference}/receiverWithCapturedType.kt (92%) rename compiler/fir/resolve/testData/resolve/{problems => inference}/receiverWithCapturedType.txt (79%) create mode 100644 compiler/fir/resolve/testData/resolve/inference/simpleCapturedTypes.kt create mode 100644 compiler/fir/resolve/testData/resolve/inference/simpleCapturedTypes.txt 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 f1136fb83f7..3ba25d6dd5d 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 @@ -18,6 +18,7 @@ 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.types.model.CaptureStatus fun Candidate.resolveArgumentExpression( @@ -144,7 +145,7 @@ private fun Candidate.resolveBlockArgument( } } -fun resolveSubCallArgument( +fun Candidate.resolveSubCallArgument( csBuilder: ConstraintSystemBuilder, argument: FirResolvable, expectedType: ConeKotlinType, @@ -169,7 +170,7 @@ fun resolveSubCallArgument( resolvePlainArgumentType(csBuilder, argumentType, expectedType, sink, isReceiver, isDispatch, isSafeCall) } -fun resolvePlainExpressionArgument( +fun Candidate.resolvePlainExpressionArgument( csBuilder: ConstraintSystemBuilder, argument: FirExpression, expectedType: ConeKotlinType?, @@ -184,7 +185,7 @@ fun resolvePlainExpressionArgument( resolvePlainArgumentType(csBuilder, argumentType, expectedType, sink, isReceiver, isDispatch, isSafeCall) } -fun resolvePlainArgumentType( +fun Candidate.resolvePlainArgumentType( csBuilder: ConstraintSystemBuilder, argumentType: ConeKotlinType, expectedType: ConeKotlinType, @@ -195,15 +196,25 @@ fun resolvePlainArgumentType( ) { val position = SimpleConstraintSystemConstraintPosition //TODO + val capturedType = prepareCapturedType(argumentType, sink.components.session) + val nullableExpectedType = expectedType.withNullability(ConeNullability.NULLABLE) if (isReceiver && isSafeCall) { - if (!isDispatch && !csBuilder.addSubtypeConstraintIfCompatible(argumentType, nullableExpectedType, position)) { + if (!isDispatch && !csBuilder.addSubtypeConstraintIfCompatible(capturedType, nullableExpectedType, position)) { sink.reportApplicability(CandidateApplicability.WRONG_RECEIVER) // TODO } return } - checkApplicabilityForArgumentType(csBuilder, argumentType, expectedType, position, isReceiver, isDispatch, nullableExpectedType, sink) + checkApplicabilityForArgumentType(csBuilder, capturedType, expectedType, position, isReceiver, isDispatch, nullableExpectedType, sink) +} + +fun Candidate.prepareCapturedType(argumentType: ConeKotlinType, session: FirSession): ConeKotlinType { + if (argumentType.typeArguments.isEmpty() || argumentType !is ConeClassLikeType) return argumentType + + return bodyResolveComponents.inferenceComponents.ctx.captureFromArguments( + argumentType, CaptureStatus.FROM_EXPRESSION + ) as? ConeKotlinType ?: argumentType } private fun checkApplicabilityForArgumentType( diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt index ad9768464a6..86a8287cf89 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/InferenceUtil.kt @@ -9,10 +9,8 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator -import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintIncorporator import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolver @@ -22,8 +20,6 @@ import org.jetbrains.kotlin.types.AbstractTypeApproximator import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner import org.jetbrains.kotlin.types.model.SimpleTypeMarker import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContextDelegate -import org.jetbrains.kotlin.types.model.TypeVariableMarker -import org.jetbrains.kotlin.types.model.TypeVariableTypeConstructorMarker fun ConeTypeContext.hasNullableSuperType(type: ConeKotlinType): Boolean { if (type is ConeClassLikeType) return false @@ -50,7 +46,7 @@ class InferenceComponents( val returnTypeCalculator: ReturnTypeCalculator, val scopeSession: ScopeSession ) { - private val approximator = object : AbstractTypeApproximator(ctx) { + val approximator: AbstractTypeApproximator = object : AbstractTypeApproximator(ctx) { override fun createErrorType(message: String): SimpleTypeMarker { return ConeClassErrorType(message) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt index aeec397c29f..5e06d9650a3 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt @@ -116,7 +116,7 @@ internal sealed class CheckReceivers : ResolutionStage() { } else { val argumentExtensionReceiverValue = candidate.implicitExtensionReceiverValue if (argumentExtensionReceiverValue != null && explicitReceiverKind.shouldBeCheckedAgainstImplicit()) { - resolvePlainArgumentType( + candidate.resolvePlainArgumentType( candidate.csBuilder, argumentType = argumentExtensionReceiverValue.type, expectedType = candidate.substitutor.substituteOrSelf(expectedReceiverType.type), diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt index db0fc0e7533..4fef5ca0ff3 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirCallCompleter.kt @@ -22,11 +22,9 @@ import org.jetbrains.kotlin.fir.resolve.transformers.MapArguments import org.jetbrains.kotlin.fir.resolve.transformers.StoreType import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer -import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType import org.jetbrains.kotlin.fir.resolve.typeFromCallee import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype -import org.jetbrains.kotlin.fir.returnExpressions import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType import org.jetbrains.kotlin.fir.types.ConeKotlinType @@ -39,6 +37,7 @@ 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.inference.model.SimpleConstraintSystemConstraintPosition +import org.jetbrains.kotlin.types.TypeApproximatorConfiguration import org.jetbrains.kotlin.types.model.StubTypeMarker import org.jetbrains.kotlin.types.model.TypeVariableMarker @@ -97,7 +96,10 @@ class FirCallCompleter( val finalSubstitutor = candidate.system.asReadOnlyStorage().buildAbstractResultingSubstitutor(inferenceComponents.ctx) as ConeSubstitutor return call.transformSingle( - FirCallCompletionResultsWriterTransformer(session, finalSubstitutor, returnTypeCalculator), + FirCallCompletionResultsWriterTransformer( + session, finalSubstitutor, returnTypeCalculator, + inferenceComponents.approximator + ), null ) } @@ -126,7 +128,7 @@ class FirCallCompleter( FirValueParameterImpl( null, session, - FirResolvedTypeRefImpl(null, itType), + FirResolvedTypeRefImpl(null, itType.approximateLambdaInputType()), name, FirVariableSymbol(name), defaultValue = null, @@ -141,9 +143,14 @@ class FirCallCompleter( val expectedReturnTypeRef = expectedReturnType?.let { lambdaArgument.returnTypeRef.resolvedTypeFromPrototype(it) } val newLambdaExpression = lambdaArgument.copy( - receiverTypeRef = receiverType?.let { lambdaArgument.receiverTypeRef?.resolvedTypeFromPrototype(it) }, + receiverTypeRef = receiverType?.let { + lambdaArgument.receiverTypeRef?.resolvedTypeFromPrototype(it.approximateLambdaInputType()) + }, valueParameters = lambdaArgument.valueParameters.mapIndexed { index, parameter -> - parameter.transformReturnTypeRef(StoreType, parameter.returnTypeRef.resolvedTypeFromPrototype(parameters[index])) + parameter.transformReturnTypeRef( + StoreType, + parameter.returnTypeRef.resolvedTypeFromPrototype(parameters[index].approximateLambdaInputType()) + ) parameter } + listOfNotNull(itParam), returnTypeRef = expectedReturnTypeRef ?: noExpectedType @@ -156,4 +163,9 @@ class FirCallCompleter( return returnArguments to InferenceSession.default } } + + private fun ConeKotlinType.approximateLambdaInputType(): ConeKotlinType = + inferenceComponents.approximator.approximateToSuperType( + this, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference + ) as ConeKotlinType? ?: this } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt index 95c5fe99517..0635a4538f9 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt @@ -29,12 +29,15 @@ import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl import org.jetbrains.kotlin.fir.types.impl.FirTypeProjectionWithVarianceImpl import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult import org.jetbrains.kotlin.fir.visitors.compose +import org.jetbrains.kotlin.types.AbstractTypeApproximator +import org.jetbrains.kotlin.types.TypeApproximatorConfiguration import org.jetbrains.kotlin.types.Variance class FirCallCompletionResultsWriterTransformer( override val session: FirSession, private val finalSubstitutor: ConeSubstitutor, - private val typeCalculator: ReturnTypeCalculator + private val typeCalculator: ReturnTypeCalculator, + private val typeApproximator: AbstractTypeApproximator ) : FirAbstractTreeTransformer(phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) { override fun transformQualifiedAccessExpression( @@ -78,7 +81,11 @@ class FirCallCompletionResultsWriterTransformer( candidate: Candidate ): FirResolvedTypeRef { val initialType = candidate.substitutor.substituteOrNull(type) - val finalType = finalSubstitutor.substituteOrNull(initialType) + val finalType = finalSubstitutor.substituteOrNull(initialType)?.let { substitutedType -> + typeApproximator.approximateToSuperType( + substitutedType, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference + ) as ConeKotlinType? ?: substitutedType + } return withReplacedConeType(finalType) } diff --git a/compiler/fir/resolve/testData/resolve/problems/receiverWithCapturedType.kt b/compiler/fir/resolve/testData/resolve/inference/receiverWithCapturedType.kt similarity index 92% rename from compiler/fir/resolve/testData/resolve/problems/receiverWithCapturedType.kt rename to compiler/fir/resolve/testData/resolve/inference/receiverWithCapturedType.kt index 80f1a717edc..0a52fd88247 100644 --- a/compiler/fir/resolve/testData/resolve/problems/receiverWithCapturedType.kt +++ b/compiler/fir/resolve/testData/resolve/inference/receiverWithCapturedType.kt @@ -12,7 +12,7 @@ fun ResolvedCall.updateD(d: D): D { } fun test_1_1(resolvedCall: ResolvedCall) { - resolvedCall.getParameterForArgument() // should be ok + resolvedCall.getParameterForArgument() // should be ok } fun test_1_2(resolvedCall: ResolvedCall) { @@ -33,4 +33,4 @@ fun test_2_2(resolvedCall: ResolvedCall, d: CallableDescr fun test_2_3(resolvedCall: ResolvedCall, d: CallableDescriptor) { val x = resolvedCall.updateD(d) // should be ok -} \ No newline at end of file +} diff --git a/compiler/fir/resolve/testData/resolve/problems/receiverWithCapturedType.txt b/compiler/fir/resolve/testData/resolve/inference/receiverWithCapturedType.txt similarity index 79% rename from compiler/fir/resolve/testData/resolve/problems/receiverWithCapturedType.txt rename to compiler/fir/resolve/testData/resolve/inference/receiverWithCapturedType.txt index 0ef43564717..c9589ba47f9 100644 --- a/compiler/fir/resolve/testData/resolve/problems/receiverWithCapturedType.txt +++ b/compiler/fir/resolve/testData/resolve/inference/receiverWithCapturedType.txt @@ -15,19 +15,19 @@ FILE: receiverWithCapturedType.kt ^updateD R|/d| } public final fun test_1_1(resolvedCall: R|ResolvedCall|): R|kotlin/Unit| { - R|/resolvedCall|.#() + R|/resolvedCall|.R|/getParameterForArgument|() } public final fun test_1_2(resolvedCall: R|ResolvedCall|): R|kotlin/Unit| { - R|/resolvedCall|.R|/getParameterForArgument|() + R|/resolvedCall|.R|/getParameterForArgument|() } public final fun test_1_3(resolvedCall: R|ResolvedCall|): R|kotlin/Unit| { R|/resolvedCall|.R|/getParameterForArgument|() } public final fun test_2_1(resolvedCall: R|ResolvedCall|, d: R|CallableDescriptor|): R|kotlin/Unit| { - lval x: = R|/resolvedCall|.#(R|/d|) + lval x: = R|/resolvedCall|.#(R|/d|) } public final fun test_2_2(resolvedCall: R|ResolvedCall|, d: R|CallableDescriptor|): R|kotlin/Unit| { - lval x: R|CallableDescriptor| = R|/resolvedCall|.R|/updateD|(R|/d|) + lval x: R|CallableDescriptor| = R|/resolvedCall|.R|/updateD|(R|/d|) } public final fun test_2_3(resolvedCall: R|ResolvedCall|, d: R|CallableDescriptor|): R|kotlin/Unit| { lval x: R|CallableDescriptor| = R|/resolvedCall|.R|/updateD|(R|/d|) diff --git a/compiler/fir/resolve/testData/resolve/inference/simpleCapturedTypes.kt b/compiler/fir/resolve/testData/resolve/inference/simpleCapturedTypes.kt new file mode 100644 index 00000000000..76c9f0ab28f --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/inference/simpleCapturedTypes.kt @@ -0,0 +1,21 @@ +interface Ann { + fun foo() +} + +interface KC { + val x: T +} +fun id(x: KC): KC = x +fun KC.idR(): KC = this +val KC.idP: KC get() = this + +private fun getSetterInfos(kc: KC) { + id(kc).x.foo() + + kc.idR().x.foo() + kc.idP.x.foo() + + val x1 = id(kc) + val x2 = kc.idR() + val x3 = kc.idP +} diff --git a/compiler/fir/resolve/testData/resolve/inference/simpleCapturedTypes.txt b/compiler/fir/resolve/testData/resolve/inference/simpleCapturedTypes.txt new file mode 100644 index 00000000000..7a44b661a74 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/inference/simpleCapturedTypes.txt @@ -0,0 +1,28 @@ +FILE: simpleCapturedTypes.kt + public abstract interface Ann : R|kotlin/Any| { + public abstract fun foo(): R|kotlin/Unit| + + } + public abstract interface KC : R|kotlin/Any| { + public abstract val x: R|T| + public get(): R|T| + + } + public final fun id(x: R|KC|): R|KC| { + ^id R|/x| + } + public final fun R|KC|.idR(): R|KC| { + ^idR this@R|/idR| + } + public final val R|KC|.idP: R|KC| + public get(): R|KC| { + ^ this@R|/idP| + } + private final fun getSetterInfos(kc: R|KC|): R|kotlin/Unit| { + R|/id|(R|/kc|).R|FakeOverride|.R|/Ann.foo|() + R|/kc|.R|/idR|().R|FakeOverride|.R|/Ann.foo|() + R|/kc|.R|/idP|.R|FakeOverride|.R|/Ann.foo|() + lval x1: R|KC| = R|/id|(R|/kc|) + lval x2: R|KC| = R|/kc|.R|/idR|() + lval x3: R|KC| = R|/kc|.R|/idP| + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index a977cebe630..ba9c052c7f9 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -722,6 +722,29 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { } } + @TestMetadata("compiler/fir/resolve/testData/resolve/inference") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Inference extends AbstractFirDiagnosticsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInInference() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/resolve/inference"), Pattern.compile("^([^.]+)\\.kt$"), true); + } + + @TestMetadata("receiverWithCapturedType.kt") + public void testReceiverWithCapturedType() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/inference/receiverWithCapturedType.kt"); + } + + @TestMetadata("simpleCapturedTypes.kt") + public void testSimpleCapturedTypes() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/inference/simpleCapturedTypes.kt"); + } + } + @TestMetadata("compiler/fir/resolve/testData/resolve/multifile") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -928,11 +951,6 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/resolve/testData/resolve/problems/propertyFromJavaPlusAssign.kt"); } - @TestMetadata("receiverWithCapturedType.kt") - public void testReceiverWithCapturedType() throws Exception { - runTest("compiler/fir/resolve/testData/resolve/problems/receiverWithCapturedType.kt"); - } - @TestMetadata("samConversionInConstructorCall.kt") public void testSamConversionInConstructorCall() throws Exception { runTest("compiler/fir/resolve/testData/resolve/problems/samConversionInConstructorCall.kt");