import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract sealed interface KeyType { object Integrated : KeyType } sealed interface NonceTrait { class Required : NonceTrait object Without : NonceTrait } sealed interface AuthCapability { sealed class Authenticated : AuthCapability object Unauthenticated : AuthCapability } sealed interface Algorithm, out I : NonceTrait, out K : KeyType> { sealed interface Unauthenticated : Algorithm sealed interface Authenticated, out I : NonceTrait, out K : KeyType> : Algorithm sealed interface RequiringNonce, K : KeyType> : Algorithm sealed interface WithoutNonce, K : KeyType> : Algorithm } @OptIn(ExperimentalContracts::class) fun Algorithm<*, I, K>.isAuthenticated(): Boolean { contract { returns(true) implies (this@isAuthenticated is Algorithm.Authenticated<*, I, K>) returns(false) implies (this@isAuthenticated is Algorithm.Unauthenticated) } TODO() } @OptIn(ExperimentalContracts::class) fun , K : KeyType> Algorithm.requiresNonce(): Boolean { contract { returns(true) implies (this@requiresNonce is Algorithm.RequiringNonce) returns(false) implies (this@requiresNonce is Algorithm.WithoutNonce) } TODO() } fun, K: KeyType> Algorithm.foo() {} fun test_1_1(algorithm: Algorithm, NonceTrait, KeyType>) { algorithm.foo() // wrong receiver } fun test_1_2(algorithm: Algorithm, NonceTrait, KeyType>) { if (!algorithm.requiresNonce()) { algorithm.foo() // wrong receiver } } fun test_1_3(algorithm: Algorithm, NonceTrait, KeyType>) { if (algorithm.isAuthenticated()) { algorithm.foo() // wrong receiver } } fun test_1_4(algorithm: Algorithm, NonceTrait, KeyType>) { if (!algorithm.requiresNonce() && algorithm.isAuthenticated()) { algorithm.foo() // OK, but should be wrong receiver } } fun test_2_1(algorithm: Algorithm.WithoutNonce, KeyType>) { algorithm.foo() // wrong receiver } fun test_2_2(algorithm: Algorithm.Authenticated<*, NonceTrait.Without, KeyType>) { algorithm.foo() // wrong receiver }