[FIR] UPPER_BOUND_VIOLATED optimizations
This commit is contained in:
+8
-1
@@ -13,6 +13,9 @@ fun <T : A> fest() {
|
|||||||
fun test() {
|
fun test() {
|
||||||
val b1 = B<<!UPPER_BOUND_VIOLATED!>Int<!>>()
|
val b1 = B<<!UPPER_BOUND_VIOLATED!>Int<!>>()
|
||||||
val b2 = B<C>()
|
val b2 = B<C>()
|
||||||
|
val b3 = B<<!UPPER_BOUND_VIOLATED!>Any?<!>>()
|
||||||
|
val b4 = B<UnexistingType>()
|
||||||
|
val b5 = B<<!UPPER_BOUND_VIOLATED!>B<UnexistingType><!>>()
|
||||||
fest<<!UPPER_BOUND_VIOLATED!>Boolean<!>>()
|
fest<<!UPPER_BOUND_VIOLATED!>Boolean<!>>()
|
||||||
fest<C>()
|
fest<C>()
|
||||||
fest<HHH>()
|
fest<HHH>()
|
||||||
@@ -38,4 +41,8 @@ fun <K, L : K> rest() {
|
|||||||
class NumColl<T : Collection<Number>>
|
class NumColl<T : Collection<Number>>
|
||||||
typealias NL<K> = NumColl<List<K>>
|
typealias NL<K> = NumColl<List<K>>
|
||||||
val test7 = NL<Int>()
|
val test7 = NL<Int>()
|
||||||
val test8 = <!UPPER_BOUND_VIOLATED!>NL<String>()<!>
|
val test8 = <!UPPER_BOUND_VIOLATED!>NL<String>()<!>
|
||||||
|
|
||||||
|
class NumberPhile<T: Number>(x: T)
|
||||||
|
val np1 = NumberPhile(10)
|
||||||
|
val np2 = <!INAPPLICABLE_CANDIDATE!>NumberPhile<!>("Test")
|
||||||
+13
@@ -21,6 +21,9 @@ FILE: upperBoundViolated.kt
|
|||||||
public final fun test(): R|kotlin/Unit| {
|
public final fun test(): R|kotlin/Unit| {
|
||||||
lval b1: R|B<kotlin/Int>| = R|/B.B|<R|kotlin/Int|>()
|
lval b1: R|B<kotlin/Int>| = R|/B.B|<R|kotlin/Int|>()
|
||||||
lval b2: R|B<C>| = R|/B.B|<R|C|>()
|
lval b2: R|B<C>| = R|/B.B|<R|C|>()
|
||||||
|
lval b3: R|B<kotlin/Any?>| = R|/B.B|<R|kotlin/Any?|>()
|
||||||
|
lval b4: R|B<A>| = R|/B.B|<R|A|>()
|
||||||
|
lval b5: R|B<B<ERROR CLASS: Symbol not found, for `UnexistingType`>>| = R|/B.B|<R|B<ERROR CLASS: Symbol not found, for `UnexistingType`>|>()
|
||||||
R|/fest|<R|kotlin/Boolean|>()
|
R|/fest|<R|kotlin/Boolean|>()
|
||||||
R|/fest|<R|C|>()
|
R|/fest|<R|C|>()
|
||||||
R|/fest|<R|C|>()
|
R|/fest|<R|C|>()
|
||||||
@@ -59,3 +62,13 @@ FILE: upperBoundViolated.kt
|
|||||||
public get(): R|NumColl<kotlin/collections/List<kotlin/Int>>|
|
public get(): R|NumColl<kotlin/collections/List<kotlin/Int>>|
|
||||||
public final val test8: R|NumColl<kotlin/collections/List<kotlin/String>>| = R|/NumColl.NumColl|<R|kotlin/String|>()
|
public final val test8: R|NumColl<kotlin/collections/List<kotlin/String>>| = R|/NumColl.NumColl|<R|kotlin/String|>()
|
||||||
public get(): R|NumColl<kotlin/collections/List<kotlin/String>>|
|
public get(): R|NumColl<kotlin/collections/List<kotlin/String>>|
|
||||||
|
public final class NumberPhile<T : R|kotlin/Number|> : R|kotlin/Any| {
|
||||||
|
public constructor<T : R|kotlin/Number|>(x: R|T|): R|NumberPhile<T>| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public final val np1: R|NumberPhile<kotlin/Int>| = R|/NumberPhile.NumberPhile|<R|kotlin/Int|>(Int(10))
|
||||||
|
public get(): R|NumberPhile<kotlin/Int>|
|
||||||
|
public final val np2: <ERROR TYPE REF: Inapplicable(INAPPLICABLE): [/NumberPhile.NumberPhile]> = <Inapplicable(INAPPLICABLE): [/NumberPhile.NumberPhile]>#(String(Test))
|
||||||
|
public get(): <ERROR TYPE REF: Inapplicable(INAPPLICABLE): [/NumberPhile.NumberPhile]>
|
||||||
|
|||||||
+64
-30
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.typeContext
|
|||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||||
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
|
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.min
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
|
object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
|
||||||
@@ -39,12 +40,16 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
|
|||||||
stubTypesEqualToAnything = false
|
stubTypesEqualToAnything = false
|
||||||
)
|
)
|
||||||
|
|
||||||
val parameterPairs = calleeFir.typeParameters.zip(functionCall.typeArguments)
|
val parameterPairs = mutableMapOf<FirTypeParameterSymbol, FirResolvedTypeRef>()
|
||||||
.map { (proto, actual) ->
|
val count = min(calleeFir.typeParameters.size, functionCall.typeArguments.size)
|
||||||
proto.symbol to actual.safeAs<FirTypeProjectionWithVariance>()
|
|
||||||
?.typeRef.safeAs<FirResolvedTypeRef>()
|
for (it in 0 until count) {
|
||||||
}
|
functionCall.typeArguments[it].safeAs<FirTypeProjectionWithVariance>()
|
||||||
.toMapWithoutNulls()
|
?.typeRef.safeAs<FirResolvedTypeRef>()
|
||||||
|
?.let { that ->
|
||||||
|
parameterPairs[calleeFir.typeParameters[it].symbol] = that
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// we substitute actual values to the
|
// we substitute actual values to the
|
||||||
// type parameters from the declaration
|
// type parameters from the declaration
|
||||||
@@ -52,16 +57,28 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
|
|||||||
parameterPairs.mapValues { it.value.type }
|
parameterPairs.mapValues { it.value.type }
|
||||||
)
|
)
|
||||||
|
|
||||||
parameterPairs.forEach { proto, actual ->
|
val canBeSkip = mutableSetOf<FirTypeParameterSymbol>()
|
||||||
|
|
||||||
|
parameterPairs.forEach { (proto, actual) ->
|
||||||
|
if (actual.source == null) {
|
||||||
|
// canBeSkip.add(proto) // inferred types report INAPPLICABLE_CANDIDATE in case of an error
|
||||||
|
// inferred types don't report INAPPLICABLE_CANDIDATE for typealiases!
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
if (!satisfiesBounds(proto, actual.type, substitutor, typeCheckerContext)) {
|
if (!satisfiesBounds(proto, actual.type, substitutor, typeCheckerContext)) {
|
||||||
reporter.report(actual.source)
|
reporter.report(actual.source)
|
||||||
return@forEach
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// we must analyze nested things like
|
// we must analyze nested things like
|
||||||
// S<S<K, L>, T<K, L>>()
|
// S<S<K, L>, T<K, L>>()
|
||||||
actual.type.safeAs<ConeClassLikeType>()?.let {
|
actual.type.safeAs<ConeClassLikeType>()?.let {
|
||||||
analyzeTypeParameters(it, context, reporter, typeCheckerContext, actual.source)
|
val errorOccurred = analyzeTypeParameters(it, context, reporter, typeCheckerContext, actual.source)
|
||||||
|
|
||||||
|
if (errorOccurred) {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +91,7 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
|
|||||||
// typealias A<G> = B<List<G>>
|
// typealias A<G> = B<List<G>>
|
||||||
// val a = A<Int>()
|
// val a = A<Int>()
|
||||||
when (calleeFir) {
|
when (calleeFir) {
|
||||||
is FirConstructor -> analyzeConstructorCall(functionCall, substitutor, typeCheckerContext, reporter)
|
is FirConstructor -> analyzeConstructorCall(functionCall, substitutor, typeCheckerContext, reporter, canBeSkip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +100,7 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
|
|||||||
callSiteSubstitutor: ConeSubstitutor,
|
callSiteSubstitutor: ConeSubstitutor,
|
||||||
typeCheckerContext: AbstractTypeCheckerContext,
|
typeCheckerContext: AbstractTypeCheckerContext,
|
||||||
reporter: DiagnosticReporter,
|
reporter: DiagnosticReporter,
|
||||||
|
canBeSkip: MutableSet<FirTypeParameterSymbol>
|
||||||
) {
|
) {
|
||||||
// holds Collection<Number> bound.
|
// holds Collection<Number> bound.
|
||||||
// note that if B used another type parameter here,
|
// note that if B used another type parameter here,
|
||||||
@@ -104,21 +122,26 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
|
|||||||
?.type.safeAs<ConeClassLikeType>()
|
?.type.safeAs<ConeClassLikeType>()
|
||||||
?: return
|
?: return
|
||||||
|
|
||||||
val constructorsParameterPairs = protoConstructor.typeParameters
|
val constructorsParameterPairs = mutableMapOf<FirTypeParameterSymbol, ConeSimpleKotlinType>()
|
||||||
.zip(actualConstructor.typeArguments)
|
val count = min(protoConstructor.typeParameters.size, actualConstructor.typeArguments.size)
|
||||||
.map { (proto, actual) ->
|
|
||||||
proto.symbol to actual.safeAs<ConeSimpleKotlinType>()
|
for (it in 0 until count) {
|
||||||
}
|
actualConstructor.typeArguments[it].safeAs<ConeSimpleKotlinType>()
|
||||||
.toMapWithoutNulls()
|
?.let { that ->
|
||||||
|
// if (!canBeSkip.contains(protoConstructor.typeParameters[it].symbol)) {
|
||||||
|
constructorsParameterPairs[protoConstructor.typeParameters[it].symbol] = that
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// we substitute typealias declaration
|
// we substitute typealias declaration
|
||||||
// parameters to the ones used in the
|
// parameters to the ones used in the
|
||||||
// typealias target
|
// typealias target
|
||||||
val declarationSiteSubstitutor = substitutorByMap(
|
val declarationSiteSubstitutor = substitutorByMap(
|
||||||
constructorsParameterPairs.mapValues { it.value.type }
|
constructorsParameterPairs.toMap().mapValues { it.value.type }
|
||||||
)
|
)
|
||||||
|
|
||||||
constructorsParameterPairs.forEach { proto, actual ->
|
constructorsParameterPairs.forEach { (proto, actual) ->
|
||||||
// just in case
|
// just in case
|
||||||
var intersection = typeCheckerContext.intersectTypes(
|
var intersection = typeCheckerContext.intersectTypes(
|
||||||
proto.fir.bounds.filterIsInstance<FirResolvedTypeRef>().map { it.type }
|
proto.fir.bounds.filterIsInstance<FirResolvedTypeRef>().map { it.type }
|
||||||
@@ -134,7 +157,7 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
|
|||||||
|
|
||||||
if (!satisfiesBounds) {
|
if (!satisfiesBounds) {
|
||||||
reporter.report(functionCall.source)
|
reporter.report(functionCall.source)
|
||||||
return@forEach
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,6 +167,7 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
|
|||||||
* and reports the diagnostic on the given
|
* and reports the diagnostic on the given
|
||||||
* reportTarget (because we can't report them
|
* reportTarget (because we can't report them
|
||||||
* on type parameters themselves now).
|
* on type parameters themselves now).
|
||||||
|
* Returns true if an error occured
|
||||||
*/
|
*/
|
||||||
private fun analyzeTypeParameters(
|
private fun analyzeTypeParameters(
|
||||||
type: ConeClassLikeType,
|
type: ConeClassLikeType,
|
||||||
@@ -151,30 +175,40 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() {
|
|||||||
reporter: DiagnosticReporter,
|
reporter: DiagnosticReporter,
|
||||||
typeCheckerContext: AbstractTypeCheckerContext,
|
typeCheckerContext: AbstractTypeCheckerContext,
|
||||||
reportTarget: FirSourceElement?
|
reportTarget: FirSourceElement?
|
||||||
) {
|
): Boolean {
|
||||||
val prototypeClass = type.lookupTag.toSymbol(context.session)
|
val prototypeClass = type.lookupTag.toSymbol(context.session)
|
||||||
?.fir.safeAs<FirRegularClass>()
|
?.fir.safeAs<FirRegularClass>()
|
||||||
?: return
|
?: return false
|
||||||
|
|
||||||
val parameterPairs = prototypeClass.typeParameters.zip(type.typeArguments)
|
val parameterPairs = mutableMapOf<FirTypeParameterSymbol, ConeClassLikeType>()
|
||||||
.map { (proto, actual) ->
|
val count = min(prototypeClass.typeParameters.size, type.typeArguments.size)
|
||||||
proto.symbol to actual.safeAs<ConeClassLikeType>()
|
|
||||||
}
|
for (it in 0 until count) {
|
||||||
.toMapWithoutNulls()
|
type.typeArguments[it].safeAs<ConeClassLikeType>()
|
||||||
|
?.let { that ->
|
||||||
|
parameterPairs[prototypeClass.typeParameters[it].symbol] = that
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val substitutor = substitutorByMap(
|
val substitutor = substitutorByMap(
|
||||||
parameterPairs.mapValues { it.value.type }
|
parameterPairs.toMap().mapValues { it.value.type }
|
||||||
)
|
)
|
||||||
|
|
||||||
parameterPairs.forEach { proto, actual ->
|
parameterPairs.forEach { (proto, actual) ->
|
||||||
if (!satisfiesBounds(proto, actual.type, substitutor, typeCheckerContext)) {
|
if (!satisfiesBounds(proto, actual.type, substitutor, typeCheckerContext)) {
|
||||||
// should report on the parameter instead!
|
// should report on the parameter instead!
|
||||||
reporter.report(reportTarget)
|
reporter.report(reportTarget)
|
||||||
return@forEach
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzeTypeParameters(actual, context, reporter, typeCheckerContext, reportTarget)
|
val errorOccurred = analyzeTypeParameters(actual, context, reporter, typeCheckerContext, reportTarget)
|
||||||
|
|
||||||
|
if (errorOccurred) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user