NI: discard def not null types if they appear in return positions, in inv or in variance
^KT-37343 Fixed
This commit is contained in:
+143
@@ -12,8 +12,10 @@ import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
|
||||
import org.jetbrains.kotlin.incremental.record
|
||||
import org.jetbrains.kotlin.resolve.calls.components.CollectionTypeVariableUsagesInfo.getTypeParameterByVariable
|
||||
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.model.*
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.substitute
|
||||
@@ -26,6 +28,9 @@ import org.jetbrains.kotlin.resolve.calls.tower.VisibilityError
|
||||
import org.jetbrains.kotlin.resolve.sam.SAM_LOOKUP_NAME
|
||||
import org.jetbrains.kotlin.resolve.sam.getFunctionTypeForPossibleSamType
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
|
||||
import org.jetbrains.kotlin.types.model.typeConstructor
|
||||
import org.jetbrains.kotlin.types.typeUtil.contains
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
@@ -285,6 +290,144 @@ internal object CheckExplicitReceiverKindConsistency : ResolutionPart() {
|
||||
}
|
||||
}
|
||||
|
||||
internal object CollectionTypeVariableUsagesInfo : ResolutionPart() {
|
||||
private val KotlinType.isComputed get() = this !is WrappedType || isComputed()
|
||||
|
||||
private fun NewConstraintSystem.isContainedInInvariantOrContravariantPositions(
|
||||
variableTypeConstructor: TypeConstructorMarker,
|
||||
baseType: KotlinTypeMarker,
|
||||
wasOutVariance: Boolean = true
|
||||
): Boolean {
|
||||
if (baseType !is KotlinType) return false
|
||||
|
||||
val dependentTypeParameter = getTypeParameterByVariable(variableTypeConstructor) ?: return false
|
||||
val declaredTypeParameters = baseType.constructor.parameters
|
||||
|
||||
if (declaredTypeParameters.size < baseType.arguments.size) return false
|
||||
|
||||
for ((argumentsIndex, argument) in baseType.arguments.withIndex()) {
|
||||
if (argument.isStarProjection || argument.type.isMarkedNullable) continue
|
||||
|
||||
val currentEffectiveVariance =
|
||||
declaredTypeParameters[argumentsIndex].variance == Variance.OUT_VARIANCE || argument.projectionKind == Variance.OUT_VARIANCE
|
||||
val effectiveVarianceFromTopLevel = wasOutVariance && currentEffectiveVariance
|
||||
|
||||
if ((argument.type.constructor == dependentTypeParameter || argument.type.constructor == variableTypeConstructor) && !effectiveVarianceFromTopLevel)
|
||||
return true
|
||||
|
||||
if (isContainedInInvariantOrContravariantPositions(variableTypeConstructor, argument.type, effectiveVarianceFromTopLevel))
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isContainedInInvariantOrContravariantPositionsAmongTypeParameters(
|
||||
checkingType: TypeVariableFromCallableDescriptor,
|
||||
typeParameters: List<TypeParameterDescriptor>
|
||||
) = typeParameters.any {
|
||||
it.variance != Variance.OUT_VARIANCE && it.typeConstructor == checkingType.originalTypeParameter.typeConstructor
|
||||
}
|
||||
|
||||
private fun NewConstraintSystem.getDependentTypeParameters(
|
||||
variable: TypeConstructorMarker,
|
||||
dependentTypeParametersSeen: List<Pair<TypeConstructorMarker, KotlinTypeMarker?>> = listOf()
|
||||
): List<Pair<TypeConstructorMarker, KotlinTypeMarker?>> {
|
||||
val context = asConstraintSystemCompleterContext()
|
||||
val dependentTypeParameters = getBuilder().currentStorage().notFixedTypeVariables.mapNotNull { (typeConstructor, constraints) ->
|
||||
val upperBounds = constraints.constraints.filter {
|
||||
it.position.from is DeclaredUpperBoundConstraintPosition && it.kind == ConstraintKind.UPPER
|
||||
}
|
||||
|
||||
upperBounds.mapNotNull { constraint ->
|
||||
if (constraint.type.typeConstructor(context) != variable) {
|
||||
val suitableUpperBound = upperBounds.find { upperBound ->
|
||||
with(context) { upperBound.type.contains { it.typeConstructor() == variable } }
|
||||
}?.type
|
||||
|
||||
if (suitableUpperBound != null) typeConstructor to suitableUpperBound else null
|
||||
} else typeConstructor to null
|
||||
}
|
||||
}.flatten().filter { it !in dependentTypeParametersSeen && it.first != variable }
|
||||
|
||||
return dependentTypeParameters + dependentTypeParameters.mapNotNull { (typeConstructor, _) ->
|
||||
if (typeConstructor != variable) {
|
||||
getDependentTypeParameters(typeConstructor, dependentTypeParameters + dependentTypeParametersSeen)
|
||||
} else null
|
||||
}.flatten()
|
||||
}
|
||||
|
||||
private fun NewConstraintSystem.isContainedInInvariantOrContravariantPositionsAmongUpperBound(
|
||||
checkingType: TypeConstructorMarker,
|
||||
dependentTypeParameters: List<Pair<TypeConstructorMarker, KotlinTypeMarker?>>
|
||||
): Boolean {
|
||||
var currentTypeParameterConstructor = checkingType
|
||||
|
||||
return dependentTypeParameters.any { (typeConstructor, upperBound) ->
|
||||
val isContainedOrNoUpperBound =
|
||||
upperBound == null || isContainedInInvariantOrContravariantPositions(currentTypeParameterConstructor, upperBound)
|
||||
currentTypeParameterConstructor = typeConstructor
|
||||
isContainedOrNoUpperBound
|
||||
}
|
||||
}
|
||||
|
||||
private fun NewConstraintSystem.getTypeParameterByVariable(typeConstructor: TypeConstructorMarker) =
|
||||
(getBuilder().currentStorage().allTypeVariables[typeConstructor] as? TypeVariableFromCallableDescriptor)?.originalTypeParameter?.typeConstructor
|
||||
|
||||
private fun NewConstraintSystem.getDependingOnTypeParameter(variable: TypeConstructor) =
|
||||
getBuilder().currentStorage().notFixedTypeVariables[variable]?.constraints?.mapNotNull {
|
||||
if (it.position.from is DeclaredUpperBoundConstraintPosition && it.kind == ConstraintKind.UPPER) {
|
||||
it.type.typeConstructor(asConstraintSystemCompleterContext())
|
||||
} else null
|
||||
} ?: emptyList()
|
||||
|
||||
private fun NewConstraintSystem.isContainedInInvariantOrContravariantPositionsWithDependencies(
|
||||
variable: TypeVariableFromCallableDescriptor,
|
||||
declarationDescriptor: DeclarationDescriptor?
|
||||
): Boolean {
|
||||
if (declarationDescriptor !is CallableDescriptor) return false
|
||||
|
||||
val returnType = declarationDescriptor.returnType ?: return false
|
||||
|
||||
if (!returnType.isComputed) return false
|
||||
|
||||
val typeVariableConstructor = variable.freshTypeConstructor
|
||||
val dependentTypeParameters = getDependentTypeParameters(typeVariableConstructor)
|
||||
val dependingOnTypeParameter = getDependingOnTypeParameter(typeVariableConstructor)
|
||||
|
||||
val isContainedInUpperBounds =
|
||||
isContainedInInvariantOrContravariantPositionsAmongUpperBound(typeVariableConstructor, dependentTypeParameters)
|
||||
val isContainedAnyDependentTypeInReturnType = dependentTypeParameters.any { (typeParameter, _) ->
|
||||
returnType.contains {
|
||||
it.typeConstructor(asConstraintSystemCompleterContext()) == getTypeParameterByVariable(typeParameter) && !it.isMarkedNullable
|
||||
}
|
||||
}
|
||||
|
||||
return isContainedInInvariantOrContravariantPositions(typeVariableConstructor, returnType)
|
||||
|| dependingOnTypeParameter.any { isContainedInInvariantOrContravariantPositions(it, returnType) }
|
||||
|| dependentTypeParameters.any { isContainedInInvariantOrContravariantPositions(it.first, returnType) }
|
||||
|| (isContainedAnyDependentTypeInReturnType && isContainedInUpperBounds)
|
||||
}
|
||||
|
||||
private fun TypeVariableFromCallableDescriptor.recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter() {
|
||||
freshTypeConstructor.isContainedInInvariantOrContravariantPositions = true
|
||||
}
|
||||
|
||||
override fun KotlinResolutionCandidate.process(workIndex: Int) {
|
||||
for (variable in resolvedCall.freshVariablesSubstitutor.freshVariables) {
|
||||
if (resolvedCall.candidateDescriptor is ClassConstructorDescriptor) {
|
||||
val typeParameters = resolvedCall.candidateDescriptor.containingDeclaration.declaredTypeParameters
|
||||
|
||||
if (isContainedInInvariantOrContravariantPositionsAmongTypeParameters(variable, typeParameters)) {
|
||||
variable.recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter()
|
||||
}
|
||||
} else if (getSystem().isContainedInInvariantOrContravariantPositionsWithDependencies(variable, candidateDescriptor)) {
|
||||
variable.recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinResolutionCandidate.resolveKotlinArgument(
|
||||
argument: KotlinCallArgument,
|
||||
candidateParameter: ParameterDescriptor?,
|
||||
|
||||
+27
-3
@@ -5,9 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableTypeConstructor
|
||||
import org.jetbrains.kotlin.types.AbstractNullabilityChecker
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
|
||||
import org.jetbrains.kotlin.types.checker.NewCapturedType
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
|
||||
abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheckerContext(), TypeSystemInferenceExtensionContext {
|
||||
@@ -179,9 +181,31 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck
|
||||
private fun simplifyLowerConstraint(typeVariable: KotlinTypeMarker, subType: KotlinTypeMarker): Boolean {
|
||||
val lowerConstraint = when (typeVariable) {
|
||||
is SimpleTypeMarker ->
|
||||
// Foo <: T or
|
||||
// Foo <: T? -- Foo!! <: T
|
||||
if (typeVariable.isMarkedNullable()) subType.makeDefinitelyNotNullOrNotNull() else subType
|
||||
/*
|
||||
* Foo <: T -- Foo <: T
|
||||
* Foo <: T? (T is contained in invariant or contravariant positions of a return type) -- Foo <: T
|
||||
* Example:
|
||||
* fun <T> foo(x: T?): Inv<T> {}
|
||||
* fun <K> main(z: K) { val x = foo(z) }
|
||||
* Foo <: T? (T isn't contained there) -- Foo!! <: T
|
||||
* Example:
|
||||
* fun <T> foo(x: T?) {}
|
||||
* fun <K> main(z: K) { foo(z) }
|
||||
*/
|
||||
if (typeVariable.isMarkedNullable()) {
|
||||
val typeVariableTypeConstructor = typeVariable.typeConstructor()
|
||||
val subTypeConstructor = subType.typeConstructor()
|
||||
|
||||
if (subTypeConstructor !is TypeVariableTypeConstructor && typeVariableTypeConstructor is TypeVariableTypeConstructor && typeVariableTypeConstructor.isContainedInInvariantOrContravariantPositions) {
|
||||
if (subType is NewCapturedType) {
|
||||
subType.withNotNullProjection()
|
||||
} else {
|
||||
subType.withNullability(false)
|
||||
}
|
||||
} else {
|
||||
subType.makeDefinitelyNotNullOrNotNull()
|
||||
}
|
||||
} else subType
|
||||
|
||||
is FlexibleTypeMarker -> {
|
||||
assertFlexibleTypeVariable(typeVariable)
|
||||
|
||||
+3
-1
@@ -49,10 +49,12 @@ class TypeVariableTypeConstructor(private val builtIns: KotlinBuiltIns, val debu
|
||||
override fun refine(kotlinTypeRefiner: KotlinTypeRefiner): TypeConstructor = this
|
||||
|
||||
override fun toString() = "TypeVariable($debugName)"
|
||||
|
||||
var isContainedInInvariantOrContravariantPositions: Boolean = false
|
||||
}
|
||||
|
||||
sealed class NewTypeVariable(builtIns: KotlinBuiltIns, name: String) : TypeVariableMarker {
|
||||
val freshTypeConstructor: TypeConstructor = TypeVariableTypeConstructor(builtIns, name)
|
||||
val freshTypeConstructor = TypeVariableTypeConstructor(builtIns, name)
|
||||
|
||||
// member scope is used if we have receiver with type TypeVariable(T)
|
||||
// todo add to member scope methods from supertypes for type variable
|
||||
|
||||
+2
@@ -206,6 +206,7 @@ enum class KotlinCallKind(vararg resolutionPart: ResolutionPart) {
|
||||
NoTypeArguments,
|
||||
NoArguments,
|
||||
CreateFreshVariablesSubstitutor,
|
||||
CollectionTypeVariableUsagesInfo,
|
||||
CheckExplicitReceiverKindConsistency,
|
||||
CheckReceivers,
|
||||
PostponedVariablesInitializerResolutionPart
|
||||
@@ -220,6 +221,7 @@ enum class KotlinCallKind(vararg resolutionPart: ResolutionPart) {
|
||||
MapArguments,
|
||||
ArgumentsToCandidateParameterDescriptor,
|
||||
CreateFreshVariablesSubstitutor,
|
||||
CollectionTypeVariableUsagesInfo,
|
||||
CheckExplicitReceiverKindConsistency,
|
||||
CheckReceivers,
|
||||
CheckArgumentsInParenthesis,
|
||||
|
||||
@@ -41,7 +41,6 @@ open class TypeApproximatorConfiguration {
|
||||
open val errorType get() = false
|
||||
open val integerLiteralType: Boolean = false // IntegerLiteralTypeConstructor
|
||||
open val definitelyNotNullType get() = true
|
||||
open val definitelyNotNullTypeInInvariantPosition get() = true
|
||||
open val intersection: IntersectionStrategy = TO_COMMON_SUPERTYPE
|
||||
|
||||
open val typeVariable: (TypeVariableTypeConstructorMarker) -> Boolean = { false }
|
||||
@@ -83,10 +82,7 @@ open class TypeApproximatorConfiguration {
|
||||
override val typeVariable: (TypeVariableTypeConstructorMarker) -> Boolean get() = { true }
|
||||
}
|
||||
|
||||
object IncorporationConfiguration : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FOR_INCORPORATION) {
|
||||
override val definitelyNotNullTypeInInvariantPosition: Boolean get() = false
|
||||
}
|
||||
|
||||
object IncorporationConfiguration : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FOR_INCORPORATION)
|
||||
object SubtypeCapturedTypesApproximation : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FOR_SUBTYPING)
|
||||
object CapturedAndIntegerLiteralsTypesApproximation : TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FROM_EXPRESSION) {
|
||||
override val integerLiteralType: Boolean get() = true
|
||||
@@ -95,7 +91,6 @@ open class TypeApproximatorConfiguration {
|
||||
object FinalApproximationAfterResolutionAndInference :
|
||||
TypeApproximatorConfiguration.AbstractCapturedTypesApproximation(FROM_EXPRESSION) {
|
||||
override val integerLiteralType: Boolean get() = true
|
||||
override val definitelyNotNullTypeInInvariantPosition: Boolean get() = false
|
||||
}
|
||||
|
||||
object IntegerLiteralsTypesApproximation : TypeApproximatorConfiguration.AllFlexibleSameValue() {
|
||||
@@ -377,7 +372,11 @@ abstract class AbstractTypeApproximator(val ctx: TypeSystemInferenceExtensionCon
|
||||
|
||||
// C = in Int, Int <: C => Int? <: C?
|
||||
// C = out Number, C <: Number => C? <: Number?
|
||||
return if (type.isMarkedNullable()) baseResult.withNullability(true) else baseResult
|
||||
return when {
|
||||
type.isMarkedNullable() -> baseResult.withNullability(true)
|
||||
type.isProjectionNotNull() -> baseResult.withNullability(false)
|
||||
else -> baseResult
|
||||
}
|
||||
}
|
||||
|
||||
private fun approximateSimpleToSuperType(type: SimpleTypeMarker, conf: TypeApproximatorConfiguration, depth: Int) =
|
||||
@@ -480,12 +479,6 @@ abstract class AbstractTypeApproximator(val ctx: TypeSystemInferenceExtensionCon
|
||||
if (argument.isStarProjection()) continue
|
||||
|
||||
val effectiveVariance = AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance())
|
||||
if (effectiveVariance == TypeVariance.INV) {
|
||||
val argumentType = argument.getType()
|
||||
if (argumentType is DefinitelyNotNullTypeMarker && !conf.definitelyNotNullTypeInInvariantPosition) {
|
||||
newArguments[index] = argumentType.original().withNullability(false).asTypeArgument()
|
||||
}
|
||||
}
|
||||
|
||||
val argumentType = newArguments[index]?.getType() ?: argument.getType()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user