From 873224dfbc7ac1249e5c6662fa99ceae200dc1db Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Sat, 29 Aug 2020 18:10:41 +0300 Subject: [PATCH] [NI] Use new type substitutor for known type parameters Use known type parameters substitutor after substitutor for fresh variables. The old logic of substituions had the following order: - replace known type parameters - replace type parameters with type variables - complete inference - replace type variables with inferred types According to the updated logic, replacement goes as follows: - replace type parameters with type variables - replace known type parameters; if they were variables, this will effectively remove them from inference - complete inference - replace remaining type variables with inferred types Support projection substitution in new type substitutor. It is needed for correct interaction with old type substitutor. Old type substitutors can contain mappings constructor -> projection which couldn't be expressed correctly with existing substitutor API in some cases. ^KT-41386 Fixed --- ...irOldFrontendDiagnosticsTestGenerated.java | 5 ++ .../tower/KotlinToResolvedCallTransformer.kt | 7 +-- .../calls/components/ResolutionParts.kt | 45 ++++++++++++--- .../components/NewTypeSubstitutor.kt | 55 +++++++++++++++---- .../resolve/calls/model/ResolutionAtoms.kt | 2 +- .../calls/model/ResolutionCandidate.kt | 3 +- .../diagnostics/tests/generics/kt5508.fir.kt | 1 - .../diagnostics/tests/generics/kt5508.kt | 3 +- .../inference/regressions/kt41386.fir.kt | 8 +++ .../tests/inference/regressions/kt41386.kt | 8 +++ .../tests/inference/regressions/kt41386.txt | 11 ++++ .../checkers/DiagnosticsTestGenerated.java | 5 ++ .../DiagnosticsUsingJavacTestGenerated.java | 5 ++ 13 files changed, 129 insertions(+), 29 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/regressions/kt41386.fir.kt create mode 100644 compiler/testData/diagnostics/tests/inference/regressions/kt41386.kt create mode 100644 compiler/testData/diagnostics/tests/inference/regressions/kt41386.txt diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java index 808e1c71be3..fbf4c11121a 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirOldFrontendDiagnosticsTestGenerated.java @@ -11937,6 +11937,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte runTest("compiler/testData/diagnostics/tests/inference/regressions/kt38691.kt"); } + @TestMetadata("kt41386.kt") + public void testKt41386() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/regressions/kt41386.kt"); + } + @TestMetadata("kt4420.kt") public void testKt4420() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/regressions/kt4420.kt"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt index efe8885fb2f..8750da26e65 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinToResolvedCallTransformer.kt @@ -30,7 +30,6 @@ import org.jetbrains.kotlin.resolve.calls.inference.buildResultingSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.components.FreshVariableNewTypeSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutorByConstructorMap -import org.jetbrains.kotlin.resolve.calls.inference.components.composeWith import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.calls.inference.substitute import org.jetbrains.kotlin.resolve.calls.inference.substituteAndApproximateTypes @@ -809,10 +808,10 @@ class NewResolvedCallImpl( shouldApproximate: Boolean = true ): CallableDescriptor { val inferredTypeVariablesSubstitutor = substitutor ?: FreshVariableNewTypeSubstitutor.Empty - val compositeSubstitutor = inferredTypeVariablesSubstitutor.composeWith(resolvedCallAtom.knownParametersSubstitutor) - return substitute(resolvedCallAtom.freshVariablesSubstitutor) - .substituteAndApproximateTypes(compositeSubstitutor, if (shouldApproximate) typeApproximator else null) + // TODO: merge last two substitutors to avoid redundant descriptor substitutions + return substitute(resolvedCallAtom.freshVariablesSubstitutor).substitute(resolvedCallAtom.knownParametersSubstitutor) + .substituteAndApproximateTypes(inferredTypeVariablesSubstitutor, if (shouldApproximate) typeApproximator else null) } fun getArgumentTypeForConstantConvertedArgument(valueArgument: ValueArgument): IntegerValueTypeConstant? { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt index c1ee1c79eea..acc53f2b50f 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/ResolutionParts.kt @@ -12,7 +12,7 @@ import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMapper.TypeArgumentsMapping.NoExplicitArguments import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem -import org.jetbrains.kotlin.resolve.calls.inference.components.FreshVariableNewTypeSubstitutor +import org.jetbrains.kotlin.resolve.calls.inference.components.* import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.calls.inference.substitute import org.jetbrains.kotlin.resolve.calls.model.* @@ -115,14 +115,22 @@ internal object NoArguments : ResolutionPart() { internal object CreateFreshVariablesSubstitutor : ResolutionPart() { override fun KotlinResolutionCandidate.process(workIndex: Int) { - resolvedCall.knownParametersSubstitutor = knownTypeParametersResultingSubstitutor ?: TypeSubstitutor.EMPTY + val toFreshVariables = + if (candidateDescriptor.typeParameters.isEmpty()) + FreshVariableNewTypeSubstitutor.Empty + else + createToFreshVariableSubstitutorAndAddInitialConstraints(candidateDescriptor, csBuilder) + + val knownTypeParametersSubstitutor = knownTypeParametersResultingSubstitutor?.let { + createKnownParametersFromFreshVariablesSubstitutor(toFreshVariables, knownTypeParametersResultingSubstitutor) + } ?: EmptySubstitutor + + resolvedCall.freshVariablesSubstitutor = toFreshVariables + resolvedCall.knownParametersSubstitutor = knownTypeParametersSubstitutor if (candidateDescriptor.typeParameters.isEmpty()) { - resolvedCall.freshVariablesSubstitutor = FreshVariableNewTypeSubstitutor.Empty return } - val toFreshVariables = createToFreshVariableSubstitutorAndAddInitialConstraints(candidateDescriptor, csBuilder) - resolvedCall.freshVariablesSubstitutor = toFreshVariables // bad function -- error on declaration side if (csBuilder.hasContradiction) return @@ -176,6 +184,27 @@ internal object CreateFreshVariablesSubstitutor : ResolutionPart() { KotlinTypeFactory.flexibleType(type.makeNotNullable().lowerIfFlexible(), type.makeNullable().upperIfFlexible()) } else type + private fun createKnownParametersFromFreshVariablesSubstitutor( + freshVariableSubstitutor: FreshVariableNewTypeSubstitutor, + knownTypeParametersSubstitutor: TypeSubstitutor, + ): NewTypeSubstitutor { + if (knownTypeParametersSubstitutor.isEmpty) + return EmptySubstitutor + + val knownTypeParameterByTypeVariable = mutableMapOf().let { map -> + for (typeVariable in freshVariableSubstitutor.freshVariables) { + val typeParameterType = typeVariable.originalTypeParameter.defaultType + val substitutedKnownTypeParameter = knownTypeParametersSubstitutor.substitute(typeParameterType) + + if (substitutedKnownTypeParameter !== typeParameterType) + map[typeVariable.defaultType.constructor] = substitutedKnownTypeParameter + } + map + } + + return knownTypeParametersSubstitutor.composeWith(NewTypeSubstitutorByConstructorMap(knownTypeParameterByTypeVariable)) + } + fun createToFreshVariableSubstitutorAndAddInitialConstraints( candidateDescriptor: CallableDescriptor, csBuilder: ConstraintSystemOperation @@ -595,8 +624,8 @@ private fun KotlinResolutionCandidate.checkUnsafeImplicitInvokeAfterSafeCall(arg } private fun KotlinResolutionCandidate.prepareExpectedType(expectedType: UnwrappedType): UnwrappedType { - val resultType = knownTypeParametersResultingSubstitutor?.substitute(expectedType) ?: expectedType - return resolvedCall.freshVariablesSubstitutor.safeSubstitute(resultType) + val resultType = resolvedCall.freshVariablesSubstitutor.safeSubstitute(expectedType) + return resolvedCall.knownParametersSubstitutor.safeSubstitute(resultType) } internal object CheckReceivers : ResolutionPart() { @@ -714,7 +743,7 @@ internal object ErrorDescriptorResolutionPart : ResolutionPart() { resolvedCall.typeArgumentMappingByOriginal = TypeArgumentsToParametersMapper.TypeArgumentsMapping.NoExplicitArguments resolvedCall.argumentMappingByOriginal = emptyMap() resolvedCall.freshVariablesSubstitutor = FreshVariableNewTypeSubstitutor.Empty - resolvedCall.knownParametersSubstitutor = TypeSubstitutor.EMPTY + resolvedCall.knownParametersSubstitutor = EmptySubstitutor resolvedCall.argumentToCandidateParameter = emptyMap() kotlinCall.explicitReceiver?.safeAs()?.let { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt index 4bb9ca05567..f637d19717a 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/NewTypeSubstitutor.kt @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.types.checker.NewCapturedTypeConstructor import org.jetbrains.kotlin.types.checker.intersectTypes import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker -interface NewTypeSubstitutor: TypeSubstitutorMarker { +interface NewTypeSubstitutor : TypeSubstitutorMarker { fun substituteNotNullTypeWithConstructor(constructor: TypeConstructor): UnwrappedType? fun safeSubstitute(type: UnwrappedType): UnwrappedType = @@ -23,6 +23,15 @@ interface NewTypeSubstitutor: TypeSubstitutorMarker { val isEmpty: Boolean + /** + * Returns not null when substitutor manages specific type projection substitution by itself. + * Intended for corner cases involving interactions with legacy type substitutor, + * please consider using substituteNotNullTypeWithConstructor instead of making manual projection substitutions. + */ + fun substituteArgumentProjection(argument: TypeProjection): TypeProjection? { + return null + } + private fun substitute(type: UnwrappedType, keepAnnotation: Boolean, runCapturedChecks: Boolean): UnwrappedType? = when (type) { is SimpleType -> substitute(type, keepAnnotation, runCapturedChecks) @@ -165,6 +174,13 @@ interface NewTypeSubstitutor: TypeSubstitutorMarker { val argument = arguments[index] if (argument.isStarProjection) continue + + val specialProjectionSubstitution = substituteArgumentProjection(argument) + if (specialProjectionSubstitution != null) { + newArguments[index] = specialProjectionSubstitution + continue + } + val substitutedArgumentType = substitute(argument.type.unwrap(), keepAnnotation, runCapturedChecks) ?: continue newArguments[index] = TypeProjectionImpl(argument.projectionKind, substitutedArgumentType) @@ -205,24 +221,39 @@ class FreshVariableNewTypeSubstitutor(val freshVariables: List abstract val freshVariablesSubstitutor: FreshVariableNewTypeSubstitutor - abstract val knownParametersSubstitutor: TypeSubstitutor + abstract val knownParametersSubstitutor: NewTypeSubstitutor abstract val argumentsWithConversion: Map abstract val argumentsWithSuspendConversion: Map abstract val argumentsWithUnitConversion: Map diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt index 2fff5ea4195..1909bafffa2 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/ResolutionCandidate.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.calls.components.* import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem import org.jetbrains.kotlin.resolve.calls.inference.components.FreshVariableNewTypeSubstitutor +import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError import org.jetbrains.kotlin.resolve.calls.inference.model.LowerPriorityToPreserveCompatibility @@ -194,7 +195,7 @@ class MutableResolvedCallAtom( override lateinit var typeArgumentMappingByOriginal: TypeArgumentsToParametersMapper.TypeArgumentsMapping override lateinit var argumentMappingByOriginal: Map override lateinit var freshVariablesSubstitutor: FreshVariableNewTypeSubstitutor - override lateinit var knownParametersSubstitutor: TypeSubstitutor + override lateinit var knownParametersSubstitutor: NewTypeSubstitutor lateinit var argumentToCandidateParameter: Map private var samAdapterMap: HashMap? = null private var suspendAdapterMap: HashMap? = null diff --git a/compiler/testData/diagnostics/tests/generics/kt5508.fir.kt b/compiler/testData/diagnostics/tests/generics/kt5508.fir.kt index 7d0c2dfac64..c9daf5ae02c 100644 --- a/compiler/testData/diagnostics/tests/generics/kt5508.fir.kt +++ b/compiler/testData/diagnostics/tests/generics/kt5508.fir.kt @@ -1,4 +1,3 @@ -// !WITH_NEW_INFERENCE // KT-5508 Stackoverflow in type substitution abstract class A { diff --git a/compiler/testData/diagnostics/tests/generics/kt5508.kt b/compiler/testData/diagnostics/tests/generics/kt5508.kt index 4c13070bdfc..f1bbe71ad06 100644 --- a/compiler/testData/diagnostics/tests/generics/kt5508.kt +++ b/compiler/testData/diagnostics/tests/generics/kt5508.kt @@ -1,4 +1,3 @@ -// !WITH_NEW_INFERENCE // KT-5508 Stackoverflow in type substitution abstract class A { @@ -6,7 +5,7 @@ abstract class A { public abstract fun bar(x: T) public inner abstract class B : A>() { - public inner class C : B>() + public inner class C : B>() { // Here B> means A.B>.B.B.C>>.B.B>.B.B.C>.C> // while for being a correct override it should be A.B>.B.B.C> diff --git a/compiler/testData/diagnostics/tests/inference/regressions/kt41386.fir.kt b/compiler/testData/diagnostics/tests/inference/regressions/kt41386.fir.kt new file mode 100644 index 00000000000..81e07c9dfc7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/regressions/kt41386.fir.kt @@ -0,0 +1,8 @@ +// !LANGUAGE: +NewInference + +open class Test(val map1 : Map, val map2 : Map) { + open val inverse: Test = object : Test(map2, map1) { + override val inverse: Test + get() = this@Test + } +} diff --git a/compiler/testData/diagnostics/tests/inference/regressions/kt41386.kt b/compiler/testData/diagnostics/tests/inference/regressions/kt41386.kt new file mode 100644 index 00000000000..39ec46c8ec7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/regressions/kt41386.kt @@ -0,0 +1,8 @@ +// !LANGUAGE: +NewInference + +open class Test(val map1 : Map, val map2 : Map) { + open val inverse: Test = object : Test(map2, map1) { + override val inverse: Test + get() = this@Test + } +} diff --git a/compiler/testData/diagnostics/tests/inference/regressions/kt41386.txt b/compiler/testData/diagnostics/tests/inference/regressions/kt41386.txt new file mode 100644 index 00000000000..2906ee3eb9f --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/regressions/kt41386.txt @@ -0,0 +1,11 @@ +package + +public open class Test { + public constructor Test(/*0*/ map1: kotlin.collections.Map, /*1*/ map2: kotlin.collections.Map) + public open val inverse: Test + public final val map1: kotlin.collections.Map + public final val map2: kotlin.collections.Map + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 1542c3b5490..123e845722f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -11944,6 +11944,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali runTest("compiler/testData/diagnostics/tests/inference/regressions/kt38691.kt"); } + @TestMetadata("kt41386.kt") + public void testKt41386() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/regressions/kt41386.kt"); + } + @TestMetadata("kt4420.kt") public void testKt4420() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/regressions/kt4420.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 282110adefc..f4d086bd377 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -11939,6 +11939,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing runTest("compiler/testData/diagnostics/tests/inference/regressions/kt38691.kt"); } + @TestMetadata("kt41386.kt") + public void testKt41386() throws Exception { + runTest("compiler/testData/diagnostics/tests/inference/regressions/kt41386.kt"); + } + @TestMetadata("kt4420.kt") public void testKt4420() throws Exception { runTest("compiler/testData/diagnostics/tests/inference/regressions/kt4420.kt");