[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:
+8
-4
@@ -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
|
||||
|
||||
+21
-2
@@ -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) {
|
||||
|
||||
+1
-3
@@ -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())
|
||||
}
|
||||
Reference in New Issue
Block a user