[FIR] Make captureFromTypeParameterUpperBoundIfNeeded smarter
Before this change `ARGUMENT_TYPE_MISMATCH` would complain that `Y` "is not a subtype of" `Inv<Y>`, because the function would only check immediate bounds of the type parameter `Y`. `chosenSupertype` would be `X`, not `Inv<out kotlin/String>`. ^KT-60056
This commit is contained in:
committed by
Space Team
parent
5e1e8e8f61
commit
69fba8d33b
+1
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
|
import org.jetbrains.kotlin.fir.collectUpperBounds
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||||
import org.jetbrains.kotlin.fir.isPrimitiveType
|
import org.jetbrains.kotlin.fir.isPrimitiveType
|
||||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
|||||||
import org.jetbrains.kotlin.fir.scopes.*
|
import org.jetbrains.kotlin.fir.scopes.*
|
||||||
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
|
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
|
||||||
import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature
|
import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature
|
||||||
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||||
@@ -810,46 +809,6 @@ fun FirElement.isLhsOfAssignment(context: CheckerContext): Boolean {
|
|||||||
return lastQualified is FirVariableAssignment && lastQualified.lValue == this
|
return lastQualified is FirVariableAssignment && lastQualified.lValue == this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Collects the upper bounds as [ConeClassLikeType].
|
|
||||||
*/
|
|
||||||
fun ConeKotlinType?.collectUpperBounds(): Set<ConeClassLikeType> {
|
|
||||||
if (this == null) return emptySet()
|
|
||||||
|
|
||||||
val upperBounds = mutableSetOf<ConeClassLikeType>()
|
|
||||||
val seen = mutableSetOf<ConeKotlinType>()
|
|
||||||
fun collect(type: ConeKotlinType) {
|
|
||||||
if (!seen.add(type)) return // Avoid infinite recursion.
|
|
||||||
|
|
||||||
when (type) {
|
|
||||||
is ConeErrorType -> return // Ignore error types
|
|
||||||
is ConeLookupTagBasedType -> when (type) {
|
|
||||||
is ConeClassLikeType -> upperBounds.add(type)
|
|
||||||
is ConeTypeParameterType -> {
|
|
||||||
val symbol = type.lookupTag.typeParameterSymbol
|
|
||||||
symbol.resolvedBounds.forEach { collect(it.coneType) }
|
|
||||||
}
|
|
||||||
else -> error("missing branch for ${javaClass.name}")
|
|
||||||
}
|
|
||||||
is ConeTypeVariableType -> {
|
|
||||||
val symbol = (type.typeConstructor.originalTypeParameter as? ConeTypeParameterLookupTag)?.typeParameterSymbol ?: return
|
|
||||||
symbol.resolvedBounds.forEach { collect(it.coneType) }
|
|
||||||
}
|
|
||||||
is ConeDefinitelyNotNullType -> collect(type.original)
|
|
||||||
is ConeIntersectionType -> type.intersectedTypes.forEach(::collect)
|
|
||||||
is ConeFlexibleType -> collect(type.upperBound)
|
|
||||||
is ConeCapturedType -> type.constructor.supertypes?.forEach(::collect)
|
|
||||||
is ConeIntegerConstantOperatorType -> upperBounds.add(type.getApproximatedType())
|
|
||||||
is ConeStubType, is ConeIntegerLiteralConstantType -> {
|
|
||||||
error("$type should not reach here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
collect(this)
|
|
||||||
return upperBounds
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ConeKotlinType.leastUpperBound(session: FirSession): ConeKotlinType {
|
fun ConeKotlinType.leastUpperBound(session: FirSession): ConeKotlinType {
|
||||||
val upperBounds = collectUpperBounds().takeIf { it.isNotEmpty() } ?: return session.builtinTypes.nullableAnyType.type
|
val upperBounds = collectUpperBounds().takeIf { it.isNotEmpty() } ?: return session.builtinTypes.nullableAnyType.type
|
||||||
return ConeTypeIntersector.intersectTypes(session.typeContext, upperBounds)
|
return ConeTypeIntersector.intersectTypes(session.typeContext, upperBounds)
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
|||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.collectUpperBounds
|
import org.jetbrains.kotlin.fir.collectUpperBounds
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.typeParameterSymbols
|
import org.jetbrains.kotlin.fir.analysis.checkers.typeParameterSymbols
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
|||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.collectUpperBounds
|
import org.jetbrains.kotlin.fir.collectUpperBounds
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
|||||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory2
|
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory2
|
||||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.collectUpperBounds
|
import org.jetbrains.kotlin.fir.collectUpperBounds
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ import org.jetbrains.kotlin.KtSourceElement
|
|||||||
import org.jetbrains.kotlin.config.AnalysisFlags
|
import org.jetbrains.kotlin.config.AnalysisFlags
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.collectUpperBounds
|
import org.jetbrains.kotlin.fir.collectUpperBounds
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
|
|||||||
@@ -668,11 +668,9 @@ internal fun captureFromTypeParameterUpperBoundIfNeeded(
|
|||||||
): ConeKotlinType {
|
): ConeKotlinType {
|
||||||
val expectedTypeClassId = expectedType.upperBoundIfFlexible().classId ?: return argumentType
|
val expectedTypeClassId = expectedType.upperBoundIfFlexible().classId ?: return argumentType
|
||||||
val simplifiedArgumentType = argumentType.lowerBoundIfFlexible() as? ConeTypeParameterType ?: return argumentType
|
val simplifiedArgumentType = argumentType.lowerBoundIfFlexible() as? ConeTypeParameterType ?: return argumentType
|
||||||
val typeParameter = simplifiedArgumentType.lookupTag.typeParameterSymbol.fir
|
|
||||||
|
|
||||||
val context = session.typeContext
|
val context = session.typeContext
|
||||||
|
|
||||||
val chosenSupertype = typeParameter.symbol.resolvedBounds.map { it.coneType }
|
val chosenSupertype = simplifiedArgumentType.collectUpperBounds()
|
||||||
.singleOrNull { it.hasSupertypeWithGivenClassId(expectedTypeClassId, context) } ?: return argumentType
|
.singleOrNull { it.hasSupertypeWithGivenClassId(expectedTypeClassId, context) } ?: return argumentType
|
||||||
|
|
||||||
val capturedType = context.captureFromExpression(chosenSupertype) ?: return argumentType
|
val capturedType = context.captureFromExpression(chosenSupertype) ?: return argumentType
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 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
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
|
||||||
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects the upper bounds as [ConeClassLikeType].
|
||||||
|
*/
|
||||||
|
fun ConeKotlinType?.collectUpperBounds(): Set<ConeClassLikeType> {
|
||||||
|
if (this == null) return emptySet()
|
||||||
|
|
||||||
|
val upperBounds = mutableSetOf<ConeClassLikeType>()
|
||||||
|
val seen = mutableSetOf<ConeKotlinType>()
|
||||||
|
fun collect(type: ConeKotlinType) {
|
||||||
|
if (!seen.add(type)) return // Avoid infinite recursion.
|
||||||
|
|
||||||
|
when (type) {
|
||||||
|
is ConeErrorType -> return // Ignore error types
|
||||||
|
is ConeLookupTagBasedType -> when (type) {
|
||||||
|
is ConeClassLikeType -> upperBounds.add(type)
|
||||||
|
is ConeTypeParameterType -> {
|
||||||
|
val symbol = type.lookupTag.typeParameterSymbol
|
||||||
|
symbol.resolvedBounds.forEach { collect(it.coneType) }
|
||||||
|
}
|
||||||
|
else -> error("missing branch for ${javaClass.name}")
|
||||||
|
}
|
||||||
|
is ConeTypeVariableType -> {
|
||||||
|
val symbol = (type.typeConstructor.originalTypeParameter as? ConeTypeParameterLookupTag)?.typeParameterSymbol ?: return
|
||||||
|
symbol.resolvedBounds.forEach { collect(it.coneType) }
|
||||||
|
}
|
||||||
|
is ConeDefinitelyNotNullType -> collect(type.original)
|
||||||
|
is ConeIntersectionType -> type.intersectedTypes.forEach(::collect)
|
||||||
|
is ConeFlexibleType -> collect(type.upperBound)
|
||||||
|
is ConeCapturedType -> type.constructor.supertypes?.forEach(::collect)
|
||||||
|
is ConeIntegerConstantOperatorType -> upperBounds.add(type.getApproximatedType())
|
||||||
|
is ConeStubType, is ConeIntegerLiteralConstantType -> {
|
||||||
|
error("$type should not reach here")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collect(this)
|
||||||
|
return upperBounds
|
||||||
|
}
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
interface Inv<T>
|
|
||||||
|
|
||||||
fun <Y: X, X : Inv<out String>> foo(x: X, y: Y) {
|
|
||||||
val rX = bar(x)
|
|
||||||
rX.length
|
|
||||||
|
|
||||||
val rY = bar(<!ARGUMENT_TYPE_MISMATCH!>y<!>)
|
|
||||||
rY.<!UNRESOLVED_REFERENCE!>length<!>
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Y> bar(l: Inv<Y>): Y = TODO()
|
|
||||||
Vendored
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
interface Inv<T>
|
interface Inv<T>
|
||||||
|
|
||||||
fun <Y: X, X : Inv<out String>> foo(x: X, y: Y) {
|
fun <Y: X, X : Inv<out String>> foo(x: X, y: Y) {
|
||||||
|
|||||||
Reference in New Issue
Block a user