[FIR] Add FINAL_UPPER_BOUND checker

This commit is contained in:
Andrey Zinovyev
2021-04-05 14:51:44 +03:00
committed by GitHub
parent d0513c9ece
commit 67505a0071
36 changed files with 232 additions and 180 deletions
@@ -38,15 +38,15 @@ fun test(z: Int, c: Char) {}
typealias BA = A
fun <T> kek(t: T) where T : (String) -> Any?, T : Char {}
fun <T> kek(t: T) where T : () -> Boolean, T : String {}
fun <T : Int> kek(t: T) {}
fun <T> kek(t: T) where T : (String) -> Any?, T : <!FINAL_UPPER_BOUND!>Char<!> {}
fun <T> kek(t: T) where T : () -> Boolean, T : <!FINAL_UPPER_BOUND!>String<!> {}
fun <T : <!FINAL_UPPER_BOUND!>Int<!>> kek(t: T) {}
fun lol(a: Array<Int>) {}
fun lol(a: Array<Boolean>) {}
<!CONFLICTING_OVERLOADS!>fun <T> mem(t: T)<!> where T : () -> Boolean, T : String {}
<!CONFLICTING_OVERLOADS!>fun <T> mem(t: T)<!> where T : String, T : () -> Boolean {}
<!CONFLICTING_OVERLOADS!>fun <T> mem(t: T)<!> where T : () -> Boolean, T : <!FINAL_UPPER_BOUND!>String<!> {}
<!CONFLICTING_OVERLOADS!>fun <T> mem(t: T)<!> where T : <!FINAL_UPPER_BOUND!>String<!>, T : () -> Boolean {}
class M {
companion <!REDECLARATION!>object<!> {}
@@ -14,7 +14,7 @@ open class D() : B() {
override var test: <!VAR_TYPE_MISMATCH_ON_OVERRIDE!>Char<!> = '\n'
}
class E<T : Double>(val value: T) : B() {
class E<T : <!FINAL_UPPER_BOUND!>Double<!>>(val value: T) : B() {
override var test: <!VAR_TYPE_MISMATCH_ON_OVERRIDE!>T<!> = value
}
@@ -22,14 +22,14 @@ open class F<T : Number>(val value: T) {
open var rest: T = value
}
class G<E : Double>(val balue: E) : F<E>(balue) {
class G<E : <!FINAL_UPPER_BOUND!>Double<!>>(val balue: E) : F<E>(balue) {
override var rest: E = balue
}
class H<E : String>(val balue: E) : <!INAPPLICABLE_CANDIDATE!>F<E><!>(balue) {
class H<E : <!FINAL_UPPER_BOUND!>String<!>>(val balue: E) : <!INAPPLICABLE_CANDIDATE!>F<E><!>(balue) {
override var rest: E = balue // no report because of INAPPLICABLE_CANDIDATE
}
class M<E : String>(val balue: E) : F<Double>(3.14) {
class M<E : <!FINAL_UPPER_BOUND!>String<!>>(val balue: E) : F<Double>(3.14) {
override var rest: <!VAR_TYPE_MISMATCH_ON_OVERRIDE!>E<!> = balue
}
@@ -15,7 +15,7 @@ open class D() : B() {
override fun test(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Char<!> = '\n'
}
class E<T : Double>(val value: T) : B() {
class E<T : <!FINAL_UPPER_BOUND!>Double<!>>(val value: T) : B() {
override fun test(): T = value
}
@@ -23,15 +23,15 @@ open class F<T : Number>(val value: T) {
open fun rest(): T = value
}
class G<E : Double>(val balue: E) : F<E>(balue) {
class G<E : <!FINAL_UPPER_BOUND!>Double<!>>(val balue: E) : F<E>(balue) {
override fun rest(): E = balue
}
class H<E : String>(val balue: E) : <!INAPPLICABLE_CANDIDATE!>F<E><!>(balue) {
class H<E : <!FINAL_UPPER_BOUND!>String<!>>(val balue: E) : <!INAPPLICABLE_CANDIDATE!>F<E><!>(balue) {
override fun rest(): E = balue // no report because of INAPPLICABLE_CANDIDATE
}
class M<E : String>(val balue: E) : F<Double>(3.14) {
class M<E : <!FINAL_UPPER_BOUND!>String<!>>(val balue: E) : F<Double>(3.14) {
override fun rest(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>E<!> = balue
}
@@ -35,6 +35,7 @@ fun main(args: Array<String>) {
alias<FirRegularClass>("RegularClassChecker")
alias<FirConstructor>("ConstructorChecker")
alias<FirFile>("FileChecker")
alias<FirTypeParameter>("FirTypeParameterChecker")
additional(
fieldName = "controlFlowAnalyserCheckers",
@@ -289,6 +289,10 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
val TYPE_PARAMETER_AS_REIFIED by error<FirSourceElement, PsiElement> {
parameter<FirTypeParameterSymbol>("typeParameter")
}
val FINAL_UPPER_BOUND by warning<FirSourceElement, PsiElement> {
parameter<ConeKotlinType>("type")
}
}
val REFLECTION by object : DiagnosticGroup("Reflection") {
@@ -33,6 +33,8 @@ internal class ComposedDeclarationCheckers : DeclarationCheckers() {
get() = _constructorCheckers
override val fileCheckers: Set<FirFileChecker>
get() = _fileCheckers
override val typeParameterCheckers: Set<FirTypeParameterChecker>
get() = _typeParameterCheckers
override val controlFlowAnalyserCheckers: Set<FirControlFlowChecker>
get() = _controlFlowAnalyserCheckers
override val variableAssignmentCfaBasedCheckers: Set<AbstractFirPropertyInitializationChecker>
@@ -47,6 +49,7 @@ internal class ComposedDeclarationCheckers : DeclarationCheckers() {
private val _regularClassCheckers: MutableSet<FirRegularClassChecker> = mutableSetOf()
private val _constructorCheckers: MutableSet<FirConstructorChecker> = mutableSetOf()
private val _fileCheckers: MutableSet<FirFileChecker> = mutableSetOf()
private val _typeParameterCheckers: MutableSet<FirTypeParameterChecker> = mutableSetOf()
private val _controlFlowAnalyserCheckers: MutableSet<FirControlFlowChecker> = mutableSetOf()
private val _variableAssignmentCfaBasedCheckers: MutableSet<AbstractFirPropertyInitializationChecker> = mutableSetOf()
@@ -61,6 +64,7 @@ internal class ComposedDeclarationCheckers : DeclarationCheckers() {
_regularClassCheckers += checkers.regularClassCheckers
_constructorCheckers += checkers.constructorCheckers
_fileCheckers += checkers.fileCheckers
_typeParameterCheckers += checkers.typeParameterCheckers
_controlFlowAnalyserCheckers += checkers.controlFlowAnalyserCheckers
_variableAssignmentCfaBasedCheckers += checkers.variableAssignmentCfaBasedCheckers
}
@@ -28,6 +28,7 @@ abstract class DeclarationCheckers {
open val regularClassCheckers: Set<FirRegularClassChecker> = emptySet()
open val constructorCheckers: Set<FirConstructorChecker> = emptySet()
open val fileCheckers: Set<FirFileChecker> = emptySet()
open val typeParameterCheckers: Set<FirTypeParameterChecker> = emptySet()
open val controlFlowAnalyserCheckers: Set<FirControlFlowChecker> = emptySet()
open val variableAssignmentCfaBasedCheckers: Set<AbstractFirPropertyInitializationChecker> = emptySet()
@@ -41,4 +42,5 @@ abstract class DeclarationCheckers {
@CheckersComponentInternal internal val allRegularClassCheckers: Set<FirRegularClassChecker> get() = regularClassCheckers + memberDeclarationCheckers + basicDeclarationCheckers + classCheckers
@CheckersComponentInternal internal val allConstructorCheckers: Set<FirConstructorChecker> get() = constructorCheckers + functionCheckers + basicDeclarationCheckers + memberDeclarationCheckers
@CheckersComponentInternal internal val allFileCheckers: Set<FirFileChecker> get() = fileCheckers + basicDeclarationCheckers
@CheckersComponentInternal internal val allTypeParameterCheckers: Set<FirTypeParameterChecker> get() = typeParameterCheckers + basicDeclarationCheckers
}
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
typealias FirBasicDeclarationChecker = FirDeclarationChecker<FirDeclaration>
typealias FirMemberDeclarationChecker = FirDeclarationChecker<FirMemberDeclaration>
@@ -29,3 +30,4 @@ typealias FirClassChecker = FirDeclarationChecker<FirClass<*>>
typealias FirRegularClassChecker = FirDeclarationChecker<FirRegularClass>
typealias FirConstructorChecker = FirDeclarationChecker<FirConstructor>
typealias FirFileChecker = FirDeclarationChecker<FirFile>
typealias FirTypeParameterChecker = FirDeclarationChecker<FirTypeParameter>
@@ -214,6 +214,7 @@ object FirErrors {
val INNER_CLASS_OF_GENERIC_THROWABLE_SUBCLASS by error0<FirSourceElement, KtClassOrObject>(SourceElementPositioningStrategies.DECLARATION_NAME)
val KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE by error1<FirSourceElement, KtNamedDeclaration, FirTypeParameterSymbol>(SourceElementPositioningStrategies.DECLARATION_NAME)
val TYPE_PARAMETER_AS_REIFIED by error1<FirSourceElement, PsiElement, FirTypeParameterSymbol>()
val FINAL_UPPER_BOUND by warning1<FirSourceElement, PsiElement, ConeKotlinType>()
// Reflection
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<FirSourceElement, KtExpression, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.BuiltinTypes
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.isSupertypeOf
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription
@@ -199,9 +200,6 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
else -> true
}
fun KotlinTypeMarker.isSupertypeOf(context: TypeCheckerProviderContext, type: KotlinTypeMarker?) =
type != null && AbstractTypeChecker.isSubtypeOf(context, type, this)
private fun simpleTypeStatement(realVar: RealVariable, exactType: Boolean, type: ConeKotlinType): MutableTypeStatement {
return MutableTypeStatement(
realVar,
@@ -5,13 +5,11 @@
package org.jetbrains.kotlin.fir.analysis.checkers
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.cfa.FirReturnsImpliesAnalyzer.isSupertypeOf
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.modalityModifier
import org.jetbrains.kotlin.fir.analysis.diagnostics.overrideModifier
@@ -30,16 +28,16 @@ import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtModifierList
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtParameter.VAL_VAR_TOKEN_SET
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal fun FirClass<*>.unsubstitutedScope(context: CheckerContext) =
@@ -321,3 +319,81 @@ val FirValueParameter.hasValOrVar: Boolean
val source = this.source ?: return false
return source.getChild(VAL_VAR_TOKEN_SET) != null
}
fun KotlinTypeMarker.isSupertypeOf(context: TypeCheckerProviderContext, type: KotlinTypeMarker?) =
type != null && AbstractTypeChecker.isSubtypeOf(context, type, this)
fun KotlinTypeMarker.isSubtypeOf(context: TypeCheckerProviderContext, type: KotlinTypeMarker?) =
type != null && AbstractTypeChecker.isSubtypeOf(context, this, type)
fun ConeKotlinType.canHaveSubtypes(session: FirSession): Boolean {
if (this.isMarkedNullable) {
return true
}
val clazz = toRegularClass(session) ?: return true
if (clazz.isEnumClass || clazz.isExpect || clazz.modality != Modality.FINAL) {
return true
}
clazz.typeParameters.forEachIndexed { idx, typeParameterRef ->
val typeParameter = typeParameterRef.symbol.fir
val typeProjection = typeArguments[idx]
if (typeProjection.isStarProjection) {
return true
}
val argument = typeProjection.type!! //safe because it is not a star
when (typeParameter.variance) {
Variance.INVARIANT ->
when (typeProjection.kind) {
ProjectionKind.INVARIANT ->
if (lowerThanBound(session.typeContext, argument, typeParameter) || argument.canHaveSubtypes(session)) {
return true
}
ProjectionKind.IN ->
if (lowerThanBound(session.typeContext, argument, typeParameter)) {
return true
}
ProjectionKind.OUT ->
if (argument.canHaveSubtypes(session)) {
return true
}
ProjectionKind.STAR ->
return true
}
Variance.IN_VARIANCE ->
if (typeProjection.kind != ProjectionKind.OUT) {
if (lowerThanBound(session.typeContext, argument, typeParameter)) {
return true
}
} else {
if (argument.canHaveSubtypes(session)) {
return true
}
}
Variance.OUT_VARIANCE ->
if (typeProjection.kind != ProjectionKind.IN) {
if (argument.canHaveSubtypes(session)) {
return true
}
} else {
if (lowerThanBound(session.typeContext, argument, typeParameter)) {
return true
}
}
}
}
return false
}
private fun lowerThanBound(context: ConeInferenceContext, argument: ConeKotlinType, typeParameter: FirTypeParameter): Boolean {
typeParameter.bounds.forEach { boundTypeRef ->
if (argument != boundTypeRef.coneType && argument.isSubtypeOf(context, boundTypeRef.coneType)) {
return true
}
}
return false
}
@@ -0,0 +1,30 @@
/*
* 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.analysis.checkers.declaration
import org.jetbrains.kotlin.fir.analysis.checkers.canHaveSubtypes
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.types.coneType
object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
override fun check(declaration: FirTypeParameter, context: CheckerContext, reporter: DiagnosticReporter) {
val containingDeclaration = context.containingDeclarations.lastOrNull()
if (containingDeclaration is FirConstructor) return
if (containingDeclaration is FirSimpleFunction && containingDeclaration.isOverride) return
if (containingDeclaration is FirProperty && containingDeclaration.isOverride) return
declaration.symbol.fir.bounds.forEach { bound ->
if (!bound.coneType.canHaveSubtypes(context.session)) {
reporter.reportOn(bound.source, FirErrors.FINAL_UPPER_BOUND, bound.coneType, context)
}
}
}
}
@@ -61,7 +61,7 @@ class DeclarationCheckersDiagnosticComponent(
}
override fun visitTypeParameter(typeParameter: FirTypeParameter, data: CheckerContext) {
checkers.allBasicDeclarationCheckers.check(typeParameter, data, reporter)
checkers.allTypeParameterCheckers.check(typeParameter, data, reporter)
}
override fun visitEnumEntry(enumEntry: FirEnumEntry, data: CheckerContext) {
@@ -91,6 +91,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPRESSION_OF_NUL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXTENSION_PROPERTY_WITH_BACKING_FIELD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FINAL_UPPER_BOUND
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_VARARG_PARAMETER_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUNCTION_DECLARATION_WITH_NO_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.GENERIC_THROWABLE_SUBCLASS
@@ -438,6 +439,11 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
)
map.put(TYPE_PARAMETER_AS_REIFIED, "Cannot use ''{0}'' as reified type parameter. Use a class instead", SYMBOL)
map.put(
FINAL_UPPER_BOUND,
"''{0}'' is a final type, and thus a value of the type parameter is predetermined",
RENDER_TYPE
)
// Reflection
map.put(
@@ -87,4 +87,8 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
override val variableAssignmentCfaBasedCheckers: Set<AbstractFirPropertyInitializationChecker> = setOf(
FirPropertyInitializationAnalyzer,
)
override val typeParameterCheckers: Set<FirTypeParameterChecker> = setOf(
FirTypeParameterBoundsChecker,
)
}
+1 -1
View File
@@ -40,4 +40,4 @@ fun test() {
fun <T : Any> foo() {}
fun <T : Int?> bar() {}
fun <T : Int> Int.buzz() : Unit {}
fun <T : <!FINAL_UPPER_BOUND!>Int<!>> Int.buzz() : Unit {}
+4 -4
View File
@@ -43,12 +43,12 @@ fun test() {
class Foo() {}
class Bar<T : Foo>
class Bar<T : <!FINAL_UPPER_BOUND!>Foo<!>>
class Buzz<T> where T : Bar<Int>, T : <!UNRESOLVED_REFERENCE!>nioho<!>
class Buzz<T> where T : <!FINAL_UPPER_BOUND!>Bar<Int><!>, T : <!UNRESOLVED_REFERENCE!>nioho<!>
class X<T : Foo>
class Y<T> where T : Foo, T : Bar<Foo>
class X<T : <!FINAL_UPPER_BOUND!>Foo<!>>
class Y<T> where T : <!FINAL_UPPER_BOUND!>Foo<!>, T : <!FINAL_UPPER_BOUND!>Bar<Foo><!>
fun <T> test2(t : T)
where
@@ -5,7 +5,7 @@ fun <T> takeFun(f: (T) -> Unit) {}
fun <T, R> callFun(f: (T) -> R): R = TODO()
fun <T> foo(s: T) {}
fun <T : Int> fooInt(s: T) {}
fun <T : <!FINAL_UPPER_BOUND!>Int<!>> fooInt(s: T) {}
open class Wrapper<T>(val value: T)
fun <T, R : Wrapper<in T>> createWrapper(s: T): R = TODO()
@@ -1,97 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// SKIP_TXT
// Issues: KT-25105
class Message1
class Task<T>
object Message2
enum class Message3
data class Message4(val x: Int)
sealed class Message5<T> {
open fun <A : T> execute() {}
}
interface Manager<T> {
fun <A : T> execute1(task: Task<A>) {}
fun <T : Int> execute2(task: T) {}
fun <T : Message2> execute3() {}
fun <A : T> execute4() {}
fun <A : T> execute5() {}
val <A : T> A.x get() = 10
var <A : T> A.y
get() = 10
set(value) {}
}
object MessageManager1 : Manager<Message1> {
override fun <T : Message1> execute1(task: Task<T>) {}
override fun <T : Int> execute2(task: T) {}
override fun <T : Message2> execute3() {}
}
object MessageManager2 : Manager<Message3> {
override fun <T : Message3> execute4() {}
}
object MessageManager3 : Manager<Message4> {
override fun <T : Message4> execute5() {}
}
class MessageManager4 : Manager<Message1> {
override fun <T : Message1> execute1(task: Task<T>) {}
override fun <T : Int> execute2(task: T) {}
override fun <T : Message2> execute3() {}
}
class MessageManager5 : Manager<Message3> {
override fun <T : Message3> execute4() {}
}
class MessageManager6 : Manager<Message4> {
override fun <T : Message4> execute5() {}
}
interface MessageManager7 : Manager<Message4> {
override fun <T : Message4> execute5() {}
}
interface MessageManager8 : Manager<Message1> {
override fun <T : Message1> execute1(task: Task<T>) {}
override fun <T : Int> execute2(task: T) {}
override fun <T : Message2> execute3() {}
}
interface MessageManager9 : Manager<Message3> {
override fun <T : Message3> execute4() {}
}
object MessageManager10 : Message5<Int>() {
override fun <T : Int> execute() {}
}
class MessageManager11<A> : Message5<Message5<A>>() {
override fun <T : Message5<A>> execute() {}
}
data class MessageManager12(val x: Int) : Message5<Message2>() {
override fun <T : Message2> execute() {}
}
sealed class MessageManager13<A> : Message5<A>() {
override fun <T : A> execute() {}
}
class MessageManager14 : Manager<Message2> {
override val <T : Message2> T.x get() = 10
override var <T : Message2> T.y
get() = 10
set(value) {}
}
object MessageManager15 : Manager<Int> {
override val <T : Int> T.x get() = 10
override var <T : Int> T.y
get() = 10
set(value) {}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
// SKIP_TXT
// Issues: KT-25105
@@ -10,9 +10,9 @@ data class Message4(val x: Int)
interface Manager<T> {}
object MessageManager1 : Manager<Message1> {
fun <T : Message1> execute1(task: Task<T>) {}
fun <T : Int> execute2(task: T) {}
fun <T : Message2> execute3() {}
fun <T : <!FINAL_UPPER_BOUND!>Message1<!>> execute1(task: Task<T>) {}
fun <T : <!FINAL_UPPER_BOUND!>Int<!>> execute2(task: T) {}
fun <T : <!FINAL_UPPER_BOUND!>Message2<!>> execute3() {}
}
object MessageManager2 : Manager<Message3> {
@@ -20,13 +20,13 @@ object MessageManager2 : Manager<Message3> {
}
object MessageManager3 : Manager<Message4> {
fun <T : Message4> execute5() {}
fun <T : <!FINAL_UPPER_BOUND!>Message4<!>> execute5() {}
}
class MessageManager4 : Manager<Message1> {
fun <T : Message1> execute1(task: Task<T>) {}
fun <T : Int> execute2(task: T) {}
fun <T : Message2> execute3() {}
fun <T : <!FINAL_UPPER_BOUND!>Message1<!>> execute1(task: Task<T>) {}
fun <T : <!FINAL_UPPER_BOUND!>Int<!>> execute2(task: T) {}
fun <T : <!FINAL_UPPER_BOUND!>Message2<!>> execute3() {}
}
class MessageManager5 : Manager<Message3> {
@@ -34,17 +34,17 @@ class MessageManager5 : Manager<Message3> {
}
class MessageManager6 : Manager<Message4> {
fun <T : Message4> execute5() {}
fun <T : <!FINAL_UPPER_BOUND!>Message4<!>> execute5() {}
}
interface MessageManager7 : Manager<Message4> {
fun <T : Message4> execute5() {}
fun <T : <!FINAL_UPPER_BOUND!>Message4<!>> execute5() {}
}
interface MessageManager8 : Manager<Message1> {
fun <T : Message1> execute1(task: Task<T>) {}
fun <T : Int> execute2(task: T) {}
fun <T : Message2> execute3() {}
fun <T : <!FINAL_UPPER_BOUND!>Message1<!>> execute1(task: Task<T>) {}
fun <T : <!FINAL_UPPER_BOUND!>Int<!>> execute2(task: T) {}
fun <T : <!FINAL_UPPER_BOUND!>Message2<!>> execute3() {}
}
interface MessageManager9 : Manager<Message3> {
@@ -52,7 +52,7 @@ interface MessageManager9 : Manager<Message3> {
}
object MessageManager10 : <!UNRESOLVED_REFERENCE!>Message5<Int><!>() {
fun <T : Int> execute() {}
fun <T : <!FINAL_UPPER_BOUND!>Int<!>> execute() {}
}
class MessageManager11<A> : <!UNRESOLVED_REFERENCE!>Message5<<!UNRESOLVED_REFERENCE!>Message5<A><!>><!>() {
@@ -60,7 +60,7 @@ class MessageManager11<A> : <!UNRESOLVED_REFERENCE!>Message5<<!UNRESOLVED_REFERE
}
data class MessageManager12(val x: Int) : <!UNRESOLVED_REFERENCE!>Message5<Message2><!>() {
fun <T : Message2> execute() {}
fun <T : <!FINAL_UPPER_BOUND!>Message2<!>> execute() {}
}
sealed class MessageManager13<A> : <!UNRESOLVED_REFERENCE!>Message5<A><!>() {
@@ -68,15 +68,15 @@ sealed class MessageManager13<A> : <!UNRESOLVED_REFERENCE!>Message5<A><!>() {
}
class MessageManager14 : Manager<Message2> {
val <T : Message2> T.x get() = 10
var <T : Message2> T.y
val <T : <!FINAL_UPPER_BOUND!>Message2<!>> T.x get() = 10
var <T : <!FINAL_UPPER_BOUND!>Message2<!>> T.y
get() = 10
set(value) {}
}
object MessageManager15 : Manager<Int> {
val <T : Int> T.x get() = 10
var <T : Int> T.y
val <T : <!FINAL_UPPER_BOUND!>Int<!>> T.x get() = 10
var <T : <!FINAL_UPPER_BOUND!>Int<!>> T.y
get() = 10
set(value) {}
}
@@ -53,7 +53,7 @@ fun testCaptured(cSub: SubInv<out Number>, cInv: Inv<out Number>) {
fun testVariableWithBound() {
fun <K : Number> createWithNumberBound(): Inv<K> = TODO()
fun <K : Int> createWithIntBound(): Inv<K> = TODO()
fun <K : <!FINAL_UPPER_BOUND!>Int<!>> createWithIntBound(): Inv<K> = TODO()
val c1 = select(SubInv<Int>(), createWithNumberBound())
@@ -15,4 +15,4 @@ enum class E1
class A5<T> where T : C1, T : E1
object O1
class A6<T> where T : O1, T : C2
class A6<T> where T : <!FINAL_UPPER_BOUND!>O1<!>, T : C2
@@ -2,7 +2,7 @@ fun <A : Array<Any>> f1() {}
fun <T, A : Array<out T>> f2() {}
fun <S, T : S, A> f3() where A : Array<out S>, A : Array<out T> {}
fun <T : IntArray> f4() {}
fun <T : <!FINAL_UPPER_BOUND!>IntArray<!>> f4() {}
fun <T> f5() where T : Array<Any> {}
@@ -110,15 +110,15 @@ fun <T : Number> T.case_19_4(): Boolean? {
}
// TESTCASE NUMBER: 20
fun <T : String> T?.case_20_1(): Boolean {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_20_1(): Boolean {
contract { returns(true) implies (this@case_20_1 != null) }
return this@case_20_1 != null
}
fun <T : String> T?.case_20_2(): Boolean {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_20_2(): Boolean {
contract { returns(true) implies (this@case_20_2 == null) }
return this@case_20_2 == null
}
fun <T : String> T?.case_20_3(): Boolean {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_20_3(): Boolean {
contract { returns(false) implies (this@case_20_3 != null) }
return !(this@case_20_3 != null)
}
@@ -132,11 +132,11 @@ fun <T : String?> T.case_21_2(): Boolean {
contract { returns(true) implies (this@case_21_2 == null) }
return this@case_21_2 == null
}
fun <T : String> T?.case_21_5(): Boolean? {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_21_5(): Boolean? {
contract { returnsNotNull() implies (this@case_21_5 != null) }
return if (this@case_21_5 != null) true else null
}
fun <T : String> T?.case_21_7(): Boolean? {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_21_7(): Boolean? {
contract { returns(null) implies (this@case_21_7 != null) }
return if (this@case_21_7 != null) null else true
}
@@ -20,11 +20,11 @@ fun <T : Number> T.case_2() {
}
// TESTCASE NUMBER: 3
fun <T : String> T?.case_3_1() {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_3_1() {
contract { returns() implies (this@case_3_1 == null) }
if (!(this@case_3_1 == null)) throw Exception()
}
fun <T : String> T?.case_3_2() {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_3_2() {
contract { returns() implies (this@case_3_2 != null) }
if (!(this@case_3_2 != null)) throw Exception()
}
@@ -76,19 +76,19 @@ fun <T : Number> T.case_6_4(): Boolean? {
}
// TESTCASE NUMBER: 7
fun <T : String> T?.case_7_1(): Boolean {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_1(): Boolean {
contract { returns(true) implies (this@case_7_1 == null) }
return this@case_7_1 == null
}
fun <T : String> T?.case_7_2(): Boolean {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_2(): Boolean {
contract { returns(false) implies (this@case_7_2 != null) }
return !(this@case_7_2 != null)
}
fun <T : String> T?.case_7_3(): Boolean? {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_3(): Boolean? {
contract { returnsNotNull() implies (this@case_7_3 != null) }
return if (this@case_7_3 != null) true else null
}
fun <T : String> T?.case_7_4(): Boolean? {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_4(): Boolean? {
contract { returns(null) implies (this@case_7_4 != null) }
return if (this@case_7_4 != null) null else true
}
@@ -20,11 +20,11 @@ fun <T : Number> T.case_2() {
}
// TESTCASE NUMBER: 3
fun <T : String> T?.case_3_1() {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_3_1() {
contract { returns() implies (this@case_3_1 != null) }
if (!(this@case_3_1 != null)) throw Exception()
}
fun <T : String> T?.case_3_2() {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_3_2() {
contract { returns() implies (this@case_3_2 == null) }
if (!(this@case_3_2 == null)) throw Exception()
}
@@ -76,47 +76,47 @@ fun <T : Number> T.case_6_4(): Boolean? {
}
// TESTCASE NUMBER: 7
fun <T : String> T?.case_7_1(): Boolean {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_1(): Boolean {
contract { returns(true) implies (this@case_7_1 != null) }
return this@case_7_1 != null
}
fun <T : String> T?.case_7_2(): Boolean {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_2(): Boolean {
contract { returns(true) implies (this@case_7_2 == null) }
return this@case_7_2 == null
}
fun <T : String> T?.case_7_3(): Boolean? {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_3(): Boolean? {
contract { returnsNotNull() implies (this@case_7_3 == null) }
return if (this@case_7_3 == null) true else null
}
fun <T : String> T?.case_7_4(): Boolean? {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_4(): Boolean? {
contract { returns(null) implies (this@case_7_4 == null) }
return if (this@case_7_4 == null) null else true
}
fun <T : String> T?.case_7_5(): Boolean {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_5(): Boolean {
contract { returns(false) implies (this@case_7_5 == null) }
return !(this@case_7_5 == null)
}
fun <T : String> T?.case_7_6(): Boolean? {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_6(): Boolean? {
contract { returnsNotNull() implies (this@case_7_6 != null) }
return if (this@case_7_6 != null) true else null
}
fun <T : String> T?.case_7_7(): Boolean? {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_7(): Boolean? {
contract { returns(null) implies (this@case_7_7 != null) }
return if (this@case_7_7 != null) null else true
}
fun <T : String> T?.case_7_8(): Boolean {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_8(): Boolean {
contract { returns(false) implies (this@case_7_8 != null) }
return !(this@case_7_8 != null)
}
fun <T : String> T?.case_7_9(): Boolean {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_9(): Boolean {
contract { returns(false) implies (this@case_7_9 == null) }
return !(this@case_7_9 == null)
}
fun <T : String> T?.case_7_10(): Boolean? {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_10(): Boolean? {
contract { returnsNotNull() implies (this@case_7_10 == null) }
return if (this@case_7_10 == null) true else null
}
fun <T : String> T?.case_7_11(): Boolean? {
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_7_11(): Boolean? {
contract { returns(null) implies (this@case_7_11 == null) }
return if (this@case_7_11 == null) null else true
}
@@ -3,7 +3,7 @@
import kotlin.contracts.*
// TESTCASE NUMBER: 1
fun <T : Boolean>T.case_1(): Boolean? {
fun <T : <!FINAL_UPPER_BOUND!>Boolean<!>>T.case_1(): Boolean? {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(null) implies (!this@case_1)<!> }
return if (!this) null else true
}
@@ -9,7 +9,7 @@ fun Boolean?.case_1(): Boolean {
}
// TESTCASE NUMBER: 2
fun <T : Boolean>T?.case_2(): Boolean {
fun <T : <!FINAL_UPPER_BOUND!>Boolean<!>>T?.case_2(): Boolean {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(true) implies (this@case_2 != null && this@case_2 !is Nothing && this@case_2)<!> }
return this != null && this !is Nothing && this
}
@@ -10,7 +10,7 @@ fun <T : Number?> T.case_1() {
}
// TESTCASE NUMBER: 2
fun <T : Number, K : String> T?.case_2(value_1: K?) {
fun <T : Number, K : <!FINAL_UPPER_BOUND!>String<!>> T?.case_2(value_1: K?) {
contract { returns() implies (this@case_2 is T && value_1 is K) }
if (!(this@case_2 is T && value_1 is K)) throw Exception()
}
@@ -36,7 +36,7 @@ class case_4 : ClassLevel3() {
return this == null
}
fun <T : Boolean>T.case_4_2() {
fun <T : <!FINAL_UPPER_BOUND!>Boolean<!>>T.case_4_2() {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (!this@case_4_2)<!> }
if (this) throw Exception()
}
@@ -35,7 +35,7 @@ inline fun <reified T>case_3(x: Any?) {
}
// TESTCASE NUMBER: 4
inline fun <reified T : Boolean>case_4(x: Any?) {
inline fun <reified T : <!FINAL_UPPER_BOUND!>Boolean<!>>case_4(x: Any?) {
if (x is Int is T == null) {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>.<!UNRESOLVED_REFERENCE!>inv<!>()
@@ -97,8 +97,8 @@ fun case_9() {
* UNEXPECTED BEHAVIOUR
* ISSUES: KT-22175
*/
fun <T : String> T?.case_10() = this
fun <T : Int> T?.case_10() = this
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_10() = this
fun <T : <!FINAL_UPPER_BOUND!>Int<!>> T?.case_10() = this
fun case_10() {
var x: Int? = 10
x = null
@@ -106,7 +106,7 @@ fun case_10() {
}
// TESTCASE NUMBER: 11
fun <T : String> T?.case_11() = this
fun <T : <!FINAL_UPPER_BOUND!>String<!>> T?.case_11() = this
fun case_11() {
var x: Int? = 10
x = null
@@ -881,6 +881,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.FINAL_UPPER_BOUND) { firDiagnostic ->
FinalUpperBoundImpl(
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic ->
ExtensionInClassReferenceNotAllowedImpl(
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration),
@@ -628,6 +628,11 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val typeParameter: KtTypeParameterSymbol
}
abstract class FinalUpperBound : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = FinalUpperBound::class
abstract val type: KtType
}
abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class
abstract val referencedDeclaration: KtCallableSymbol
@@ -1010,6 +1010,14 @@ internal class TypeParameterAsReifiedImpl(
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class FinalUpperBoundImpl(
override val type: KtType,
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.FinalUpperBound(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class ExtensionInClassReferenceNotAllowedImpl(
override val referencedDeclaration: KtCallableSymbol,
firDiagnostic: FirPsiDiagnostic<*>,