[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
This commit is contained in:
Pavel Kirpichenkov
2020-08-29 18:10:41 +03:00
parent c706673de9
commit 873224dfbc
13 changed files with 129 additions and 29 deletions
@@ -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");
@@ -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<D : CallableDescriptor>(
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? {
@@ -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<TypeConstructor, UnwrappedType>().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<SimpleKotlinCallArgument>()?.let {
@@ -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<TypeVariableFromC
}
}
fun createCompositeSubstitutor(appliedFirst: NewTypeSubstitutor, appliedLast: TypeSubstitutor): NewTypeSubstitutor {
if (appliedLast.isEmpty) return appliedFirst
fun createCompositeSubstitutor(appliedFirst: TypeSubstitutor, appliedLast: NewTypeSubstitutor): NewTypeSubstitutor {
if (appliedFirst.isEmpty) return appliedLast
return object : NewTypeSubstitutor {
override fun substituteNotNullTypeWithConstructor(constructor: TypeConstructor): UnwrappedType? {
val substitutedOnce = appliedFirst.substituteNotNullTypeWithConstructor(constructor)
override fun substituteArgumentProjection(argument: TypeProjection): TypeProjection? {
val substitutedProjection = appliedFirst.substitute(argument)
return if (substitutedOnce != null) {
appliedLast.substitute(substitutedOnce.unwrap())
if (substitutedProjection == null || substitutedProjection === argument) {
return null
}
if (substitutedProjection.isStarProjection)
return substitutedProjection
val resultingType = appliedLast.safeSubstitute(substitutedProjection.type.unwrap())
return TypeProjectionImpl(substitutedProjection.projectionKind, resultingType)
}
override fun substituteNotNullTypeWithConstructor(constructor: TypeConstructor): UnwrappedType? {
val substitutedOnce = constructor.declarationDescriptor?.defaultType?.let {
appliedFirst.substitute(it)
}
return if (substitutedOnce == null) {
appliedLast.substituteNotNullTypeWithConstructor(constructor)
} else {
constructor.declarationDescriptor?.defaultType?.let {
appliedLast.substitute(it)
}
appliedLast.safeSubstitute(substitutedOnce)
}
}
override val isEmpty: Boolean get() = appliedFirst.isEmpty && appliedLast.isEmpty
override val isEmpty: Boolean
get() = appliedFirst.isEmpty && appliedLast.isEmpty
}
}
fun NewTypeSubstitutor.composeWith(appliedAfter: TypeSubstitutor) = createCompositeSubstitutor(this, appliedAfter)
fun TypeSubstitutor.composeWith(appliedAfter: NewTypeSubstitutor) = createCompositeSubstitutor(this, appliedAfter)
@@ -65,7 +65,7 @@ abstract class ResolvedCallAtom : ResolvedAtom() {
abstract val typeArgumentMappingByOriginal: TypeArgumentsToParametersMapper.TypeArgumentsMapping
abstract val argumentMappingByOriginal: Map<ValueParameterDescriptor, ResolvedCallArgument>
abstract val freshVariablesSubstitutor: FreshVariableNewTypeSubstitutor
abstract val knownParametersSubstitutor: TypeSubstitutor
abstract val knownParametersSubstitutor: NewTypeSubstitutor
abstract val argumentsWithConversion: Map<KotlinCallArgument, SamConversionDescription>
abstract val argumentsWithSuspendConversion: Map<KotlinCallArgument, UnwrappedType>
abstract val argumentsWithUnitConversion: Map<KotlinCallArgument, UnwrappedType>
@@ -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<ValueParameterDescriptor, ResolvedCallArgument>
override lateinit var freshVariablesSubstitutor: FreshVariableNewTypeSubstitutor
override lateinit var knownParametersSubstitutor: TypeSubstitutor
override lateinit var knownParametersSubstitutor: NewTypeSubstitutor
lateinit var argumentToCandidateParameter: Map<KotlinCallArgument, ValueParameterDescriptor>
private var samAdapterMap: HashMap<KotlinCallArgument, SamConversionDescription>? = null
private var suspendAdapterMap: HashMap<KotlinCallArgument, UnwrappedType>? = null
@@ -1,4 +1,3 @@
// !WITH_NEW_INFERENCE
// KT-5508 Stackoverflow in type substitution
abstract class A<T> {
+1 -2
View File
@@ -1,4 +1,3 @@
// !WITH_NEW_INFERENCE
// KT-5508 Stackoverflow in type substitution
abstract class A<T> {
@@ -6,7 +5,7 @@ abstract class A<T> {
public abstract fun bar(x: T)
public inner abstract class B<S> : A<B<S>>() {
public inner <!ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED!>class C<!><U> : <!NI;DEBUG_INFO_UNRESOLVED_WITH_TARGET, NI;UNRESOLVED_REFERENCE_WRONG_RECEIVER!>B<!><C<U>>()
public inner <!ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED!>class C<!><U> : B<C<U>>()
{
// Here B<C<U>> means A<A<A<T>.B<S>>.B<A<T>.B<S>.C<U>>>.B<A<A<T>.B<S>>.B<A<T>.B<S>.C<U>>.C<U>>
// while for being a correct override it should be A<A<T>.B<S>>.B<A<T>.B<S>.C<U>>
@@ -0,0 +1,8 @@
// !LANGUAGE: +NewInference
open class Test<T1, T2>(val map1 : Map<T1, T2>, val map2 : Map<T2, T1>) {
open val inverse: Test<T2, T1> = object : <!INAPPLICABLE_CANDIDATE!>Test<T2, T1><!>(map2, map1) {
override val inverse: Test<T1, T2>
get() = this@Test
}
}
@@ -0,0 +1,8 @@
// !LANGUAGE: +NewInference
open class Test<T1, T2>(val map1 : Map<T1, T2>, val map2 : Map<T2, T1>) {
open val inverse: Test<T2, T1> = object : Test<T2, T1>(map2, map1) {
override val inverse: Test<T1, T2>
get() = this@Test
}
}
@@ -0,0 +1,11 @@
package
public open class Test</*0*/ T1, /*1*/ T2> {
public constructor Test</*0*/ T1, /*1*/ T2>(/*0*/ map1: kotlin.collections.Map<T1, T2>, /*1*/ map2: kotlin.collections.Map<T2, T1>)
public open val inverse: Test<T2, T1>
public final val map1: kotlin.collections.Map<T1, T2>
public final val map2: kotlin.collections.Map<T2, T1>
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
}
@@ -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");
@@ -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");