[cls] write contracts information to cls stubs

^ KTIJ-24665
this information would be used to create resolved FirElements from stubs,
so no ProtoBuf would be kept in memory
This commit is contained in:
Anna Kozlova
2023-04-05 17:12:30 +02:00
committed by Space Team
parent 9d4db72d72
commit 4fe239375f
44 changed files with 1182 additions and 534 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.KtRealSourceElementKind
import org.jetbrains.kotlin.contracts.description.*
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
@@ -19,6 +20,7 @@ import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.types.ConeKotlinType
object FirContractChecker : FirFunctionChecker() {
private val EMPTY_CONTRACT_MESSAGE = "Empty contract block is not allowed"
@@ -66,7 +68,7 @@ object FirContractChecker : FirFunctionChecker() {
else if (declaration.symbol.callableId.isLocal || declaration.visibility == Visibilities.Local) contractNotAllowed("Contracts are not allowed for local functions")
}
private object DiagnosticExtractor : ConeContractDescriptionVisitor<ConeDiagnostic?, Nothing?>() {
private object DiagnosticExtractor : KtContractDescriptionVisitor<ConeDiagnostic?, Nothing?, ConeKotlinType, ConeDiagnostic>() {
override fun visitContractDescriptionElement(
contractDescriptionElement: ConeContractDescriptionElement,
data: Nothing?
@@ -90,7 +92,7 @@ object FirContractChecker : FirFunctionChecker() {
}
override fun visitErroneousCallsEffectDeclaration(
callsEffect: ConeErroneousCallsEffectDeclaration,
callsEffect: KtErroneousCallsEffectDeclaration<ConeKotlinType, ConeDiagnostic>,
data: Nothing?
): ConeDiagnostic {
return callsEffect.diagnostic
@@ -112,7 +114,7 @@ object FirContractChecker : FirFunctionChecker() {
}
override fun visitErroneousIsInstancePredicate(
isInstancePredicate: ConeErroneousIsInstancePredicate,
isInstancePredicate: KtErroneousIsInstancePredicate<ConeKotlinType, ConeDiagnostic>,
data: Nothing?
): ConeDiagnostic {
return isInstancePredicate.diagnostic
@@ -123,20 +125,20 @@ object FirContractChecker : FirFunctionChecker() {
}
override fun visitErroneousConstantReference(
erroneousConstantReference: ConeErroneousConstantReference,
erroneousConstantReference: KtErroneousConstantReference<ConeKotlinType, ConeDiagnostic>,
data: Nothing?
): ConeDiagnostic {
return erroneousConstantReference.diagnostic
}
override fun visitErroneousValueParameterReference(
valueParameterReference: ConeErroneousValueParameterReference,
valueParameterReference: KtErroneousValueParameterReference<ConeKotlinType, ConeDiagnostic>,
data: Nothing?
): ConeDiagnostic {
return valueParameterReference.diagnostic
}
override fun visitErroneousElement(element: ConeErroneousContractElement, data: Nothing?): ConeDiagnostic {
override fun visitErroneousElement(element: KtErroneousContractElement<ConeKotlinType, ConeDiagnostic>, data: Nothing?): ConeDiagnostic {
return element.diagnostic
}
}
@@ -5,22 +5,24 @@
package org.jetbrains.kotlin.fir.deserialization
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.contracts.description.KtBooleanValueParameterReference
import org.jetbrains.kotlin.contracts.description.KtConstantReference
import org.jetbrains.kotlin.contracts.description.KtValueParameterReference
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.builder.buildResolvedContractDescription
import org.jetbrains.kotlin.fir.contracts.description.*
import org.jetbrains.kotlin.fir.contracts.toFirElement
import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.expressions.LogicOperationKind
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.isBoolean
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.Flags
import org.jetbrains.kotlin.metadata.deserialization.isInstanceType
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.serialization.deserialization.ProtoBufContractDeserializer
class FirContractDeserializer(private val c: FirDeserializationContext) {
class FirContractDeserializer(private val c: FirDeserializationContext) :
ProtoBufContractDeserializer<ConeKotlinType, ConeDiagnostic, FirContractDescriptionOwner>() {
fun loadContract(proto: ProtoBuf.Contract, owner: FirContractDescriptionOwner): FirContractDescription? {
val effects = proto.effectList.map { loadPossiblyConditionalEffect(it, owner) ?: return null }
return buildResolvedContractDescription {
@@ -28,105 +30,16 @@ class FirContractDeserializer(private val c: FirDeserializationContext) {
}
}
private fun loadPossiblyConditionalEffect(
proto: ProtoBuf.Effect,
override fun extractType(proto: ProtoBuf.Expression): ConeKotlinType? {
return c.typeDeserializer.type(proto.isInstanceType(c.typeTable) ?: return null)
}
override fun extractVariable(
valueParameterIndex: Int,
owner: FirContractDescriptionOwner
): ConeEffectDeclaration? {
if (proto.hasConclusionOfConditionalEffect()) {
val conclusion = loadExpression(proto.conclusionOfConditionalEffect, owner) ?: return null
val effect = loadSimpleEffect(proto, owner) ?: return null
return ConeConditionalEffectDeclaration(effect, conclusion)
}
return loadSimpleEffect(proto, owner)
}
private fun loadSimpleEffect(proto: ProtoBuf.Effect, owner: FirContractDescriptionOwner): ConeEffectDeclaration? {
val type: ProtoBuf.Effect.EffectType = if (proto.hasEffectType()) proto.effectType else return null
return when(type) {
ProtoBuf.Effect.EffectType.RETURNS_CONSTANT -> {
val argument = proto.effectConstructorArgumentList.firstOrNull()
val returnValue = if (argument == null) {
ConeConstantReference.WILDCARD
} else {
loadExpression(argument, owner) as? ConeConstantReference ?: return null
}
ConeReturnsEffectDeclaration(returnValue)
}
ProtoBuf.Effect.EffectType.RETURNS_NOT_NULL -> {
ConeReturnsEffectDeclaration(ConeConstantReference.NOT_NULL)
}
ProtoBuf.Effect.EffectType.CALLS -> {
val argument = proto.effectConstructorArgumentList.firstOrNull() ?: return null
val callable = extractVariable(argument, owner) ?: return null
val invocationKind = if (proto.hasKind())
proto.kind.toDescriptorInvocationKind() ?: return null
else
EventOccurrencesRange.UNKNOWN
ConeCallsEffectDeclaration(callable, invocationKind)
}
}
}
private fun loadExpression(proto: ProtoBuf.Expression, owner: FirContractDescriptionOwner): ConeBooleanExpression? {
val primitiveType = getPrimitiveType(proto)
val primitiveExpression = extractPrimitiveExpression(proto, primitiveType, owner)
val complexType = getComplexType(proto)
val childs: MutableList<ConeBooleanExpression> = mutableListOf()
childs.addIfNotNull(primitiveExpression)
return when (complexType) {
ComplexExpressionType.AND_SEQUENCE -> {
proto.andArgumentList.mapTo(childs) { loadExpression(it, owner) ?: return null }
childs.reduce { acc, booleanExpression -> ConeBinaryLogicExpression(acc, booleanExpression, LogicOperationKind.AND) }
}
ComplexExpressionType.OR_SEQUENCE -> {
proto.orArgumentList.mapTo(childs) { loadExpression(it, owner) ?: return null }
childs.reduce { acc, booleanExpression -> ConeBinaryLogicExpression(acc, booleanExpression, LogicOperationKind.OR) }
}
null -> primitiveExpression
}
}
private fun extractPrimitiveExpression(proto: ProtoBuf.Expression, primitiveType: PrimitiveExpressionType?, owner: FirContractDescriptionOwner): ConeBooleanExpression? {
val isInverted = Flags.IS_NEGATED.get(proto.flags)
return when (primitiveType) {
PrimitiveExpressionType.VALUE_PARAMETER_REFERENCE, PrimitiveExpressionType.RECEIVER_REFERENCE -> {
(extractVariable(proto, owner) as? ConeBooleanValueParameterReference?)?.invertIfNecessary(isInverted)
}
PrimitiveExpressionType.CONSTANT ->
(loadConstant(proto.constantValue) as? ConeBooleanConstantReference)?.invertIfNecessary(isInverted)
PrimitiveExpressionType.INSTANCE_CHECK -> {
val variable = extractVariable(proto, owner) ?: return null
val type = extractType(proto) ?: return null
ConeIsInstancePredicate(variable, type, isInverted)
}
PrimitiveExpressionType.NULLABILITY_CHECK -> {
val variable = extractVariable(proto, owner) ?: return null
ConeIsNullPredicate(variable, isInverted)
}
null -> null
}
}
private fun ConeBooleanExpression.invertIfNecessary(shouldInvert: Boolean): ConeBooleanExpression =
if (shouldInvert) ConeLogicalNot(this) else this
private fun extractVariable(proto: ProtoBuf.Expression, owner: FirContractDescriptionOwner): ConeValueParameterReference? {
if (!proto.hasValueParameterReference()) return null
val ownerFunction = owner as FirSimpleFunction
val valueParameterIndex = proto.valueParameterReference - 1
): KtValueParameterReference<ConeKotlinType, ConeDiagnostic>? {
val name: String
val ownerFunction = owner as FirSimpleFunction
val typeRef = if (valueParameterIndex < 0) {
name = "this"
ownerFunction.receiverParameter?.typeRef
@@ -137,90 +50,24 @@ class FirContractDeserializer(private val c: FirDeserializationContext) {
} ?: return null
return if (!typeRef.isBoolean)
ConeValueParameterReference(valueParameterIndex, name)
KtValueParameterReference(valueParameterIndex, name)
else
ConeBooleanValueParameterReference(valueParameterIndex, name)
KtBooleanValueParameterReference(valueParameterIndex, name)
}
private fun ProtoBuf.Effect.InvocationKind.toDescriptorInvocationKind(): EventOccurrencesRange? = when (this) {
ProtoBuf.Effect.InvocationKind.AT_MOST_ONCE -> EventOccurrencesRange.AT_MOST_ONCE
ProtoBuf.Effect.InvocationKind.EXACTLY_ONCE -> EventOccurrencesRange.EXACTLY_ONCE
ProtoBuf.Effect.InvocationKind.AT_LEAST_ONCE -> EventOccurrencesRange.AT_LEAST_ONCE
}
private fun extractType(proto: ProtoBuf.Expression): ConeKotlinType? {
return c.typeDeserializer.type(proto.isInstanceType(c.typeTable) ?: return null)
}
private fun loadConstant(value: ProtoBuf.Expression.ConstantValue): ConeConstantReference? = when (value) {
ProtoBuf.Expression.ConstantValue.TRUE -> ConeBooleanConstantReference.TRUE
ProtoBuf.Expression.ConstantValue.FALSE -> ConeBooleanConstantReference.FALSE
ProtoBuf.Expression.ConstantValue.NULL -> ConeConstantReference.NULL
}
private fun getComplexType(proto: ProtoBuf.Expression): ComplexExpressionType? {
val isOrSequence = proto.orArgumentCount != 0
val isAndSequence = proto.andArgumentCount != 0
return when {
isOrSequence && isAndSequence -> null
isOrSequence -> ComplexExpressionType.OR_SEQUENCE
isAndSequence -> ComplexExpressionType.AND_SEQUENCE
else -> null
override fun loadConstant(value: ProtoBuf.Expression.ConstantValue): KtConstantReference<ConeKotlinType, ConeDiagnostic> {
return when (value) {
ProtoBuf.Expression.ConstantValue.TRUE -> ConeContractConstantValues.TRUE
ProtoBuf.Expression.ConstantValue.FALSE -> ConeContractConstantValues.FALSE
ProtoBuf.Expression.ConstantValue.NULL -> ConeContractConstantValues.NULL
}
}
private fun getPrimitiveType(proto: ProtoBuf.Expression): PrimitiveExpressionType? {
// Expected to be one element, but can be empty (unknown expression) or contain several elements (invalid data)
val expressionTypes: MutableList<PrimitiveExpressionType> = mutableListOf()
// Check for predicates
when {
proto.hasValueParameterReference() && proto.hasType() ->
expressionTypes.add(PrimitiveExpressionType.INSTANCE_CHECK)
proto.hasValueParameterReference() && Flags.IS_NULL_CHECK_PREDICATE.get(proto.flags) ->
expressionTypes.add(PrimitiveExpressionType.NULLABILITY_CHECK)
}
// If message contains correct predicate, then predicate's type overrides type of value,
// even is message has one
if (expressionTypes.isNotEmpty()) {
return expressionTypes.singleOrNull()
}
// Otherwise, check if it is a value
when {
proto.hasValueParameterReference() && proto.valueParameterReference > 0 ->
expressionTypes.add(PrimitiveExpressionType.VALUE_PARAMETER_REFERENCE)
proto.hasValueParameterReference() && proto.valueParameterReference == 0 ->
expressionTypes.add(PrimitiveExpressionType.RECEIVER_REFERENCE)
proto.hasConstantValue() -> expressionTypes.add(PrimitiveExpressionType.CONSTANT)
}
return expressionTypes.singleOrNull()
override fun getNotNull(): KtConstantReference<ConeKotlinType, ConeDiagnostic> {
return ConeContractConstantValues.NOT_NULL
}
private fun ProtoBuf.Expression.hasType(): Boolean = this.hasIsInstanceType() || this.hasIsInstanceTypeId()
// Arguments of expressions with such types are never other expressions
private enum class PrimitiveExpressionType {
VALUE_PARAMETER_REFERENCE,
RECEIVER_REFERENCE,
CONSTANT,
INSTANCE_CHECK,
NULLABILITY_CHECK
}
// Expressions with such type can take other expressions as arguments.
// Additionally, for performance reasons, "complex expression" and "primitive expression"
// can co-exist in the one and the same message. If "primitive expression" is present
// in the current message, it is treated as the first argument of "complex expression".
private enum class ComplexExpressionType {
AND_SEQUENCE,
OR_SEQUENCE
override fun getWildcard(): KtConstantReference<ConeKotlinType, ConeDiagnostic> {
return ConeContractConstantValues.WILDCARD
}
}
@@ -6,12 +6,15 @@
package org.jetbrains.kotlin.fir.serialization
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.contracts.description.KtContractDescriptionVisitor
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
import org.jetbrains.kotlin.fir.contracts.description.*
import org.jetbrains.kotlin.fir.contracts.effects
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.expressions.LogicOperationKind
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.Flags
@@ -57,9 +60,9 @@ class FirContractSerializer {
is ConeReturnsEffectDeclaration -> {
when (effectDeclaration.value) {
ConeConstantReference.NOT_NULL ->
ConeContractConstantValues.NOT_NULL ->
builder.effectType = ProtoBuf.Effect.EffectType.RETURNS_NOT_NULL
ConeConstantReference.WILDCARD ->
ConeContractConstantValues.WILDCARD ->
builder.effectType = ProtoBuf.Effect.EffectType.RETURNS_CONSTANT
else -> {
builder.effectType = ProtoBuf.Effect.EffectType.RETURNS_CONSTANT
@@ -89,7 +92,7 @@ class FirContractSerializer {
contractDescriptionElement: ConeContractDescriptionElement,
contractDescription: FirContractDescription
): ProtoBuf.Expression.Builder {
return contractDescriptionElement.accept(object : ConeContractDescriptionVisitor<ProtoBuf.Expression.Builder, Unit>() {
return contractDescriptionElement.accept(object : KtContractDescriptionVisitor<ProtoBuf.Expression.Builder, Unit, ConeKotlinType, ConeDiagnostic>() {
override fun visitLogicalBinaryOperationContractExpression(
binaryLogicExpression: ConeBinaryLogicExpression,
data: Unit
@@ -201,14 +204,14 @@ class FirContractSerializer {
private fun constantValueProtobufEnum(constantReference: ConeConstantReference): ProtoBuf.Expression.ConstantValue? =
when (constantReference) {
ConeBooleanConstantReference.TRUE -> ProtoBuf.Expression.ConstantValue.TRUE
ConeBooleanConstantReference.FALSE -> ProtoBuf.Expression.ConstantValue.FALSE
ConeConstantReference.NULL -> ProtoBuf.Expression.ConstantValue.NULL
ConeConstantReference.NOT_NULL -> throw IllegalStateException(
ConeContractConstantValues.TRUE -> ProtoBuf.Expression.ConstantValue.TRUE
ConeContractConstantValues.FALSE -> ProtoBuf.Expression.ConstantValue.FALSE
ConeContractConstantValues.NULL -> ProtoBuf.Expression.ConstantValue.NULL
ConeContractConstantValues.NOT_NULL -> throw IllegalStateException(
"Internal error during serialization of function contract: NOT_NULL constant isn't denotable in protobuf format. " +
"Its serialization should be handled at higher level"
)
ConeConstantReference.WILDCARD -> null
ConeContractConstantValues.WILDCARD -> null
else -> throw IllegalArgumentException("Unknown constant: $constantReference")
}
}
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.KtPsiSourceElement
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.builder
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
import org.jetbrains.kotlin.contracts.description.canBeRevisited
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.*
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
import org.jetbrains.kotlin.contracts.description.*
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.hasExplicitBackingField
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.transformers
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
@@ -12,7 +13,6 @@ import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.LogicOperationKind.OR
import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
@@ -182,7 +182,7 @@ private sealed class WhenExhaustivenessChecker {
}
override fun visitBinaryLogicExpression(binaryLogicExpression: FirBinaryLogicExpression, data: D) {
if (binaryLogicExpression.kind == OR) {
if (binaryLogicExpression.kind == LogicOperationKind.OR) {
binaryLogicExpression.acceptChildren(this, data)
}
}
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.fir.resolve.transformers.contracts
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.contracts.description.*
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.contracts.description.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeContractDescriptionError
@@ -37,8 +39,8 @@ class ConeEffectExtractor(
private val BOOLEAN_NOT = FirContractsDslNames.id("kotlin", "Boolean", "not")
}
private fun ConeContractDescriptionError.asElement(): ConeErroneousContractElement {
return ConeErroneousContractElement(this)
private fun ConeContractDescriptionError.asElement(): KtErroneousContractElement<ConeKotlinType, ConeDiagnostic> {
return KtErroneousContractElement(this)
}
override fun visitElement(element: FirElement, data: Nothing?): ConeContractDescriptionElement {
@@ -63,18 +65,19 @@ class ConeEffectExtractor(
FirContractsDslNames.RETURNS -> {
val argument = functionCall.arguments.firstOrNull()
val value = if (argument == null) {
ConeConstantReference.WILDCARD
ConeContractConstantValues.WILDCARD
} else {
when (val value = argument.asContractElement()) {
is ConeConstantReference -> value
else -> ConeErroneousConstantReference(ConeContractDescriptionError.NotAConstant(value))
else -> KtErroneousConstantReference(ConeContractDescriptionError.NotAConstant(value))
}
}
ConeReturnsEffectDeclaration(value)
@Suppress("UNCHECKED_CAST")
KtReturnsEffectDeclaration(value as ConeConstantReference)
}
FirContractsDslNames.RETURNS_NOT_NULL -> {
ConeReturnsEffectDeclaration(ConeConstantReference.NOT_NULL)
ConeReturnsEffectDeclaration(ConeContractConstantValues.NOT_NULL)
}
FirContractsDslNames.CALLS_IN_PLACE -> {
@@ -82,7 +85,7 @@ class ConeEffectExtractor(
when (val argument = functionCall.arguments.getOrNull(1)) {
null -> ConeCallsEffectDeclaration(reference, EventOccurrencesRange.UNKNOWN)
else -> when (val kind = argument.parseInvocationKind()) {
null -> ConeErroneousCallsEffectDeclaration(reference, ConeContractDescriptionError.UnresolvedInvocationKind(argument))
null -> KtErroneousCallsEffectDeclaration(reference, ConeContractDescriptionError.UnresolvedInvocationKind(argument))
else -> ConeCallsEffectDeclaration(reference, kind)
}
}
@@ -146,10 +149,10 @@ class ConeEffectExtractor(
return ConeContractDescriptionError.UnresolvedCall(name).asElement()
}
val parameter = symbol.fir as? FirValueParameter
?: return ConeErroneousValueParameterReference(
?: return KtErroneousValueParameterReference(
ConeContractDescriptionError.IllegalParameter(symbol, "$symbol is not a value parameter")
)
val index = valueParameters.indexOf(parameter).takeUnless { it < 0 } ?: return ConeErroneousValueParameterReference(
val index = valueParameters.indexOf(parameter).takeUnless { it < 0 } ?: return KtErroneousValueParameterReference(
ConeContractDescriptionError.IllegalParameter(symbol, "Value paramter $symbol is not found in parameters of outer function")
)
val type = parameter.returnTypeRef.coneType
@@ -200,10 +203,10 @@ class ConeEffectExtractor(
override fun <T> visitConstExpression(constExpression: FirConstExpression<T>, data: Nothing?): ConeContractDescriptionElement {
return when (constExpression.kind) {
ConstantValueKind.Null -> ConeConstantReference.NULL
ConstantValueKind.Null -> ConeContractConstantValues.NULL
ConstantValueKind.Boolean -> when (constExpression.value as Boolean) {
true -> ConeBooleanConstantReference.TRUE
false -> ConeBooleanConstantReference.FALSE
true -> ConeContractConstantValues.TRUE
false -> ConeContractConstantValues.FALSE
}
else -> ConeContractDescriptionError.IllegalConst(constExpression, onlyNullAllowed = false).asElement()
}
@@ -224,7 +227,7 @@ class ConeEffectExtractor(
}
return when (diagnostic) {
null -> ConeIsInstancePredicate(arg, type, isNegated)
else -> ConeErroneousIsInstancePredicate(arg, type, isNegated, diagnostic)
else -> KtErroneousIsInstancePredicate(arg, type, isNegated, diagnostic)
}
}
@@ -240,11 +243,11 @@ class ConeEffectExtractor(
}
}
private fun noReceiver(callableId: CallableId): ConeErroneousContractElement {
private fun noReceiver(callableId: CallableId): KtErroneousContractElement<ConeKotlinType, ConeDiagnostic> {
return ConeContractDescriptionError.NoReceiver(callableId.callableName).asElement()
}
private fun noArgument(callableId: CallableId): ConeErroneousContractElement {
private fun noArgument(callableId: CallableId): KtErroneousContractElement<ConeKotlinType, ConeDiagnostic> {
return ConeContractDescriptionError.NoArgument(callableId.callableName).asElement()
}
@@ -262,7 +265,7 @@ class ConeEffectExtractor(
private fun FirExpression.asContractValueExpression(): ConeValueParameterReference {
return when (val element = asContractElement()) {
is ConeValueParameterReference -> element
else -> ConeErroneousValueParameterReference(ConeContractDescriptionError.NotAParameterReference(element))
else -> KtErroneousValueParameterReference(ConeContractDescriptionError.NotAParameterReference(element))
}
}
}
@@ -5,17 +5,20 @@
package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
import org.jetbrains.kotlin.fir.contracts.description.*
import org.jetbrains.kotlin.fir.expressions.LogicOperationKind
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.canBeNull
import org.jetbrains.kotlin.fir.types.isAny
import org.jetbrains.kotlin.fir.types.isMarkedNullable
import org.jetbrains.kotlin.fir.types.isNullableNothing
fun ConeConstantReference.toOperation(): Operation? = when (this) {
ConeConstantReference.WILDCARD -> null
ConeConstantReference.NULL -> Operation.EqNull
ConeConstantReference.NOT_NULL -> Operation.NotEqNull
ConeBooleanConstantReference.TRUE -> Operation.EqTrue
ConeBooleanConstantReference.FALSE -> Operation.EqFalse
ConeContractConstantValues.WILDCARD -> null
ConeContractConstantValues.NULL -> Operation.EqNull
ConeContractConstantValues.NOT_NULL -> Operation.NotEqNull
ConeContractConstantValues.TRUE -> Operation.EqTrue
ConeContractConstantValues.FALSE -> Operation.EqFalse
else -> throw IllegalArgumentException("$this can not be transformed to Operation")
}
@@ -31,7 +34,7 @@ fun LogicSystem.approveContractStatement(
fun ConeBooleanExpression.visit(inverted: Boolean): TypeStatements? = when (this) {
is ConeBooleanConstantReference ->
if (inverted == (this == ConeBooleanConstantReference.TRUE)) null else mapOf()
if (inverted == (this == ConeContractConstantValues.TRUE)) null else mapOf()
is ConeLogicalNot -> arg.visit(inverted = !inverted)
is ConeIsInstancePredicate ->
arguments.getOrNull(arg.parameterIndex + 1)?.let {
+1
View File
@@ -7,6 +7,7 @@ plugins {
dependencies {
api(project(":compiler:frontend.common"))
api(project(":core:compiler.common"))
api(project(":compiler:fir:cones"))
// Necessary only to store bound PsiElement inside FirElement
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.expressions
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.visitors.*
@@ -9,13 +9,13 @@ package org.jetbrains.kotlin.fir.expressions.builder
import kotlin.contracts.*
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
import org.jetbrains.kotlin.fir.builder.FirAnnotationContainerBuilder
import org.jetbrains.kotlin.fir.builder.FirBuilderDsl
import org.jetbrains.kotlin.fir.builder.toMutableOrEmpty
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirBinaryLogicExpression
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.LogicOperationKind
import org.jetbrains.kotlin.fir.expressions.builder.FirExpressionBuilder
import org.jetbrains.kotlin.fir.expressions.impl.FirBinaryLogicExpressionImpl
import org.jetbrains.kotlin.fir.types.FirTypeRef
@@ -8,10 +8,10 @@
package org.jetbrains.kotlin.fir.expressions.impl
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirBinaryLogicExpression
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.LogicOperationKind
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirImplicitTypeRefImplWithoutSource
import org.jetbrains.kotlin.fir.visitors.*
@@ -1,22 +0,0 @@
/*
* Copyright 2010-2019 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.contracts.description
interface ConeContractDescriptionElement {
fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R
val erroneous: Boolean
}
abstract class ConeEffectDeclaration : ConeContractDescriptionElement {
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitEffectDeclaration(this, data)
}
interface ConeBooleanExpression : ConeContractDescriptionElement {
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitBooleanExpression(this, data)
}
@@ -1,69 +0,0 @@
/*
* Copyright 2010-2019 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.contracts.description
abstract class ConeContractDescriptionVisitor<out R, in D> {
open fun visitContractDescriptionElement(contractDescriptionElement: ConeContractDescriptionElement, data: D): R {
throw IllegalStateException("Top of hierarchy reached, no overloads were found for element: $contractDescriptionElement")
}
// Effects
open fun visitEffectDeclaration(effectDeclaration: ConeEffectDeclaration, data: D): R = visitContractDescriptionElement(effectDeclaration, data)
open fun visitConditionalEffectDeclaration(conditionalEffect: ConeConditionalEffectDeclaration, data: D): R =
visitEffectDeclaration(conditionalEffect, data)
open fun visitReturnsEffectDeclaration(returnsEffect: ConeReturnsEffectDeclaration, data: D): R =
visitEffectDeclaration(returnsEffect, data)
open fun visitCallsEffectDeclaration(callsEffect: ConeCallsEffectDeclaration, data: D): R =
visitEffectDeclaration(callsEffect, data)
open fun visitErroneousCallsEffectDeclaration(callsEffect: ConeErroneousCallsEffectDeclaration, data: D): R =
visitCallsEffectDeclaration(callsEffect, data)
// Expressions
open fun visitBooleanExpression(booleanExpression: ConeBooleanExpression, data: D): R =
visitContractDescriptionElement(booleanExpression, data)
open fun visitLogicalBinaryOperationContractExpression(binaryLogicExpression: ConeBinaryLogicExpression, data: D): R =
visitBooleanExpression(binaryLogicExpression, data)
open fun visitLogicalNot(logicalNot: ConeLogicalNot, data: D): R = visitBooleanExpression(logicalNot, data)
open fun visitIsInstancePredicate(isInstancePredicate: ConeIsInstancePredicate, data: D): R =
visitBooleanExpression(isInstancePredicate, data)
open fun visitErroneousIsInstancePredicate(isInstancePredicate: ConeErroneousIsInstancePredicate, data: D): R =
visitIsInstancePredicate(isInstancePredicate, data)
open fun visitIsNullPredicate(isNullPredicate: ConeIsNullPredicate, data: D): R = visitBooleanExpression(isNullPredicate, data)
// Values
open fun visitValue(value: ConeContractDescriptionValue, data: D): R = visitContractDescriptionElement(value, data)
open fun visitConstantDescriptor(constantReference: ConeConstantReference, data: D): R = visitValue(constantReference, data)
open fun visitBooleanConstantDescriptor(booleanConstantDescriptor: ConeBooleanConstantReference, data: D): R =
visitConstantDescriptor(booleanConstantDescriptor, data)
open fun visitErroneousConstantReference(erroneousConstantReference: ConeErroneousConstantReference, data: D): R =
visitConstantDescriptor(erroneousConstantReference, data)
open fun visitValueParameterReference(valueParameterReference: ConeValueParameterReference, data: D): R =
visitValue(valueParameterReference, data)
open fun visitBooleanValueParameterReference(booleanValueParameterReference: ConeBooleanValueParameterReference, data: D): R =
visitValueParameterReference(booleanValueParameterReference, data)
open fun visitErroneousValueParameterReference(valueParameterReference: ConeErroneousValueParameterReference, data: D): R =
visitValueParameterReference(valueParameterReference, data)
// Error
open fun visitErroneousElement(element: ConeErroneousContractElement, data: D): R =
visitContractDescriptionElement(element, data)
}
@@ -5,14 +5,17 @@
package org.jetbrains.kotlin.fir.contracts.description
import org.jetbrains.kotlin.contracts.description.*
import org.jetbrains.kotlin.fir.contracts.*
import org.jetbrains.kotlin.fir.contracts.impl.FirEmptyContractDescription
import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.renderer.FirRendererComponents
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.renderForDebugging
class ConeContractRenderer : ConeContractDescriptionVisitor<Unit, Nothing?>() {
class ConeContractRenderer : KtContractDescriptionVisitor<Unit, Nothing?, ConeKotlinType, ConeDiagnostic>() {
internal lateinit var components: FirRendererComponents
private val printer get() = components.printer
@@ -74,54 +77,54 @@ class ConeContractRenderer : ConeContractDescriptionVisitor<Unit, Nothing?>() {
printer.println(">")
}
override fun visitConditionalEffectDeclaration(conditionalEffect: ConeConditionalEffectDeclaration, data: Nothing?) {
override fun visitConditionalEffectDeclaration(conditionalEffect: KtConditionalEffectDeclaration<ConeKotlinType, ConeDiagnostic>, data: Nothing?) {
conditionalEffect.effect.accept(this, data)
printer.print(" -> ")
conditionalEffect.condition.accept(this, data)
}
override fun visitReturnsEffectDeclaration(returnsEffect: ConeReturnsEffectDeclaration, data: Nothing?) {
override fun visitReturnsEffectDeclaration(returnsEffect: KtReturnsEffectDeclaration<ConeKotlinType, ConeDiagnostic>, data: Nothing?) {
printer.print("Returns(")
returnsEffect.value.accept(this, data)
printer.print(")")
}
override fun visitCallsEffectDeclaration(callsEffect: ConeCallsEffectDeclaration, data: Nothing?) {
override fun visitCallsEffectDeclaration(callsEffect: KtCallsEffectDeclaration<ConeKotlinType, ConeDiagnostic>, data: Nothing?) {
printer.print("CallsInPlace(")
callsEffect.valueParameterReference.accept(this, data)
printer.print(", ${callsEffect.kind})")
}
override fun visitLogicalBinaryOperationContractExpression(binaryLogicExpression: ConeBinaryLogicExpression, data: Nothing?) {
override fun visitLogicalBinaryOperationContractExpression(binaryLogicExpression: KtBinaryLogicExpression<ConeKotlinType, ConeDiagnostic>, data: Nothing?) {
inBracketsIfNecessary(binaryLogicExpression, binaryLogicExpression.left) { binaryLogicExpression.left.accept(this, data) }
printer.print(" ${binaryLogicExpression.kind.token} ")
inBracketsIfNecessary(binaryLogicExpression, binaryLogicExpression.right) { binaryLogicExpression.right.accept(this, data) }
}
override fun visitLogicalNot(logicalNot: ConeLogicalNot, data: Nothing?) {
override fun visitLogicalNot(logicalNot: KtLogicalNot<ConeKotlinType, ConeDiagnostic>, data: Nothing?) {
inBracketsIfNecessary(logicalNot, logicalNot.arg) { printer.print("!") }
logicalNot.arg.accept(this, data)
}
override fun visitIsInstancePredicate(isInstancePredicate: ConeIsInstancePredicate, data: Nothing?) {
override fun visitIsInstancePredicate(isInstancePredicate: KtIsInstancePredicate<ConeKotlinType, ConeDiagnostic>, data: Nothing?) {
isInstancePredicate.arg.accept(this, data)
printer.print(" ${if (isInstancePredicate.isNegated) "!" else ""}is ${isInstancePredicate.type.renderForDebugging()}")
}
override fun visitIsNullPredicate(isNullPredicate: ConeIsNullPredicate, data: Nothing?) {
override fun visitIsNullPredicate(isNullPredicate: KtIsNullPredicate<ConeKotlinType, ConeDiagnostic>, data: Nothing?) {
isNullPredicate.arg.accept(this, data)
printer.print(" ${if (isNullPredicate.isNegated) "!=" else "=="} null")
}
override fun visitConstantDescriptor(constantReference: ConeConstantReference, data: Nothing?) {
override fun visitConstantDescriptor(constantReference: KtConstantReference<ConeKotlinType, ConeDiagnostic>, data: Nothing?) {
printer.print(constantReference.name)
}
override fun visitValueParameterReference(valueParameterReference: ConeValueParameterReference, data: Nothing?) {
override fun visitValueParameterReference(valueParameterReference: KtValueParameterReference<ConeKotlinType, ConeDiagnostic>, data: Nothing?) {
printer.print(valueParameterReference.name)
}
private fun inBracketsIfNecessary(parent: ConeContractDescriptionElement, child: ConeContractDescriptionElement, block: () -> Unit) {
private fun inBracketsIfNecessary(parent: KtContractDescriptionElement<ConeKotlinType, ConeDiagnostic>, child: KtContractDescriptionElement<ConeKotlinType, ConeDiagnostic>, block: () -> Unit) {
if (needsBrackets(parent, child)) {
printer.print("(")
block()
@@ -131,12 +134,12 @@ class ConeContractRenderer : ConeContractDescriptionVisitor<Unit, Nothing?>() {
}
}
private fun ConeContractDescriptionElement.isAtom(): Boolean =
this is ConeValueParameterReference || this is ConeConstantReference || this is ConeIsNullPredicate || this is ConeIsInstancePredicate
private fun KtContractDescriptionElement<ConeKotlinType, ConeDiagnostic>.isAtom(): Boolean =
this is KtValueParameterReference || this is KtConstantReference || this is KtIsNullPredicate || this is KtIsInstancePredicate
private fun needsBrackets(parent: ConeContractDescriptionElement, child: ConeContractDescriptionElement): Boolean {
private fun needsBrackets(parent: KtContractDescriptionElement<ConeKotlinType, ConeDiagnostic>, child: KtContractDescriptionElement<ConeKotlinType, ConeDiagnostic>): Boolean {
if (child.isAtom()) return false
if (parent is ConeLogicalNot) return true
if (parent is KtLogicalNot) return true
return parent::class != child::class
}
}
@@ -1,70 +0,0 @@
/*
* Copyright 2010-2019 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.contracts.description
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
/**
* Effect with condition attached to it.
*
* [condition] is some expression, which result-type is Boolean, and clause should
* be interpreted as: "if [effect] took place then [condition]-expression is
* guaranteed to be true"
*
* NB. [effect] and [condition] connected with implication in math logic sense:
* [effect] => [condition]. In particular this means that:
* - there can be multiple ways how [effect] can be produced, but for any of them
* [condition] holds.
* - if [effect] wasn't observed, we *can't* reason that [condition] is false
* - if [condition] is true, we *can't* reason that [effect] will be observed.
*/
class ConeConditionalEffectDeclaration(val effect: ConeEffectDeclaration, val condition: ConeBooleanExpression) : ConeEffectDeclaration() {
override val erroneous: Boolean
get() = effect.erroneous || condition.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitConditionalEffectDeclaration(this, data)
}
/**
* Effect which specifies that subroutine returns some particular value
*/
class ConeReturnsEffectDeclaration(val value: ConeConstantReference) : ConeEffectDeclaration() {
override val erroneous: Boolean
get() = value.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitReturnsEffectDeclaration(this, data)
}
/**
* Effect which specifies, that during execution of subroutine, callable [valueParameterReference] will be invoked
* [kind] amount of times, and will never be invoked after subroutine call is finished.
*/
open class ConeCallsEffectDeclaration(
val valueParameterReference: ConeValueParameterReference,
val kind: EventOccurrencesRange
) : ConeEffectDeclaration() {
override val erroneous: Boolean
get() = valueParameterReference.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitCallsEffectDeclaration(this, data)
}
class ConeErroneousCallsEffectDeclaration(
valueParameterReference: ConeValueParameterReference,
val diagnostic: ConeDiagnostic
) : ConeCallsEffectDeclaration(valueParameterReference, EventOccurrencesRange.UNKNOWN) {
override val erroneous: Boolean
get() = true
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitErroneousCallsEffectDeclaration(this, data)
}
@@ -1,19 +0,0 @@
/*
* Copyright 2010-2022 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.contracts.description
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
class ConeErroneousContractElement(
val diagnostic: ConeDiagnostic
) : ConeEffectDeclaration(), ConeBooleanExpression, ConeContractDescriptionValue {
override val erroneous: Boolean
get() = true
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R {
return contractDescriptionVisitor.visitErroneousElement(this, data)
}
}
@@ -1,25 +0,0 @@
/*
* Copyright 2010-2019 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.contracts.description
import org.jetbrains.kotlin.fir.expressions.LogicOperationKind
class ConeBinaryLogicExpression(val left: ConeBooleanExpression, val right: ConeBooleanExpression, val kind: LogicOperationKind) : ConeBooleanExpression {
override val erroneous: Boolean
get() = left.erroneous || right.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R {
return contractDescriptionVisitor.visitLogicalBinaryOperationContractExpression(this, data)
}
}
class ConeLogicalNot(val arg: ConeBooleanExpression) : ConeBooleanExpression {
override val erroneous: Boolean
get() = arg.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitLogicalNot(this, data)
}
@@ -1,44 +0,0 @@
/*
* Copyright 2010-2019 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.contracts.description
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.types.ConeKotlinType
open class ConeIsInstancePredicate(val arg: ConeValueParameterReference, val type: ConeKotlinType, val isNegated: Boolean) : ConeBooleanExpression {
override val erroneous: Boolean
get() = arg.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitIsInstancePredicate(this, data)
fun negated(): ConeIsInstancePredicate =
ConeIsInstancePredicate(arg, type, isNegated.not())
}
class ConeErroneousIsInstancePredicate(
arg: ConeValueParameterReference,
type: ConeKotlinType,
isNegated: Boolean,
val diagnostic: ConeDiagnostic
) : ConeIsInstancePredicate(arg, type, isNegated) {
override val erroneous: Boolean
get() = true
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitErroneousIsInstancePredicate(this, data)
}
class ConeIsNullPredicate(val arg: ConeValueParameterReference, val isNegated: Boolean) : ConeBooleanExpression {
override val erroneous: Boolean
get() = arg.erroneous
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitIsNullPredicate(this, data)
fun negated(): ConeIsNullPredicate =
ConeIsNullPredicate(arg, isNegated.not())
}
@@ -1,74 +1,33 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 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.contracts.description
import org.jetbrains.kotlin.contracts.description.*
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.types.ConeKotlinType
interface ConeContractDescriptionValue : ConeContractDescriptionElement {
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitValue(this, data)
object ConeContractConstantValues {
val NULL = KtConstantReference<ConeKotlinType, ConeDiagnostic>("NULL")
val WILDCARD = KtConstantReference<ConeKotlinType, ConeDiagnostic>("WILDCARD")
val NOT_NULL = KtConstantReference<ConeKotlinType, ConeDiagnostic>("NOT_NULL")
val TRUE = KtBooleanConstantReference<ConeKotlinType, ConeDiagnostic>("TRUE")
val FALSE = KtBooleanConstantReference<ConeKotlinType, ConeDiagnostic>("FALSE")
}
open class ConeConstantReference protected constructor(val name: String) : ConeContractDescriptionValue {
override val erroneous: Boolean
get() = false
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitConstantDescriptor(this, data)
companion object {
val NULL = ConeConstantReference("NULL")
val WILDCARD = ConeConstantReference("WILDCARD")
val NOT_NULL = ConeConstantReference("NOT_NULL")
}
}
class ConeBooleanConstantReference private constructor(name: String) : ConeConstantReference(name), ConeBooleanExpression {
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitBooleanConstantDescriptor(this, data)
companion object {
val TRUE = ConeBooleanConstantReference("TRUE")
val FALSE = ConeBooleanConstantReference("FALSE")
}
}
class ConeErroneousConstantReference(val diagnostic: ConeDiagnostic) : ConeConstantReference("ERROR") {
override val erroneous: Boolean
get() = true
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitErroneousConstantReference(this, data)
}
/*
* Index of value parameter of function
* -1 means that it is reference to extension receiver
*/
open class ConeValueParameterReference(val parameterIndex: Int, val name: String) : ConeContractDescriptionValue {
init {
assert(parameterIndex >= -1)
}
override val erroneous: Boolean
get() = false
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitValueParameterReference(this, data)
}
class ConeBooleanValueParameterReference(parameterIndex: Int, name: String) : ConeValueParameterReference(parameterIndex, name), ConeBooleanExpression {
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitBooleanValueParameterReference(this, data)
}
class ConeErroneousValueParameterReference(val diagnostic: ConeDiagnostic) : ConeValueParameterReference(Int.MAX_VALUE, "ERROR") {
override val erroneous: Boolean
get() = true
override fun <R, D> accept(contractDescriptionVisitor: ConeContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitErroneousValueParameterReference(this, data)
}
typealias ConeEffectDeclaration = KtEffectDeclaration<ConeKotlinType, ConeDiagnostic>
typealias ConeContractDescriptionElement = KtContractDescriptionElement<ConeKotlinType, ConeDiagnostic>
typealias ConeCallsEffectDeclaration = KtCallsEffectDeclaration<ConeKotlinType, ConeDiagnostic>
typealias ConeConditionalEffectDeclaration = KtConditionalEffectDeclaration<ConeKotlinType, ConeDiagnostic>
typealias ConeReturnsEffectDeclaration = KtReturnsEffectDeclaration<ConeKotlinType, ConeDiagnostic>
typealias ConeConstantReference = KtConstantReference<ConeKotlinType, ConeDiagnostic>
typealias ConeIsNullPredicate = KtIsNullPredicate<ConeKotlinType, ConeDiagnostic>
typealias ConeIsInstancePredicate = KtIsInstancePredicate<ConeKotlinType, ConeDiagnostic>
typealias ConeLogicalNot = KtLogicalNot<ConeKotlinType, ConeDiagnostic>
typealias ConeBooleanExpression = KtBooleanExpression<ConeKotlinType, ConeDiagnostic>
typealias ConeBinaryLogicExpression = KtBinaryLogicExpression<ConeKotlinType, ConeDiagnostic>
typealias ConeBooleanConstantReference = KtBooleanConstantReference<ConeKotlinType, ConeDiagnostic>
typealias ConeValueParameterReference = KtValueParameterReference<ConeKotlinType, ConeDiagnostic>
typealias ConeBooleanValueParameterReference = KtBooleanValueParameterReference<ConeKotlinType, ConeDiagnostic>
@@ -1,10 +0,0 @@
/*
* Copyright 2010-2019 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.expressions
enum class LogicOperationKind(val token: String) {
AND("&&"), OR("||")
}
@@ -42,7 +42,7 @@ val smartcastStabilityType = type(SmartcastStability::class)
val fqNameType = type(FqName::class)
val classIdType = type(ClassId::class)
val annotationUseSiteTargetType = type(AnnotationUseSiteTarget::class)
val operationKindType = type("fir.expressions", "LogicOperationKind")
val operationKindType = type("contracts.description", "LogicOperationKind")
val coneKotlinTypeType = type(ConeKotlinType::class)
val coneSimpleKotlinTypeType = type(ConeSimpleKotlinType::class)
val coneClassLikeTypeType = type(ConeClassLikeType::class)
@@ -23,12 +23,12 @@ object KotlinStubVersions {
// Though only kotlin declarations (no code in the bodies) are stubbed, please do increase this version
// if you are not 100% sure it can be avoided.
// Increasing this version will lead to reindexing of all kotlin source files on the first IDE startup with the new version.
const val SOURCE_STUB_VERSION = 149
const val SOURCE_STUB_VERSION = 150
// Binary stub version should be increased if stub format (org.jetbrains.kotlin.psi.stubs.impl) is changed
// or changes are made to the core stub building code (org.jetbrains.kotlin.idea.decompiler.stubBuilder).
// Increasing this version will lead to reindexing of all binary files that are potentially kotlin binaries (including all class files).
private const val BINARY_STUB_VERSION = 85
private const val BINARY_STUB_VERSION = 86
// Classfile stub version should be increased if changes are made to classfile stub building subsystem (org.jetbrains.kotlin.idea.decompiler.classFile)
// Increasing this version will lead to reindexing of all classfiles.
@@ -51,7 +51,8 @@ public class KtFunctionElementType extends KtStubElementType<KotlinFunctionStub,
return new KotlinFunctionStubImpl(
(StubElement<?>) parentStub, StringRef.fromString(psi.getName()), isTopLevel, fqName,
isExtension, hasBlockBody, hasBody, psi.hasTypeParameterListBeforeFunctionName(),
psi.mayHaveContract()
psi.mayHaveContract(),
null
);
}
@@ -67,7 +68,11 @@ public class KtFunctionElementType extends KtStubElementType<KotlinFunctionStub,
dataStream.writeBoolean(stub.hasBlockBody());
dataStream.writeBoolean(stub.hasBody());
dataStream.writeBoolean(stub.hasTypeParameterListBeforeFunctionName());
dataStream.writeBoolean(stub.mayHaveContract());
boolean haveContract = stub.mayHaveContract();
dataStream.writeBoolean(haveContract);
if (haveContract && stub instanceof KotlinFunctionStubImpl) {
((KotlinFunctionStubImpl) stub).serializeContract(dataStream);
}
}
@NotNull
@@ -84,10 +89,9 @@ public class KtFunctionElementType extends KtStubElementType<KotlinFunctionStub,
boolean hasBody = dataStream.readBoolean();
boolean hasTypeParameterListBeforeFunctionName = dataStream.readBoolean();
boolean mayHaveContract = dataStream.readBoolean();
return new KotlinFunctionStubImpl(
(StubElement<?>) parentStub, name, isTopLevel, fqName, isExtension, hasBlockBody, hasBody,
hasTypeParameterListBeforeFunctionName, mayHaveContract
hasTypeParameterListBeforeFunctionName, mayHaveContract, mayHaveContract ? KotlinFunctionStubImpl.Companion.deserializeContract(dataStream) : null
);
}
@@ -61,7 +61,7 @@ public class KtUserTypeElementType extends KtStubElementType<KotlinUserTypeStub,
}
}
private static void serializeType(@NotNull StubOutputStream dataStream, @Nullable KotlinTypeBean type) throws IOException {
public static void serializeType(@NotNull StubOutputStream dataStream, @Nullable KotlinTypeBean type) throws IOException {
dataStream.writeInt(KotlinTypeBeanKind.fromBean(type).ordinal());
if (type instanceof KotlinClassTypeBean) {
StubUtils.serializeClassId(dataStream, ((KotlinClassTypeBean) type).getClassId());
@@ -94,7 +94,7 @@ public class KtUserTypeElementType extends KtStubElementType<KotlinUserTypeStub,
}
@Nullable
private static KotlinTypeBean deserializeType(@NotNull StubInputStream dataStream) throws IOException {
public static KotlinTypeBean deserializeType(@NotNull StubInputStream dataStream) throws IOException {
KotlinTypeBeanKind typeKind = KotlinTypeBeanKind.values()[dataStream.readInt()];
switch (typeKind) {
case CLASS: {
@@ -7,12 +7,165 @@ package org.jetbrains.kotlin.psi.stubs.impl
import com.intellij.psi.PsiElement
import com.intellij.psi.stubs.StubElement
import com.intellij.psi.stubs.StubInputStream
import com.intellij.psi.stubs.StubOutputStream
import org.jetbrains.kotlin.contracts.description.*
import org.jetbrains.kotlin.psi.KtContractEffect
import org.jetbrains.kotlin.psi.stubs.KotlinContractEffectStub
import org.jetbrains.kotlin.psi.stubs.elements.KtContractEffectElementType
import org.jetbrains.kotlin.psi.stubs.elements.KtUserTypeElementType.deserializeType
import org.jetbrains.kotlin.psi.stubs.elements.KtUserTypeElementType.serializeType
class KotlinContractEffectStubImpl(
parent: StubElement<out PsiElement>?,
elementType: KtContractEffectElementType
) : KotlinPlaceHolderStubImpl<KtContractEffect>(parent, elementType), KotlinContractEffectStub {
) : KotlinPlaceHolderStubImpl<KtContractEffect>(parent, elementType), KotlinContractEffectStub
enum class KotlinContractEffectType {
CALLS {
override fun deserialize(dataStream: StubInputStream): KtCallsEffectDeclaration<KotlinTypeBean, Nothing?> {
val declaration = PARAMETER_REFERENCE.deserialize(dataStream)
val range = EventOccurrencesRange.values()[dataStream.readInt()]
return KtCallsEffectDeclaration(declaration as KtValueParameterReference, range)
}
},
RETURNS {
override fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement<KotlinTypeBean, Nothing?> {
return KtReturnsEffectDeclaration(CONSTANT.deserialize(dataStream) as KtConstantReference)
}
},
CONDITIONAL {
override fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement<KotlinTypeBean, Nothing?> {
val descriptionElement = values()[dataStream.readInt()].deserialize(dataStream)
val condition = values()[dataStream.readInt()].deserialize(dataStream)
return KtConditionalEffectDeclaration(
descriptionElement as KtEffectDeclaration,
condition as KtBooleanExpression
)
}
},
IS_NULL {
override fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement<KotlinTypeBean, Nothing?> {
return KtIsNullPredicate(
PARAMETER_REFERENCE.deserialize(dataStream) as KtValueParameterReference,
dataStream.readBoolean()
)
}
},
IS_INSTANCE {
override fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement<KotlinTypeBean, Nothing?> {
return KtIsInstancePredicate(
PARAMETER_REFERENCE.deserialize(dataStream) as KtValueParameterReference,
deserializeType(dataStream)!!,
dataStream.readBoolean()
)
}
},
NOT {
override fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement<KotlinTypeBean, Nothing?> {
return KtLogicalNot(values()[dataStream.readInt()].deserialize(dataStream) as KtBooleanExpression)
}
},
BOOLEAN_LOGIC {
override fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement<KotlinTypeBean, Nothing?> {
val kind = if (dataStream.readBoolean()) LogicOperationKind.AND else LogicOperationKind.OR
val left = values()[dataStream.readInt()].deserialize(dataStream) as KtBooleanExpression
val right = values()[dataStream.readInt()].deserialize(dataStream) as KtBooleanExpression
return KtBinaryLogicExpression(left, right, kind)
}
},
PARAMETER_REFERENCE {
override fun deserialize(dataStream: StubInputStream): KtValueParameterReference<KotlinTypeBean, Nothing?> {
return KtValueParameterReference(dataStream.readInt(), IGNORE_REFERENCE_PARAMETER_NAME)
}
},
CONSTANT {
override fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement<KotlinTypeBean, Nothing?> {
return when (val str = dataStream.readNameString()!!) {
"TRUE" -> KotlinContractConstantValues.TRUE
"FALSE" -> KotlinContractConstantValues.FALSE
"NULL" -> KotlinContractConstantValues.NULL
"NOT_NULL" -> KotlinContractConstantValues.NOT_NULL
"WILDCARD" -> KotlinContractConstantValues.WILDCARD
else -> error("Unexpected $str")
}
}
};
abstract fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement<KotlinTypeBean, Nothing?>
companion object {
const val IGNORE_REFERENCE_PARAMETER_NAME = "<ignore>"
}
}
class KotlinContractSerializationVisitor(val dataStream: StubOutputStream) :
KtContractDescriptionVisitor<Unit, Nothing?, KotlinTypeBean, Nothing?>() {
override fun visitConditionalEffectDeclaration(
conditionalEffect: KtConditionalEffectDeclaration<KotlinTypeBean, Nothing?>,
data: Nothing?
) {
dataStream.writeInt(KotlinContractEffectType.CONDITIONAL.ordinal)
conditionalEffect.effect.accept(this, data)
conditionalEffect.condition.accept(this, data)
}
override fun visitReturnsEffectDeclaration(returnsEffect: KtReturnsEffectDeclaration<KotlinTypeBean, Nothing?>, data: Nothing?) {
dataStream.writeInt(KotlinContractEffectType.RETURNS.ordinal)
returnsEffect.value.accept(this, data)
}
override fun visitCallsEffectDeclaration(callsEffect: KtCallsEffectDeclaration<KotlinTypeBean, Nothing?>, data: Nothing?) {
dataStream.writeInt(KotlinContractEffectType.CALLS.ordinal)
callsEffect.valueParameterReference.accept(this, data)
dataStream.writeInt(callsEffect.kind.ordinal)
}
override fun visitLogicalBinaryOperationContractExpression(
binaryLogicExpression: KtBinaryLogicExpression<KotlinTypeBean, Nothing?>,
data: Nothing?
) {
dataStream.writeInt(KotlinContractEffectType.BOOLEAN_LOGIC.ordinal)
dataStream.writeBoolean(binaryLogicExpression.kind == LogicOperationKind.AND)
binaryLogicExpression.left.accept(this, data)
binaryLogicExpression.right.accept(this, data)
}
override fun visitLogicalNot(logicalNot: KtLogicalNot<KotlinTypeBean, Nothing?>, data: Nothing?) {
dataStream.writeInt(KotlinContractEffectType.NOT.ordinal)
logicalNot.arg.accept(this, data)
}
override fun visitIsInstancePredicate(isInstancePredicate: KtIsInstancePredicate<KotlinTypeBean, Nothing?>, data: Nothing?) {
dataStream.writeInt(KotlinContractEffectType.IS_INSTANCE.ordinal)
isInstancePredicate.arg.accept(this, data)
serializeType(dataStream, isInstancePredicate.type)
dataStream.writeBoolean(isInstancePredicate.isNegated)
}
override fun visitIsNullPredicate(isNullPredicate: KtIsNullPredicate<KotlinTypeBean, Nothing?>, data: Nothing?) {
dataStream.writeInt(KotlinContractEffectType.IS_NULL.ordinal)
isNullPredicate.arg.accept(this, data)
dataStream.writeBoolean(isNullPredicate.isNegated)
}
override fun visitConstantDescriptor(constantReference: KtConstantReference<KotlinTypeBean, Nothing?>, data: Nothing?) {
dataStream.writeInt(KotlinContractEffectType.CONSTANT.ordinal)
dataStream.writeName(constantReference.name)
}
override fun visitValueParameterReference(valueParameterReference: KtValueParameterReference<KotlinTypeBean, Nothing?>, data: Nothing?) {
dataStream.writeInt(KotlinContractEffectType.PARAMETER_REFERENCE.ordinal)
dataStream.writeInt(valueParameterReference.parameterIndex)
}
}
object KotlinContractConstantValues {
val NULL = KtConstantReference<KotlinTypeBean, Nothing?>("NULL")
val WILDCARD = KtConstantReference<KotlinTypeBean, Nothing?>("WILDCARD")
val NOT_NULL = KtConstantReference<KotlinTypeBean, Nothing?>("NOT_NULL")
val TRUE = KtBooleanConstantReference<KotlinTypeBean, Nothing?>("TRUE")
val FALSE = KtBooleanConstantReference<KotlinTypeBean, Nothing?>("FALSE")
}
@@ -16,13 +16,17 @@
package org.jetbrains.kotlin.psi.stubs.impl
import com.intellij.psi.PsiElement
import com.intellij.psi.stubs.StubElement
import com.intellij.psi.stubs.StubInputStream
import com.intellij.psi.stubs.StubOutputStream
import com.intellij.util.io.StringRef
import org.jetbrains.kotlin.contracts.description.KtContractDescriptionElement
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.stubs.KotlinFunctionStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
import org.jetbrains.kotlin.name.FqName
import com.intellij.psi.PsiElement
import java.io.IOException
class KotlinFunctionStubImpl(
parent: StubElement<out PsiElement>?,
@@ -33,7 +37,8 @@ class KotlinFunctionStubImpl(
private val hasBlockBody: Boolean,
private val hasBody: Boolean,
private val hasTypeParameterListBeforeFunctionName: Boolean,
private val mayHaveContract: Boolean
private val mayHaveContract: Boolean,
val contract: List<KtContractDescriptionElement<KotlinTypeBean, Nothing?>>?
) : KotlinStubBaseImpl<KtNamedFunction>(parent, KtStubElementTypes.FUNCTION), KotlinFunctionStub {
init {
if (isTopLevel && fqName == null) {
@@ -50,4 +55,24 @@ class KotlinFunctionStubImpl(
override fun hasBody() = hasBody
override fun hasTypeParameterListBeforeFunctionName() = hasTypeParameterListBeforeFunctionName
override fun mayHaveContract(): Boolean = mayHaveContract
@Throws(IOException::class)
fun serializeContract(dataStream: StubOutputStream) {
val effects: List<KtContractDescriptionElement<KotlinTypeBean, Nothing?>>? = contract
dataStream.writeInt(effects?.size ?: 0)
val visitor = KotlinContractSerializationVisitor(dataStream)
effects?.forEach { it.accept(visitor, null) }
}
companion object {
fun deserializeContract(dataStream: StubInputStream): List<KtContractDescriptionElement<KotlinTypeBean, Nothing?>> {
val effects = mutableListOf<KtContractDescriptionElement<KotlinTypeBean, Nothing?>>()
val count: Int = dataStream.readInt()
for (i in 0 until count) {
val effectType: KotlinContractEffectType = KotlinContractEffectType.values()[dataStream.readInt()]
effects.add(effectType.deserialize(dataStream))
}
return effects
}
}
}