Fix substitution of generic types in contracts
#KT-41078 Fixed
This commit is contained in:
committed by
TeamCityServer
parent
91fedd6a12
commit
b530427bb2
+6
@@ -32506,6 +32506,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/compositions.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("contractWithDeepGenerics.kt")
|
||||
public void testContractWithDeepGenerics() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/contractWithDeepGenerics.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("contractsOnMembers.kt")
|
||||
public void testContractsOnMembers() throws Exception {
|
||||
|
||||
@@ -33,6 +33,8 @@ import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.EmptySubstitutor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutorByConstructorMap
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
@@ -43,6 +45,8 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeConstructor
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
/**
|
||||
@@ -121,7 +125,7 @@ class EffectsExtractingVisitor(
|
||||
val arg = extractOrGetCached(expression.leftHandSide)
|
||||
return CallComputation(
|
||||
ESBooleanType,
|
||||
IsFunctor(rightType, expression.isNegated).invokeWithArguments(listOf(arg), emptyMap(), reducer)
|
||||
IsFunctor(rightType, expression.isNegated).invokeWithArguments(listOf(arg), ESTypeSubstitution.empty(builtIns), reducer)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -146,10 +150,22 @@ class EffectsExtractingVisitor(
|
||||
val args = listOf(left, right)
|
||||
|
||||
return when (expression.operationToken) {
|
||||
KtTokens.EXCLEQ -> CallComputation(ESBooleanType, EqualsFunctor(true).invokeWithArguments(args, emptyMap(), reducer))
|
||||
KtTokens.EQEQ -> CallComputation(ESBooleanType, EqualsFunctor(false).invokeWithArguments(args, emptyMap(), reducer))
|
||||
KtTokens.ANDAND -> CallComputation(ESBooleanType, AndFunctor().invokeWithArguments(args, emptyMap(), reducer))
|
||||
KtTokens.OROR -> CallComputation(ESBooleanType, OrFunctor().invokeWithArguments(args, emptyMap(), reducer))
|
||||
KtTokens.EXCLEQ -> CallComputation(
|
||||
ESBooleanType,
|
||||
EqualsFunctor(true).invokeWithArguments(args, ESTypeSubstitution.empty(builtIns), reducer)
|
||||
)
|
||||
KtTokens.EQEQ -> CallComputation(
|
||||
ESBooleanType,
|
||||
EqualsFunctor(false).invokeWithArguments(args, ESTypeSubstitution.empty(builtIns), reducer)
|
||||
)
|
||||
KtTokens.ANDAND -> CallComputation(
|
||||
ESBooleanType,
|
||||
AndFunctor().invokeWithArguments(args, ESTypeSubstitution.empty(builtIns), reducer)
|
||||
)
|
||||
KtTokens.OROR -> CallComputation(
|
||||
ESBooleanType,
|
||||
OrFunctor().invokeWithArguments(args, ESTypeSubstitution.empty(builtIns), reducer)
|
||||
)
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
}
|
||||
@@ -214,11 +230,16 @@ class EffectsExtractingVisitor(
|
||||
}
|
||||
|
||||
private fun ResolvedCall<*>.getTypeSubstitution(): ESTypeSubstitution {
|
||||
val result = mutableMapOf<ESKotlinType, ESKotlinType>()
|
||||
val substitution = mutableMapOf<TypeConstructor, UnwrappedType>()
|
||||
for ((typeParameter, typeArgument) in typeArguments) {
|
||||
result[ESKotlinType(typeParameter.defaultType)] = ESKotlinType(typeArgument)
|
||||
substitution[typeParameter.typeConstructor] = typeArgument.unwrap()
|
||||
}
|
||||
return result
|
||||
val substitutor = if (substitution.isNotEmpty()) {
|
||||
NewTypeSubstitutorByConstructorMap(substitution)
|
||||
} else {
|
||||
EmptySubstitutor
|
||||
}
|
||||
return ESTypeSubstitution(substitutor, builtIns)
|
||||
}
|
||||
|
||||
private fun ResolvedValueArgument.toComputation(): Computation? {
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.contracts.model
|
||||
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESKotlinType
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.EmptySubstitutor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
|
||||
|
||||
/**
|
||||
* An abstraction of effect-generating nature of some computation.
|
||||
@@ -43,4 +45,13 @@ abstract class AbstractFunctor : Functor {
|
||||
): List<ESEffect>
|
||||
}
|
||||
|
||||
typealias ESTypeSubstitution = Map<ESKotlinType, ESKotlinType>
|
||||
class ESTypeSubstitution(
|
||||
val substitutor: NewTypeSubstitutor,
|
||||
val builtIns: KotlinBuiltIns
|
||||
) {
|
||||
companion object {
|
||||
fun empty(builtIns: KotlinBuiltIns): ESTypeSubstitution {
|
||||
return ESTypeSubstitution(EmptySubstitutor, builtIns)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
package org.jetbrains.kotlin.contracts.model.functors
|
||||
|
||||
import org.jetbrains.kotlin.contracts.model.*
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESConstants
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESIs
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESReturns
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESType
|
||||
import org.jetbrains.kotlin.contracts.model.structure.*
|
||||
import org.jetbrains.kotlin.contracts.model.visitors.Reducer
|
||||
|
||||
class IsFunctor(val type: ESType, val isNegated: Boolean) : AbstractFunctor() {
|
||||
@@ -37,7 +34,9 @@ class IsFunctor(val type: ESType, val isNegated: Boolean) : AbstractFunctor() {
|
||||
}
|
||||
|
||||
private fun invokeWithValue(value: ESValue, typeSubstitution: ESTypeSubstitution): List<ConditionalEffect> {
|
||||
val substitutedType = typeSubstitution[type] ?: type
|
||||
val substitutedKotlinType = typeSubstitution.substitutor.safeSubstitute(type.toKotlinType(typeSubstitution.builtIns).unwrap())
|
||||
|
||||
val substitutedType = ESKotlinType(substitutedKotlinType)
|
||||
|
||||
val trueIs = ESIs(value, IsFunctor(substitutedType, isNegated))
|
||||
val falseIs = ESIs(value, IsFunctor(substitutedType, isNegated.not()))
|
||||
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts
|
||||
// ISSUE: KT-43260
|
||||
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
sealed class Either<L : Any, R : Any> {
|
||||
data class Left<L : Any, R : Any>(val leftValue: L) : Either<L, R>()
|
||||
data class Right<L : Any, R : Any>(val rightValue: R) : Either<L, R>()
|
||||
}
|
||||
|
||||
inline fun <reified L : Any, reified R : Any> Either<L, R>.isLeft(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isLeft is Either.Left<L, R>)
|
||||
}
|
||||
return this is Either.Left<L, R>
|
||||
}
|
||||
|
||||
inline fun <reified L : Any, reified R : Any> Either<L, R>.isRight(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isRight is Either.Right<L, R>)
|
||||
}
|
||||
return this is Either.Right<L, R>
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val result: Either<Exception, Unit> = Either.Left(RuntimeException("simulating missing code"))
|
||||
if (result.isLeft()) {
|
||||
val cause = result.leftValue.cause
|
||||
}
|
||||
}
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts
|
||||
// ISSUE: KT-43260
|
||||
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
sealed class Either<L : Any, R : Any> {
|
||||
data class Left<L : Any, R : Any>(val leftValue: L) : Either<L, R>()
|
||||
data class Right<L : Any, R : Any>(val rightValue: R) : Either<L, R>()
|
||||
}
|
||||
|
||||
inline fun <reified L : Any, reified R : Any> Either<L, R>.isLeft(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isLeft is Either.Left<L, R>)
|
||||
}
|
||||
return this is Either.Left<L, R>
|
||||
}
|
||||
|
||||
inline fun <reified L : Any, reified R : Any> Either<L, R>.isRight(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isRight is Either.Right<L, R>)
|
||||
}
|
||||
return this is Either.Right<L, R>
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val result: Either<Exception, Unit> = Either.Left(RuntimeException("simulating missing code"))
|
||||
if (result.isLeft()) {
|
||||
val cause = <!DEBUG_INFO_SMARTCAST!>result<!>.leftValue.cause
|
||||
}
|
||||
}
|
||||
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
package
|
||||
|
||||
public fun test(): kotlin.Unit
|
||||
public inline fun </*0*/ reified L : kotlin.Any, /*1*/ reified R : kotlin.Any> Either<L, R>.isLeft(): kotlin.Boolean
|
||||
Returns(TRUE) -> <this> is Left<L, R>
|
||||
|
||||
public inline fun </*0*/ reified L : kotlin.Any, /*1*/ reified R : kotlin.Any> Either<L, R>.isRight(): kotlin.Boolean
|
||||
Returns(TRUE) -> <this> is Right<L, R>
|
||||
|
||||
public sealed class Either</*0*/ L : kotlin.Any, /*1*/ R : kotlin.Any> {
|
||||
protected constructor Either</*0*/ L : kotlin.Any, /*1*/ R : kotlin.Any>()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public final data class Left</*0*/ L : kotlin.Any, /*1*/ R : kotlin.Any> : Either<L, R> {
|
||||
public constructor Left</*0*/ L : kotlin.Any, /*1*/ R : kotlin.Any>(/*0*/ leftValue: L)
|
||||
public final val leftValue: L
|
||||
public final operator /*synthesized*/ fun component1(): L
|
||||
public final /*synthesized*/ fun copy(/*0*/ leftValue: L = ...): Either.Left<L, R>
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final data class Right</*0*/ L : kotlin.Any, /*1*/ R : kotlin.Any> : Either<L, R> {
|
||||
public constructor Right</*0*/ L : kotlin.Any, /*1*/ R : kotlin.Any>(/*0*/ rightValue: R)
|
||||
public final val rightValue: R
|
||||
public final operator /*synthesized*/ fun component1(): R
|
||||
public final /*synthesized*/ fun copy(/*0*/ rightValue: R = ...): Either.Right<L, R>
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
Generated
+6
@@ -32602,6 +32602,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/compositions.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("contractWithDeepGenerics.kt")
|
||||
public void testContractWithDeepGenerics() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/contractWithDeepGenerics.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("contractsOnMembers.kt")
|
||||
public void testContractsOnMembers() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user