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:
Victor Petukhov
2020-02-21 12:44:12 +03:00
parent 368b0d9b0b
commit 92a0ddfe71
28 changed files with 1451 additions and 37 deletions
@@ -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?,
@@ -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)
@@ -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
@@ -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()