[FIR] Prohibit referencing type parameters in contracts ...

...if they are not reified or not belong to owner declaration of the contract

KT-57911
This commit is contained in:
Dmitriy Novozhilov
2023-04-12 17:44:51 +03:00
committed by Space Team
parent 5a92eb2c67
commit f8dc8057f0
13 changed files with 302 additions and 11 deletions
@@ -12,9 +12,9 @@ inline fun <reified T> referToReifiedGeneric(x: Any?) {
class Generic<T> {
fun referToCaptured(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is T)
}<!>
contract {
<!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (x is T)<!>
}
}
}
@@ -50,4 +50,4 @@ fun referToAliasedSimpleType(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is SimpleType)
}<!>
}
}
@@ -12,9 +12,9 @@ inline fun <reified T> referToReifiedGeneric(x: Any?) {
class Generic<T> {
fun referToCaptured(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is T)
}<!>
contract {
<!ERROR_IN_CONTRACT_DESCRIPTION!>returns() implies (x is T)<!>
}
}
}
@@ -50,4 +50,4 @@ fun referToAliasedSimpleType(x: Any?) {
<!WRONG_IMPLIES_CONDITION!>contract {
returns() implies (x is SimpleType)
}<!>
}
}
@@ -0,0 +1,83 @@
// ISSUE: KT-57911
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
abstract class Base<T> {
@OptIn(ExperimentalContracts::class)
fun checkNotNull(s: String?) {
contract { returns() implies (s != null) }
s!!
}
@OptIn(ExperimentalContracts::class)
fun checkIsT(s: Any?): Boolean {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(true) implies (s is T)<!> }
return false
}
@OptIn(ExperimentalContracts::class)
fun <R> checkIsOwnerR(s: Any?): Boolean {
contract { <!ERROR_IN_CONTRACT_DESCRIPTION!>returns(true) implies (s is R)<!> }
return false
}
@OptIn(ExperimentalContracts::class)
inline fun <reified R> checkIsReifiedR(s: Any?): Boolean {
contract { returns(true) implies (s is R) }
return false
}
open fun foo(s: String?) {
checkNotNull(s)
s.length
}
}
class Derived: Base<String>() {
override fun foo(s: String?) {
checkNotNull(s)
s<!UNSAFE_CALL!>.<!>length
}
fun test_1(s: Any) {
if (checkIsT(s)) {
s.<!UNRESOLVED_REFERENCE!>length<!>
}
}
fun test_2(s: Any) {
if (checkIsOwnerR<String>(s)) {
s.<!UNRESOLVED_REFERENCE!>length<!>
}
}
fun test_3(s: Any) {
if (checkIsReifiedR<String>(s)) {
s.<!UNRESOLVED_REFERENCE!>length<!>
}
}
}
fun test_1(d: Derived, s: String?) {
d.checkNotNull(s)
s<!UNSAFE_CALL!>.<!>length
}
fun test_2(d: Derived, s: Any?) {
if (d.checkIsT(s)) {
s.<!UNRESOLVED_REFERENCE!>length<!>
}
}
fun test_3(d: Derived, s: Any?) {
if (d.checkIsOwnerR<String>(s)) {
s.<!UNRESOLVED_REFERENCE!>length<!>
}
}
fun test_4(d: Derived, s: Any?) {
if (d.checkIsReifiedR<String>(s)) {
s.<!UNRESOLVED_REFERENCE!>length<!>
}
}
@@ -0,0 +1,83 @@
// ISSUE: KT-57911
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
abstract class Base<T> {
@OptIn(ExperimentalContracts::class)
fun checkNotNull(s: String?) {
contract { returns() implies (s != null) }
s!!
}
@OptIn(ExperimentalContracts::class)
fun checkIsT(s: Any?): Boolean {
contract { returns(true) implies (s is <!CANNOT_CHECK_FOR_ERASED, ERROR_IN_CONTRACT_DESCRIPTION!>T<!>) }
return false
}
@OptIn(ExperimentalContracts::class)
fun <R> checkIsOwnerR(s: Any?): Boolean {
contract { returns(true) implies (s is <!CANNOT_CHECK_FOR_ERASED, ERROR_IN_CONTRACT_DESCRIPTION!>R<!>) }
return false
}
@OptIn(ExperimentalContracts::class)
inline fun <reified R> checkIsReifiedR(s: Any?): Boolean {
contract { returns(true) implies (s is R) }
return false
}
open fun foo(s: String?) {
checkNotNull(s)
<!DEBUG_INFO_SMARTCAST!>s<!>.length
}
}
class Derived: Base<String>() {
override fun foo(s: String?) {
checkNotNull(s)
<!DEBUG_INFO_SMARTCAST!>s<!>.length
}
fun test_1(s: Any) {
if (checkIsT(s)) {
s.<!UNRESOLVED_REFERENCE!>length<!>
}
}
fun test_2(s: Any) {
if (checkIsOwnerR<String>(s)) {
s.<!UNRESOLVED_REFERENCE!>length<!>
}
}
fun test_3(s: Any) {
if (checkIsReifiedR<String>(s)) {
s.<!UNRESOLVED_REFERENCE!>length<!>
}
}
}
fun test_1(d: Derived, s: String?) {
d.checkNotNull(s)
<!DEBUG_INFO_SMARTCAST!>s<!>.length
}
fun test_2(d: Derived, s: Any?) {
if (d.checkIsT(s)) {
s.<!UNRESOLVED_REFERENCE!>length<!>
}
}
fun test_3(d: Derived, s: Any?) {
if (d.checkIsOwnerR<String>(s)) {
s.<!UNRESOLVED_REFERENCE!>length<!>
}
}
fun test_4(d: Derived, s: Any?) {
if (d.checkIsReifiedR<String>(s)) {
s.<!UNRESOLVED_REFERENCE!>length<!>
}
}