[NI] Use alternative to intersection type in public declarations

The new inference uses inferred intersection types normally, unlike the old inference.
However, intersection types in public declarations are approximated to supertype, which
potentially may give a less presice type, then it would be with the OI.
For non-related T1, T2 the NI approximates {T1 & T2} to Any in public declarations,
and if the OI was inferring T1 instead of the intersection type, it may lead to
less precise declaration type and related errors.

The solution is to remember an alternative for an intersection type when present.
Before approximation the alternative replaces the intersection type.

^KT-36249 Fixed
This commit is contained in:
Pavel Kirpichenkov
2020-02-20 19:55:13 +03:00
parent 4038c758ca
commit dc42b20ae3
67 changed files with 2120 additions and 30 deletions
@@ -106,12 +106,16 @@ interface NewTypeSubstitutor: TypeSubstitutorMarker {
}
if (typeConstructor is IntersectionTypeConstructor) {
var thereIsChanges = false
fun updateNullability(substituted: UnwrappedType) =
if (type.isMarkedNullable) substituted.makeNullableAsSpecified(true) else substituted
substituteNotNullTypeWithConstructor(typeConstructor)?.let { return updateNullability(it) }
var thereAreChanges = false
val newTypes = typeConstructor.supertypes.map {
substitute(it.unwrap(), keepAnnotation, runCapturedChecks)?.apply { thereIsChanges = true } ?: it.unwrap()
substitute(it.unwrap(), keepAnnotation, runCapturedChecks)?.apply { thereAreChanges = true } ?: it.unwrap()
}
if (!thereIsChanges) return null
return intersectTypes(newTypes).let { if (type.isMarkedNullable) it.makeNullableAsSpecified(true) else it }
if (!thereAreChanges) return null
return updateNullability(intersectTypes(newTypes))
}
// simple classifier type
@@ -19,8 +19,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator.ResolveDirection
import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.types.AbstractTypeApproximator
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.model.*
class ResultTypeResolver(
@@ -65,6 +64,10 @@ class ResultTypeResolver(
): KotlinTypeMarker? {
if (firstCandidate == null || secondCandidate == null) return firstCandidate ?: secondCandidate
specialResultForIntersectionType(firstCandidate, secondCandidate)?.let { intersectionWithAlternative ->
return intersectionWithAlternative
}
if (isSuitableType(firstCandidate, variableWithConstraints)) return firstCandidate
return if (isSuitableType(secondCandidate, variableWithConstraints)) {
@@ -74,6 +77,22 @@ class ResultTypeResolver(
}
}
private fun Context.specialResultForIntersectionType(
firstCandidate: KotlinTypeMarker,
secondCandidate: KotlinTypeMarker,
): KotlinTypeMarker? {
if (firstCandidate.typeConstructor() is IntersectionTypeConstructor) {
if (!AbstractTypeChecker.isSubtypeOf(this, firstCandidate.toPublicType(), secondCandidate.toPublicType())) {
return createTypeWithAlternativeForIntersectionResult(firstCandidate, secondCandidate)
}
}
return null
}
private fun KotlinTypeMarker.toPublicType(): KotlinTypeMarker =
typeApproximator.approximateToSuperType(this, TypeApproximatorConfiguration.PublicDeclaration) ?: this
private fun Context.isSuitableType(resultType: KotlinTypeMarker, variableWithConstraints: VariableWithConstraints): Boolean {
val filteredConstraints = variableWithConstraints.constraints.filter { isProperType(it.type) }
for (constraint in filteredConstraints) {
@@ -13,9 +13,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.ResultTypeResolve
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.OnlyInputTypesDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.ResolvedAtom
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.IntersectionTypeConstructor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker
import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.utils.SmartList
@@ -115,7 +115,8 @@ class TypeApproximator(builtIns: KotlinBuiltIns) : AbstractTypeApproximator(Clas
if (!languageVersionSettings.supportsFeature(LanguageFeature.NewInference)) return baseType.unwrap()
val configuration = if (local) TypeApproximatorConfiguration.LocalDeclaration else TypeApproximatorConfiguration.PublicDeclaration
return approximateToSuperType(baseType.unwrap(), configuration) ?: baseType.unwrap()
val preparedType = if (local) baseType.unwrap() else substituteAlternativesInPublicType(baseType)
return approximateToSuperType(preparedType, configuration) ?: preparedType
}
// null means that this input type is the result, i.e. input type not contains not-allowed kind of types
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.types
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
import org.jetbrains.kotlin.types.typeUtil.contains
fun substituteAlternativesInPublicType(type: KotlinType): UnwrappedType {
val substitutor = object : NewTypeSubstitutor {
override fun substituteNotNullTypeWithConstructor(constructor: TypeConstructor): UnwrappedType? {
if (constructor is IntersectionTypeConstructor) {
constructor.getAlternativeType()?.let { alternative ->
return safeSubstitute(alternative.unwrap())
}
}
return null
}
override val isEmpty: Boolean by lazy {
!type.contains { it.constructor is IntersectionTypeConstructor }
}
}
return substitutor.safeSubstitute(type.unwrap())
}