[FIR] Properly handle that type variable is contained in inv or contravar positions

#KT-45344 Fixed
This commit is contained in:
Dmitriy Novozhilov
2021-03-11 13:37:35 +03:00
committed by TeamCityServer
parent 3d1f4b8386
commit 6cc3fff48d
13 changed files with 230 additions and 46 deletions
@@ -40,6 +40,10 @@ object ConeStarProjection : ConeTypeProjection() {
get() = ProjectionKind.STAR
}
sealed class ConeKotlinTypeProjection : ConeTypeProjection() {
abstract val type: ConeKotlinType
}
data class ConeKotlinTypeProjectionIn(override val type: ConeKotlinType) : ConeKotlinTypeProjection() {
override val kind: ProjectionKind
get() = ProjectionKind.IN
@@ -50,6 +54,16 @@ data class ConeKotlinTypeProjectionOut(override val type: ConeKotlinType) : Cone
get() = ProjectionKind.OUT
}
val ConeTypeProjection.type: ConeKotlinType?
get() = when (this) {
ConeStarProjection -> null
is ConeKotlinTypeProjection -> type
is ConeKotlinType -> this
}
val ConeTypeProjection.isStarProjection: Boolean
get() = this == ConeStarProjection
// We assume type IS an invariant type projection to prevent additional wrapper here
// (more exactly, invariant type projection contains type)
sealed class ConeKotlinType : ConeKotlinTypeProjection(), KotlinTypeMarker, TypeArgumentListMarker {
@@ -68,17 +82,12 @@ sealed class ConeKotlinType : ConeKotlinTypeProjection(), KotlinTypeMarker, Type
final override fun toString(): String {
return render()
}
abstract override fun equals(other: Any?): Boolean
abstract override fun hashCode(): Int
}
sealed class ConeSimpleKotlinType : ConeKotlinType(), SimpleTypeMarker
sealed class ConeKotlinTypeProjection : ConeTypeProjection() {
abstract val type: ConeKotlinType
}
typealias ConeKotlinErrorType = ConeClassErrorType
class ConeClassLikeErrorLookupTag(override val classId: ClassId) : ConeClassLikeLookupTag()
@@ -162,7 +171,8 @@ data class ConeCapturedType(
val lowerType: ConeKotlinType?,
override val nullability: ConeNullability = ConeNullability.NOT_NULL,
val constructor: ConeCapturedTypeConstructor,
override val attributes: ConeAttributes = ConeAttributes.Empty
override val attributes: ConeAttributes = ConeAttributes.Empty,
val isProjectionNotNull: Boolean = false
) : ConeSimpleKotlinType(), CapturedTypeMarker {
constructor(
captureStatus: CaptureStatus, lowerType: ConeKotlinType?, projection: ConeTypeProjection,
@@ -315,6 +325,13 @@ open class ConeTypeVariable(name: String) : TypeVariableMarker {
class ConeTypeVariableTypeConstructor(val debugName: String) : ConeClassifierLookupTag(), TypeVariableTypeConstructorMarker {
override val name: Name get() = Name.identifier(debugName)
var isContainedInInvariantOrContravariantPositions: Boolean = false
private set
fun recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter() {
isContainedInInvariantOrContravariantPositions = true
}
}
abstract class ConeIntegerLiteralType(
@@ -12,6 +12,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
CheckExplicitReceiverConsistency,
NoTypeArguments,
CreateFreshTypeVariableSubstitutorStage,
CollectTypeVariableUsagesInfo,
CheckDispatchReceiver,
CheckExtensionReceiver,
CheckLowPriorityInOverloadResolution,
@@ -22,6 +23,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
MapArguments,
NoTypeArguments,
CreateFreshTypeVariableSubstitutorStage,
CollectTypeVariableUsagesInfo,
CheckArguments,
EagerResolveOfCallableReferences
)
@@ -33,6 +35,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
CheckExplicitReceiverConsistency,
MapTypeArguments,
CreateFreshTypeVariableSubstitutorStage,
CollectTypeVariableUsagesInfo,
CheckDispatchReceiver,
CheckExtensionReceiver,
CheckArguments,
@@ -47,6 +50,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
CheckExplicitReceiverConsistency,
MapTypeArguments,
CreateFreshTypeVariableSubstitutorStage,
CollectTypeVariableUsagesInfo,
CheckDispatchReceiver,
CheckExtensionReceiver,
CheckArguments,
@@ -58,6 +62,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
DiscriminateSynthetics,
NoTypeArguments,
CreateFreshTypeVariableSubstitutorStage,
CollectTypeVariableUsagesInfo,
CheckDispatchReceiver,
CheckExtensionReceiver,
CheckCallableReferenceExpectedType,
@@ -68,6 +73,7 @@ sealed class CallKind(vararg resolutionSequence: ResolutionStage) {
MapArguments,
NoTypeArguments,
CreateFreshTypeVariableSubstitutorStage,
CollectTypeVariableUsagesInfo,
CheckArguments,
EagerResolveOfCallableReferences
)
@@ -0,0 +1,184 @@
/*
* Copyright 2010-2021 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.fir.resolve.calls
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRef
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner
import org.jetbrains.kotlin.fir.resolve.inference.ConeTypeParameterBasedTypeVariable
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeDeclaredUpperBoundConstraintPosition
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
import org.jetbrains.kotlin.types.model.typeConstructor
import org.jetbrains.kotlin.utils.SmartList
object CollectTypeVariableUsagesInfo : ResolutionStage() {
override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) {
val candidateSymbol = candidate.symbol
if (candidateSymbol is FirConstructorSymbol) {
val typeParameters = candidateSymbol.fir.typeParameters
for (variable in candidate.freshVariables) {
if (variable !is ConeTypeParameterBasedTypeVariable) continue
if (isContainedInInvariantOrContravariantPositionsAmongTypeParameters(variable, typeParameters)) {
variable.recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter()
}
}
} else if (candidateSymbol is FirCallableSymbol) {
val session = context.session
for (variable in candidate.freshVariables) {
if (variable !is ConeTypeParameterBasedTypeVariable) continue
if (candidate.system.isContainedInInvariantOrContravariantPositionsWithDependencies(session, variable, candidateSymbol)) {
variable.recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter()
}
}
}
}
private fun isContainedInInvariantOrContravariantPositionsAmongTypeParameters(
checkingTypeVariable: ConeTypeParameterBasedTypeVariable,
typeParameters: List<FirTypeParameterRef>
): Boolean = typeParameters.any {
it.symbol.fir.variance != Variance.OUT_VARIANCE && it.symbol == checkingTypeVariable.typeParameterSymbol
}
private fun NewConstraintSystemImpl.isContainedInInvariantOrContravariantPositions(
session: FirSession,
variableTypeConstructor: ConeTypeVariableTypeConstructor,
baseType: ConeKotlinType,
wasOutVariance: Boolean = true
): Boolean {
if (baseType !is ConeClassLikeType) return false
val dependentTypeParameter = getTypeParameterByVariable(variableTypeConstructor) ?: return false
val declaration = baseType.lookupTag.toSymbol(session)?.fir as? FirTypeParameterRefsOwner ?: return false
val declaredTypeParameters = declaration.typeParameters
if (declaredTypeParameters.size < baseType.typeArguments.size) return false
for ((argumentsIndex, argument) in baseType.typeArguments.withIndex()) {
val argumentType = argument.type ?: continue
if (argumentType.isMarkedNullable) continue
val currentEffectiveVariance =
declaredTypeParameters[argumentsIndex].symbol.fir.variance == Variance.OUT_VARIANCE || argument.kind == ProjectionKind.OUT
val effectiveVarianceFromTopLevel = wasOutVariance && currentEffectiveVariance
val argumentTypeConstructor = argumentType.typeConstructor()
if ((argumentTypeConstructor == dependentTypeParameter || argumentTypeConstructor == variableTypeConstructor) && !effectiveVarianceFromTopLevel)
return true
if (
isContainedInInvariantOrContravariantPositions(
session,
variableTypeConstructor,
argumentType,
effectiveVarianceFromTopLevel
)
)
return true
}
return false
}
private fun NewConstraintSystemImpl.isContainedInInvariantOrContravariantPositionsWithDependencies(
session: FirSession,
variable: ConeTypeParameterBasedTypeVariable,
candidateSymbol: FirCallableSymbol<*>
): Boolean {
val returnType = candidateSymbol.fir.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: return false
val typeVariableConstructor = variable.typeConstructor
if (isContainedInInvariantOrContravariantPositions(session, typeVariableConstructor, returnType)) {
return true
}
val dependingOnTypeParameter = getDependingOnTypeParameter(typeVariableConstructor)
if (dependingOnTypeParameter.any { isContainedInInvariantOrContravariantPositions(session, it, returnType) }) {
return true
}
val dependentTypeParameters = getDependentTypeParameters(typeVariableConstructor)
if (dependentTypeParameters.any { isContainedInInvariantOrContravariantPositions(session, it.first, returnType) }) {
return true
}
if (!isContainedInInvariantOrContravariantPositionsAmongUpperBound(session, typeVariableConstructor, dependentTypeParameters)) {
return false
}
return dependentTypeParameters.any { (typeParameter, _) ->
returnType.contains {
it.typeConstructor(asConstraintSystemCompleterContext()) == getTypeParameterByVariable(typeParameter) && !it.isMarkedNullable()
}
}
}
private fun NewConstraintSystemImpl.getDependentTypeParameters(
variable: TypeConstructorMarker,
dependentTypeParametersSeen: List<Pair<TypeConstructorMarker, ConeKotlinType?>> = listOf()
): List<Pair<ConeTypeVariableTypeConstructor, ConeKotlinType?>> = with(asConstraintSystemCompleterContext()) {
val dependentTypeParameters = getBuilder().currentStorage().notFixedTypeVariables.asSequence()
.flatMap { (typeConstructor, constraints) ->
require(typeConstructor is ConeTypeVariableTypeConstructor)
val upperBounds = constraints.constraints.filter {
it.position.from is ConeDeclaredUpperBoundConstraintPosition && it.kind == ConstraintKind.UPPER
}
upperBounds.mapNotNull { constraint ->
if (constraint.type.typeConstructor() != variable) {
val suitableUpperBound = upperBounds.find { upperBound ->
upperBound.type.contains { it.typeConstructor() == variable }
}?.type as ConeKotlinType?
if (suitableUpperBound != null) typeConstructor to suitableUpperBound else null
} else typeConstructor to null
}
}.filter { it !in dependentTypeParametersSeen && it.first != variable }.toList()
return dependentTypeParameters + dependentTypeParameters.flatMapTo(SmartList()) { (typeConstructor, _) ->
if (typeConstructor != variable) {
getDependentTypeParameters(typeConstructor, dependentTypeParameters + dependentTypeParametersSeen)
} else emptyList()
}
}
private fun NewConstraintSystemImpl.isContainedInInvariantOrContravariantPositionsAmongUpperBound(
session: FirSession,
checkingType: ConeTypeVariableTypeConstructor,
dependentTypeParameters: List<Pair<ConeTypeVariableTypeConstructor, ConeKotlinType?>>
): Boolean {
var currentTypeParameterConstructor = checkingType
return dependentTypeParameters.any { (typeConstructor, upperBound) ->
val isContainedOrNoUpperBound =
upperBound == null || isContainedInInvariantOrContravariantPositions(session, currentTypeParameterConstructor, upperBound)
currentTypeParameterConstructor = typeConstructor
isContainedOrNoUpperBound
}
}
private fun NewConstraintSystemImpl.getTypeParameterByVariable(typeConstructor: ConeTypeVariableTypeConstructor): TypeConstructorMarker? =
(getBuilder().currentStorage().allTypeVariables[typeConstructor] as? ConeTypeParameterBasedTypeVariable)?.typeParameterSymbol?.toLookupTag()
private fun NewConstraintSystemImpl.getDependingOnTypeParameter(variable: TypeConstructorMarker): List<ConeTypeVariableTypeConstructor> =
with(asConstraintSystemCompleterContext()) {
getBuilder().currentStorage().notFixedTypeVariables[variable]?.constraints?.mapNotNull {
if (it.position.from is ConeDeclaredUpperBoundConstraintPosition && it.kind == ConstraintKind.UPPER) {
it.type.typeConstructor() as? ConeTypeVariableTypeConstructor
} else null
} ?: emptyList()
}
private fun ConeTypeVariable.recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter() {
this.typeConstructor.recordInfoAboutTypeVariableUsagesAsInvariantOrContravariantParameter()
}
}
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.modality
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents
import org.jetbrains.kotlin.fir.resolve.inference.TypeParameterBasedTypeVariable
import org.jetbrains.kotlin.fir.resolve.inference.ConeTypeParameterBasedTypeVariable
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.types.coneType
@@ -201,7 +201,7 @@ class ConeSimpleConstraintSystemImpl(val system: NewConstraintSystemImpl) : Simp
val csBuilder = system.getBuilder()
val substitutionMap = typeParameters.associateBy({ (it as ConeTypeParameterLookupTag).typeParameterSymbol }) {
require(it is ConeTypeParameterLookupTag)
val variable = TypeParameterBasedTypeVariable(it.typeParameterSymbol)
val variable = ConeTypeParameterBasedTypeVariable(it.typeParameterSymbol)
csBuilder.registerVariable(variable)
variable.defaultType
@@ -9,7 +9,7 @@ import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRef
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner
import org.jetbrains.kotlin.fir.renderWithType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.inference.TypeParameterBasedTypeVariable
import org.jetbrains.kotlin.fir.resolve.inference.ConeTypeParameterBasedTypeVariable
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeDeclaredUpperBoundConstraintPosition
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
@@ -115,7 +115,7 @@ private fun createToFreshVariableSubstitutorAndAddInitialConstraints(
val typeParameters = declaration.typeParameters
val freshTypeVariables = typeParameters.map { TypeParameterBasedTypeVariable(it.symbol) }
val freshTypeVariables = typeParameters.map { ConeTypeParameterBasedTypeVariable(it.symbol) }
val toFreshVariables = substitutorByMap(freshTypeVariables.associate { it.typeParameterSymbol to it.defaultType })
@@ -123,7 +123,7 @@ private fun createToFreshVariableSubstitutorAndAddInitialConstraints(
csBuilder.registerVariable(freshVariable)
}
fun TypeParameterBasedTypeVariable.addSubtypeConstraint(
fun ConeTypeParameterBasedTypeVariable.addSubtypeConstraint(
upperBound: ConeKotlinType//,
//position: DeclaredUpperBoundConstraintPosition
) {
@@ -251,7 +251,7 @@ internal object PostponedVariablesInitializerResolutionStage : ResolutionStage()
for (freshVariable in candidate.freshVariables) {
if (candidate.csBuilder.isPostponedTypeVariable(freshVariable)) continue
if (freshVariable !is TypeParameterBasedTypeVariable) continue
if (freshVariable !is ConeTypeParameterBasedTypeVariable) continue
val typeParameterSymbol = freshVariable.typeParameterSymbol
val typeHasVariable = receiverType.contains {
(it as? ConeTypeParameterType)?.lookupTag?.typeParameterSymbol == typeParameterSymbol
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeArgumentConstraintPosition
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeFixVariableConstraintPosition
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemUtilContext
@@ -38,7 +37,7 @@ object ConeConstraintSystemUtilContext : ConstraintSystemUtilContext {
}
override fun TypeVariableMarker.isReified(): Boolean {
return this is TypeParameterBasedTypeVariable && typeParameterSymbol.fir.isReified
return this is ConeTypeParameterBasedTypeVariable && typeParameterSymbol.fir.isReified
}
override fun KotlinTypeMarker.refineType(): KotlinTypeMarker {
@@ -8,5 +8,5 @@ package org.jetbrains.kotlin.fir.resolve.inference
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.ConeTypeVariable
class TypeParameterBasedTypeVariable(val typeParameterSymbol: FirTypeParameterSymbol) :
ConeTypeVariable(typeParameterSymbol.name.identifier)
class ConeTypeParameterBasedTypeVariable(val typeParameterSymbol: FirTypeParameterSymbol) :
ConeTypeVariable(typeParameterSymbol.name.identifier)
@@ -295,12 +295,12 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
override fun CapturedTypeMarker.withNotNullProjection(): KotlinTypeMarker {
require(this is ConeCapturedType)
return this // TODO
return ConeCapturedType(captureStatus, lowerType, nullability, constructor, attributes, isProjectionNotNull = true)
}
override fun CapturedTypeMarker.isProjectionNotNull(): Boolean {
require(this is ConeCapturedType)
return false // TODO
return isProjectionNotNull
}
override fun DefinitelyNotNullTypeMarker.original(): SimpleTypeMarker {
@@ -339,7 +339,8 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
}
override fun TypeVariableTypeConstructorMarker.isContainedInInvariantOrContravariantPositions(): Boolean {
return false
require(this is ConeTypeVariableTypeConstructor)
return isContainedInInvariantOrContravariantPositions
}
override fun createErrorType(debugName: String): ConeClassErrorType {
@@ -140,8 +140,8 @@ class Main<L>(x: L?, y: L) {
val x120 = foo12(x!!)
val x121 = foo12(y!!)
val x122 = foo12(x)
val x123 = foo12(y)
val x122 = <!INAPPLICABLE_CANDIDATE!>foo12<!>(x)
val x123 = <!INAPPLICABLE_CANDIDATE!>foo12<!>(y)
val x133 = Foo13(x).foo1(y)
val x135 = Foo13(y).foo1(y)
@@ -1,24 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION -UNUSED_VARIABLE
class Inv<T>(val x: T?)
fun <K> create(y: K) = Inv(y)
fun <K> createPrivate(y: K) = Inv(y)
fun takeInvInt(i: Inv<Int>) {}
fun <S> test(i: Int, s: S) {
val a = Inv(s)
a
val b = create(i)
b
val c = createPrivate(i)
c
<!INAPPLICABLE_CANDIDATE!>takeInvInt<!>(create(i))
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION -UNUSED_VARIABLE
class Inv<T>(val x: T?)
@@ -13,4 +13,4 @@ class Foo {
operator fun setValue(thisRef: Foo, property: KProperty<*>, value: T) {}
}
}
}