diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/contracts/firContractUtils.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/contracts/firContractUtils.kt index c06e58fb10a..ab7d7f4b69f 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/contracts/firContractUtils.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/contracts/firContractUtils.kt @@ -12,10 +12,13 @@ import org.jetbrains.kotlin.analysis.api.contracts.description.booleans.* import org.jetbrains.kotlin.analysis.api.fir.KtSymbolByFirBuilder import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirFunctionSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtParameterSymbol +import org.jetbrains.kotlin.contracts.description.* import org.jetbrains.kotlin.fir.contracts.description.* -import org.jetbrains.kotlin.fir.expressions.LogicOperationKind +import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic +import org.jetbrains.kotlin.contracts.description.LogicOperationKind +import org.jetbrains.kotlin.fir.types.ConeKotlinType -internal fun ConeEffectDeclaration.coneEffectDeclarationToAnalysisApi( +internal fun KtEffectDeclaration.coneEffectDeclarationToAnalysisApi( builder: KtSymbolByFirBuilder, firFunctionSymbol: KtFirFunctionSymbol ): KtContractEffectDeclaration = @@ -24,7 +27,7 @@ internal fun ConeEffectDeclaration.coneEffectDeclarationToAnalysisApi( private class ConeContractDescriptionElementToAnalysisApi( private val builder: KtSymbolByFirBuilder, private val firFunctionSymbol: KtFirFunctionSymbol -) : ConeContractDescriptionVisitor() { +) : KtContractDescriptionVisitor() { override fun visitConditionalEffectDeclaration( conditionalEffect: ConeConditionalEffectDeclaration, @@ -39,15 +42,15 @@ private class ConeContractDescriptionElementToAnalysisApi( data: Unit ): KtContractReturnsContractEffectDeclaration = when (val value = returnsEffect.value) { - ConeConstantReference.NULL -> + ConeContractConstantValues.NULL -> KtContractReturnsSpecificValueEffectDeclaration(KtContractConstantValue(KtContractConstantType.NULL, builder.token)) - ConeConstantReference.NOT_NULL -> KtContractReturnsNotNullEffectDeclaration(builder.token) - ConeConstantReference.WILDCARD -> KtContractReturnsSuccessfullyEffectDeclaration(builder.token) - is ConeBooleanConstantReference -> KtContractReturnsSpecificValueEffectDeclaration( + ConeContractConstantValues.NOT_NULL -> KtContractReturnsNotNullEffectDeclaration(builder.token) + ConeContractConstantValues.WILDCARD -> KtContractReturnsSuccessfullyEffectDeclaration(builder.token) + is KtBooleanConstantReference -> KtContractReturnsSpecificValueEffectDeclaration( KtContractConstantValue( when (value) { - ConeBooleanConstantReference.TRUE -> KtContractConstantType.TRUE - ConeBooleanConstantReference.FALSE -> KtContractConstantType.FALSE + ConeContractConstantValues.TRUE -> KtContractConstantType.TRUE + ConeContractConstantValues.FALSE -> KtContractConstantType.FALSE else -> error("Can't convert $value to the Analysis API") }, builder.token @@ -56,7 +59,7 @@ private class ConeContractDescriptionElementToAnalysisApi( else -> error("Can't convert $returnsEffect to the Analysis API") } - override fun visitCallsEffectDeclaration(callsEffect: ConeCallsEffectDeclaration, data: Unit): KtContractCallsInPlaceContractEffectDeclaration = + override fun visitCallsEffectDeclaration(callsEffect: KtCallsEffectDeclaration, data: Unit): KtContractCallsInPlaceContractEffectDeclaration = KtContractCallsInPlaceContractEffectDeclaration( callsEffect.valueParameterReference.accept(), callsEffect.kind @@ -92,8 +95,8 @@ private class ConeContractDescriptionElementToAnalysisApi( data: Unit ): KtContractBooleanConstantExpression = when (booleanConstantDescriptor) { - ConeBooleanConstantReference.TRUE -> KtContractBooleanConstantExpression(true, builder.token) - ConeBooleanConstantReference.FALSE -> KtContractBooleanConstantExpression(false, builder.token) + ConeContractConstantValues.TRUE -> KtContractBooleanConstantExpression(true, builder.token) + ConeContractConstantValues.FALSE -> KtContractBooleanConstantExpression(false, builder.token) else -> error("Can't convert $booleanConstantDescriptor to Analysis API") } diff --git a/analysis/decompiled/decompiler-to-file-stubs/testData/additionalClsStubInfo/Contracts/Contracts.kt b/analysis/decompiled/decompiler-to-file-stubs/testData/additionalClsStubInfo/Contracts/Contracts.kt new file mode 100644 index 00000000000..d3216e07f7b --- /dev/null +++ b/analysis/decompiled/decompiler-to-file-stubs/testData/additionalClsStubInfo/Contracts/Contracts.kt @@ -0,0 +1,77 @@ +// JVM_FILE_NAME: ContractsKt + +@file:OptIn(ExperimentalContracts::class) +package test + +import kotlin.contracts.* + +fun myRequire(x: Boolean) { + contract { + returns() implies x + } +} + +fun call_InPlace(block: () -> R): R { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + return block() +} + +fun isNull(obj: Any?): Boolean { + contract { + returns(true) implies (obj != null) + } + return obj != null +} + +fun isNotNull(foo: Any?): Any? { + contract { + returnsNotNull() implies (foo != null) + } + return foo +} + +fun isString(foo: Any?): String? { + contract { + returnsNotNull() implies (foo is String) + } + return foo as? String +} + +fun isNotString(foo: Any?): String? { + contract { + returnsNotNull() implies (foo !is String) + } + return if (foo is String) null else "not a string" +} + +fun String?.asSafe(): String? { + contract { + returnsNotNull() implies (this@asSafe != null) + } + return this +} + +fun isStringCheck(x: Any?): Any? { + contract { + returns(true) implies (x is Comparable<*> || x is CharSequence) + } + + return x is String +} + +fun isStringOrNumber(x: Any?): Any? { + contract { + returns(true) implies (x is Comparable<*> && (x is CharSequence || x is Number)) + } + + return x is String || x is Int +} + +inline fun T?.test0(): Boolean { + contract { + returns(true) implies (this@test0 is T) + } + return this is T +} \ No newline at end of file diff --git a/analysis/decompiled/decompiler-to-file-stubs/testData/additionalClsStubInfo/Contracts/Contracts.txt b/analysis/decompiled/decompiler-to-file-stubs/testData/additionalClsStubInfo/Contracts/Contracts.txt new file mode 100644 index 00000000000..87c09d084e4 --- /dev/null +++ b/analysis/decompiled/decompiler-to-file-stubs/testData/additionalClsStubInfo/Contracts/Contracts.txt @@ -0,0 +1,173 @@ +PsiJetFileStubImpl[package=test] +KotlinStub$PACKAGE_DIRECTIVE + KotlinStub$REFERENCE_EXPRESSION[referencedName=test] +KotlinStub$IMPORT_LIST +KotlinStub$FUN[fqName=test.call_InPlace, hasBlockBody=true, hasBody=true, hasTypeParameterListBeforeFunctionName=true, isExtension=false, isTopLevel=true, mayHaveContract=true, name=call_InPlace] + effect:CallsInPlace(param(0), EXACTLY_ONCE) + KotlinStub$MODIFIER_LIST[public] + KotlinStub$TYPE_PARAMETER_LIST + KotlinStub$TYPE_PARAMETER[fqName=null, isInVariance=false, isOutVariance=false, name=R] + KotlinStub$VALUE_PARAMETER_LIST + KotlinStub$VALUE_PARAMETER[fqName=null, hasDefaultValue=false, hasValOrVar=false, isMutable=false, name=block] + KotlinStub$TYPE_REFERENCE + KotlinStub$FUNCTION_TYPE + KotlinStub$VALUE_PARAMETER_LIST + KotlinStub$TYPE_REFERENCE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=R] + KotlinStub$TYPE_REFERENCE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=R] +KotlinStub$FUN[fqName=test.isNotNull, hasBlockBody=true, hasBody=true, hasTypeParameterListBeforeFunctionName=false, isExtension=false, isTopLevel=true, mayHaveContract=true, name=isNotNull] + effect:Returns(NOT_NULL) -> param(0) != null + KotlinStub$MODIFIER_LIST[public] + KotlinStub$VALUE_PARAMETER_LIST + KotlinStub$VALUE_PARAMETER[fqName=null, hasDefaultValue=false, hasValOrVar=false, isMutable=false, name=foo] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Any] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Any] +KotlinStub$FUN[fqName=test.isNotString, hasBlockBody=true, hasBody=true, hasTypeParameterListBeforeFunctionName=false, isExtension=false, isTopLevel=true, mayHaveContract=true, name=isNotString] + effect:Returns(NOT_NULL) -> param(0) !is KotlinClassTypeBean(classId=kotlin/String, arguments=[], nullable=false) + KotlinStub$MODIFIER_LIST[public] + KotlinStub$VALUE_PARAMETER_LIST + KotlinStub$VALUE_PARAMETER[fqName=null, hasDefaultValue=false, hasValOrVar=false, isMutable=false, name=foo] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Any] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=String] +KotlinStub$FUN[fqName=test.isNull, hasBlockBody=true, hasBody=true, hasTypeParameterListBeforeFunctionName=false, isExtension=false, isTopLevel=true, mayHaveContract=true, name=isNull] + effect:Returns(TRUE) -> param(0) != null + KotlinStub$MODIFIER_LIST[public] + KotlinStub$VALUE_PARAMETER_LIST + KotlinStub$VALUE_PARAMETER[fqName=null, hasDefaultValue=false, hasValOrVar=false, isMutable=false, name=obj] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Any] + KotlinStub$TYPE_REFERENCE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Boolean] +KotlinStub$FUN[fqName=test.isString, hasBlockBody=true, hasBody=true, hasTypeParameterListBeforeFunctionName=false, isExtension=false, isTopLevel=true, mayHaveContract=true, name=isString] + effect:Returns(NOT_NULL) -> param(0) is KotlinClassTypeBean(classId=kotlin/String, arguments=[], nullable=false) + KotlinStub$MODIFIER_LIST[public] + KotlinStub$VALUE_PARAMETER_LIST + KotlinStub$VALUE_PARAMETER[fqName=null, hasDefaultValue=false, hasValOrVar=false, isMutable=false, name=foo] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Any] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=String] +KotlinStub$FUN[fqName=test.isStringCheck, hasBlockBody=true, hasBody=true, hasTypeParameterListBeforeFunctionName=false, isExtension=false, isTopLevel=true, mayHaveContract=true, name=isStringCheck] + effect:Returns(TRUE) -> param(0) is KotlinClassTypeBean(classId=kotlin/Comparable, arguments=[KotlinTypeArgumentBean(projectionKind=STAR, type=null)], nullable=false) || param(0) is KotlinClassTypeBean(classId=kotlin/CharSequence, arguments=[], nullable=false) + KotlinStub$MODIFIER_LIST[public] + KotlinStub$VALUE_PARAMETER_LIST + KotlinStub$VALUE_PARAMETER[fqName=null, hasDefaultValue=false, hasValOrVar=false, isMutable=false, name=x] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Any] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Any] +KotlinStub$FUN[fqName=test.isStringOrNumber, hasBlockBody=true, hasBody=true, hasTypeParameterListBeforeFunctionName=false, isExtension=false, isTopLevel=true, mayHaveContract=true, name=isStringOrNumber] + effect:Returns(TRUE) -> param(0) is KotlinClassTypeBean(classId=kotlin/Comparable, arguments=[KotlinTypeArgumentBean(projectionKind=STAR, type=null)], nullable=false) && param(0) is KotlinClassTypeBean(classId=kotlin/CharSequence, arguments=[], nullable=false) || param(0) is KotlinClassTypeBean(classId=kotlin/Number, arguments=[], nullable=false) + KotlinStub$MODIFIER_LIST[public] + KotlinStub$VALUE_PARAMETER_LIST + KotlinStub$VALUE_PARAMETER[fqName=null, hasDefaultValue=false, hasValOrVar=false, isMutable=false, name=x] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Any] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Any] +KotlinStub$FUN[fqName=test.myRequire, hasBlockBody=true, hasBody=true, hasTypeParameterListBeforeFunctionName=false, isExtension=false, isTopLevel=true, mayHaveContract=true, name=myRequire] + effect:Returns(WILDCARD) -> param(0) + KotlinStub$MODIFIER_LIST[public] + KotlinStub$VALUE_PARAMETER_LIST + KotlinStub$VALUE_PARAMETER[fqName=null, hasDefaultValue=false, hasValOrVar=false, isMutable=false, name=x] + KotlinStub$TYPE_REFERENCE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Boolean] + KotlinStub$TYPE_REFERENCE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Unit] +KotlinStub$FUN[fqName=test.asSafe, hasBlockBody=true, hasBody=true, hasTypeParameterListBeforeFunctionName=false, isExtension=true, isTopLevel=true, mayHaveContract=true, name=asSafe] + effect:Returns(NOT_NULL) -> param(-1) != null + KotlinStub$MODIFIER_LIST[public] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=String] + KotlinStub$VALUE_PARAMETER_LIST + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=String] +KotlinStub$FUN[fqName=test.test0, hasBlockBody=true, hasBody=true, hasTypeParameterListBeforeFunctionName=true, isExtension=true, isTopLevel=true, mayHaveContract=true, name=test0] + effect:Returns(TRUE) -> param(-1) is KotlinTypeParameterTypeBean(typeParameterName=T, nullable=false, definitelyNotNull=false) + KotlinStub$MODIFIER_LIST[public inline] + KotlinStub$TYPE_PARAMETER_LIST + KotlinStub$TYPE_PARAMETER[fqName=null, isInVariance=false, isOutVariance=false, name=T] + KotlinStub$MODIFIER_LIST[reified] + KotlinStub$TYPE_REFERENCE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Number] + KotlinStub$TYPE_REFERENCE + KotlinStub$NULLABLE_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=T] + KotlinStub$VALUE_PARAMETER_LIST + KotlinStub$TYPE_REFERENCE + KotlinStub$USER_TYPE + KotlinStub$USER_TYPE + KotlinStub$REFERENCE_EXPRESSION[referencedName=kotlin] + KotlinStub$REFERENCE_EXPRESSION[referencedName=Boolean] \ No newline at end of file diff --git a/analysis/decompiled/decompiler-to-file-stubs/tests/org/jetbrains/kotlin/analysis/decompiler/stub/files/AbstractAdditionalStubInfoTest.kt b/analysis/decompiled/decompiler-to-file-stubs/tests/org/jetbrains/kotlin/analysis/decompiler/stub/files/AbstractAdditionalStubInfoTest.kt index 5a4b8e57e13..b23a5caf926 100644 --- a/analysis/decompiled/decompiler-to-file-stubs/tests/org/jetbrains/kotlin/analysis/decompiler/stub/files/AbstractAdditionalStubInfoTest.kt +++ b/analysis/decompiled/decompiler-to-file-stubs/tests/org/jetbrains/kotlin/analysis/decompiler/stub/files/AbstractAdditionalStubInfoTest.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.decompiler.stub.files import com.intellij.psi.stubs.StubElement import com.intellij.util.indexing.FileContentImpl import org.jetbrains.kotlin.analysis.decompiler.stub.file.KotlinClsStubBuilder +import org.jetbrains.kotlin.contracts.description.* import org.jetbrains.kotlin.psi.KtProjectionKind import org.jetbrains.kotlin.psi.stubs.impl.* import org.jetbrains.kotlin.test.KotlinTestUtils @@ -25,11 +26,22 @@ abstract class AbstractAdditionalStubInfoTest : AbstractDecompiledClassTest() { private fun extractAdditionInfo(stub: StubElement<*>, builder: StringBuilder, level: Int) { builder.append(stub.toString()) - if (stub is KotlinUserTypeStubImpl) { - val upperBound = stub.upperBound - if (upperBound != null) { - builder.append(" ft: ") - appendFlexibleTypeInfo(builder, upperBound) + when (stub) { + is KotlinUserTypeStubImpl -> { + val upperBound = stub.upperBound + if (upperBound != null) { + builder.append(" ft: ") + appendFlexibleTypeInfo(builder, upperBound) + } + } + is KotlinFunctionStubImpl -> { + val contract = stub.contract + if (contract != null) { + for (element in contract) { + builder.append("\n" + " ".repeat(level)).append("effect:") + element.accept(KotlinContractRenderer(builder), null) + } + } } } for (child in stub.childrenStubs) { @@ -77,4 +89,53 @@ abstract class AbstractAdditionalStubInfoTest : AbstractDecompiledClassTest() { } } } +} + +class KotlinContractRenderer(private val buffer: StringBuilder) : KtContractDescriptionVisitor() { + override fun visitConditionalEffectDeclaration(conditionalEffect: KtConditionalEffectDeclaration, data: Nothing?) { + conditionalEffect.effect.accept(this, data) + buffer.append(" -> ") + conditionalEffect.condition.accept(this, data) + } + + override fun visitReturnsEffectDeclaration(returnsEffect: KtReturnsEffectDeclaration, data: Nothing?) { + buffer.append("Returns(") + returnsEffect.value.accept(this, data) + buffer.append(")") + } + + override fun visitCallsEffectDeclaration(callsEffect: KtCallsEffectDeclaration, data: Nothing?) { + buffer.append("CallsInPlace(") + callsEffect.valueParameterReference.accept(this, data) + buffer.append(", ${callsEffect.kind})") + } + + override fun visitLogicalBinaryOperationContractExpression(binaryLogicExpression: KtBinaryLogicExpression, data: Nothing?) { + binaryLogicExpression.left.accept(this, data) + buffer.append(" ${binaryLogicExpression.kind.token} ") + binaryLogicExpression.right.accept(this, data) + } + + override fun visitLogicalNot(logicalNot: KtLogicalNot, data: Nothing?) { + logicalNot.arg.accept(this, data) + } + + override fun visitIsInstancePredicate(isInstancePredicate: KtIsInstancePredicate, data: Nothing?) { + isInstancePredicate.arg.accept(this, data) + buffer.append(" ${if (isInstancePredicate.isNegated) "!" else ""}is ${isInstancePredicate.type}") + } + + override fun visitIsNullPredicate(isNullPredicate: KtIsNullPredicate, data: Nothing?) { + isNullPredicate.arg.accept(this, data) + buffer.append(" ${if (isNullPredicate.isNegated) "!=" else "=="} null") + } + + override fun visitConstantDescriptor(constantReference: KtConstantReference, data: Nothing?) { + buffer.append(constantReference.name) + } + + override fun visitValueParameterReference(valueParameterReference: KtValueParameterReference, data: Nothing?) { + buffer.append("param(").append(valueParameterReference.parameterIndex).append(")") + } + } \ No newline at end of file diff --git a/analysis/decompiled/decompiler-to-file-stubs/tests/org/jetbrains/kotlin/analysis/decompiler/stub/files/AdditionalStubInfoTestGenerated.java b/analysis/decompiled/decompiler-to-file-stubs/tests/org/jetbrains/kotlin/analysis/decompiler/stub/files/AdditionalStubInfoTestGenerated.java index db254c140fe..59631733845 100644 --- a/analysis/decompiled/decompiler-to-file-stubs/tests/org/jetbrains/kotlin/analysis/decompiler/stub/files/AdditionalStubInfoTestGenerated.java +++ b/analysis/decompiled/decompiler-to-file-stubs/tests/org/jetbrains/kotlin/analysis/decompiler/stub/files/AdditionalStubInfoTestGenerated.java @@ -30,6 +30,12 @@ public class AdditionalStubInfoTestGenerated extends AbstractAdditionalStubInfoT runTest("analysis/decompiled/decompiler-to-file-stubs/testData/additionalClsStubInfo/AnnotatedFlexibleTypes/"); } + @Test + @TestMetadata("Contracts") + public void testContracts() throws Exception { + runTest("analysis/decompiled/decompiler-to-file-stubs/testData/additionalClsStubInfo/Contracts/"); + } + @Test @TestMetadata("OuterClassesWithFlexibleArgs") public void testOuterClassesWithFlexibleArgs() throws Exception { diff --git a/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/CallableClsStubBuilder.kt b/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/CallableClsStubBuilder.kt index d246a1ebdb4..6216c8e5e08 100644 --- a/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/CallableClsStubBuilder.kt +++ b/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/CallableClsStubBuilder.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.resolve.DataClassResolver import org.jetbrains.kotlin.serialization.deserialization.AnnotatedCallableKind import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer import org.jetbrains.kotlin.serialization.deserialization.getName +import org.jetbrains.kotlin.utils.addToStdlib.runIf fun createPackageDeclarationsStubs( parentStub: StubElement, @@ -173,6 +174,7 @@ private class FunctionClsStubBuilder( // Note that arguments passed to stubs here and elsewhere are based on what stabs would be generated based on decompiled code // As functions are never decompiled to fun f() = 1 form, hasBlockBody is always true // This info is anyway irrelevant for the purposes these stubs are used + val hasContract = functionProto.hasContract() return KotlinFunctionStubImpl( parent, callableName.ref(), @@ -182,7 +184,10 @@ private class FunctionClsStubBuilder( hasBlockBody = true, hasBody = Flags.MODALITY.get(functionProto.flags) != Modality.ABSTRACT, hasTypeParameterListBeforeFunctionName = functionProto.typeParameterList.isNotEmpty(), - mayHaveContract = functionProto.hasContract() + mayHaveContract = hasContract, + runIf(hasContract) { + ClsContractBuilder(c, typeStubBuilder).loadContract(functionProto) + } ) } } diff --git a/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/ClsContractBuilder.kt b/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/ClsContractBuilder.kt new file mode 100644 index 00000000000..f1159d841b7 --- /dev/null +++ b/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/ClsContractBuilder.kt @@ -0,0 +1,55 @@ +/* + * 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.analysis.decompiler.stub + +import org.jetbrains.kotlin.contracts.description.KtBooleanValueParameterReference +import org.jetbrains.kotlin.contracts.description.KtConstantReference +import org.jetbrains.kotlin.contracts.description.KtEffectDeclaration +import org.jetbrains.kotlin.contracts.description.KtValueParameterReference +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.isInstanceType +import org.jetbrains.kotlin.name.StandardClassIds +import org.jetbrains.kotlin.psi.stubs.impl.* +import org.jetbrains.kotlin.psi.stubs.impl.KotlinContractEffectType.Companion.IGNORE_REFERENCE_PARAMETER_NAME +import org.jetbrains.kotlin.serialization.deserialization.ProtoBufContractDeserializer +import org.jetbrains.kotlin.serialization.deserialization.getClassId + +class ClsContractBuilder(private val c: ClsStubBuilderContext, private val typeStubBuilder: TypeClsStubBuilder) : + ProtoBufContractDeserializer() { + + fun loadContract(proto: ProtoBuf.Function): List>? { + return proto.contract.effectList.map { loadPossiblyConditionalEffect(it, proto) ?: return null } + } + + override fun extractVariable(valueParameterIndex: Int, owner: ProtoBuf.Function): KtValueParameterReference { + val type = if (valueParameterIndex < 0) { + owner.receiverType + } else owner.valueParameterList[valueParameterIndex].type + return if (type.hasClassName() && c.nameResolver.getClassId(type.className) == StandardClassIds.Boolean) { + KtBooleanValueParameterReference(valueParameterIndex, name = IGNORE_REFERENCE_PARAMETER_NAME) + } else KtValueParameterReference(valueParameterIndex, name = IGNORE_REFERENCE_PARAMETER_NAME) + } + + override fun extractType(proto: ProtoBuf.Expression): KotlinTypeBean? { + return typeStubBuilder.createKotlinTypeBean(proto.isInstanceType(c.typeTable)) + } + + override fun loadConstant(value: ProtoBuf.Expression.ConstantValue): KtConstantReference { + return when (value) { + ProtoBuf.Expression.ConstantValue.TRUE -> KotlinContractConstantValues.TRUE + ProtoBuf.Expression.ConstantValue.FALSE -> KotlinContractConstantValues.FALSE + ProtoBuf.Expression.ConstantValue.NULL -> KotlinContractConstantValues.NULL + } + } + + override fun getNotNull(): KtConstantReference { + return KotlinContractConstantValues.NOT_NULL + } + + override fun getWildcard(): KtConstantReference { + return KotlinContractConstantValues.WILDCARD + } +} \ No newline at end of file diff --git a/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/TypeClsStubBuilder.kt b/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/TypeClsStubBuilder.kt index 7060b8039ff..7d4231990b0 100644 --- a/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/TypeClsStubBuilder.kt +++ b/analysis/decompiled/decompiler-to-stubs/src/org/jetbrains/kotlin/analysis/decompiler/stub/TypeClsStubBuilder.kt @@ -150,7 +150,7 @@ class TypeClsStubBuilder(private val c: ClsStubBuilderContext) { } } - private fun createKotlinTypeBean( + fun createKotlinTypeBean( type: Type? ): KotlinTypeBean? { if (type == null) return null diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirContractChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirContractChecker.kt index 0f1a3086246..b41efd7defa 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirContractChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirContractChecker.kt @@ -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() { + private object DiagnosticExtractor : KtContractDescriptionVisitor() { override fun visitContractDescriptionElement( contractDescriptionElement: ConeContractDescriptionElement, data: Nothing? @@ -90,7 +92,7 @@ object FirContractChecker : FirFunctionChecker() { } override fun visitErroneousCallsEffectDeclaration( - callsEffect: ConeErroneousCallsEffectDeclaration, + callsEffect: KtErroneousCallsEffectDeclaration, data: Nothing? ): ConeDiagnostic { return callsEffect.diagnostic @@ -112,7 +114,7 @@ object FirContractChecker : FirFunctionChecker() { } override fun visitErroneousIsInstancePredicate( - isInstancePredicate: ConeErroneousIsInstancePredicate, + isInstancePredicate: KtErroneousIsInstancePredicate, data: Nothing? ): ConeDiagnostic { return isInstancePredicate.diagnostic @@ -123,20 +125,20 @@ object FirContractChecker : FirFunctionChecker() { } override fun visitErroneousConstantReference( - erroneousConstantReference: ConeErroneousConstantReference, + erroneousConstantReference: KtErroneousConstantReference, data: Nothing? ): ConeDiagnostic { return erroneousConstantReference.diagnostic } override fun visitErroneousValueParameterReference( - valueParameterReference: ConeErroneousValueParameterReference, + valueParameterReference: KtErroneousValueParameterReference, data: Nothing? ): ConeDiagnostic { return valueParameterReference.diagnostic } - override fun visitErroneousElement(element: ConeErroneousContractElement, data: Nothing?): ConeDiagnostic { + override fun visitErroneousElement(element: KtErroneousContractElement, data: Nothing?): ConeDiagnostic { return element.diagnostic } } diff --git a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirContractDeserializer.kt b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirContractDeserializer.kt index 9356d74b88a..d02a1a4d98e 100644 --- a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirContractDeserializer.kt +++ b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirContractDeserializer.kt @@ -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() { 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 = 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? { 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 { + 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 = 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 { + 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 { + return ConeContractConstantValues.WILDCARD } } diff --git a/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirContractSerializer.kt b/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirContractSerializer.kt index 837c14eab04..1b2093af3e9 100644 --- a/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirContractSerializer.kt +++ b/compiler/fir/fir-serialization/src/org/jetbrains/kotlin/fir/serialization/FirContractSerializer.kt @@ -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() { + return contractDescriptionElement.accept(object : KtContractDescriptionVisitor() { 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") } } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt index bf879cd0bef..ee6b6ad7bad 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt @@ -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 diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt index 108bb9c1472..2f8e30a188b 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt @@ -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 diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index 805cfcf4c4b..e542885d876 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -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.* diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt index 0d74ee07de4..e0ef074b031 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt @@ -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 diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt index 0ceebaa1cf3..0a3761d9838 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt @@ -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) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/ConeEffectExtractor.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/ConeEffectExtractor.kt index 8d644b75d10..36309841270 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/ConeEffectExtractor.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/ConeEffectExtractor.kt @@ -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 { + 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 visitConstExpression(constExpression: FirConstExpression, 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 { return ConeContractDescriptionError.NoReceiver(callableId.callableName).asElement() } - private fun noArgument(callableId: CallableId): ConeErroneousContractElement { + private fun noArgument(callableId: CallableId): KtErroneousContractElement { 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)) } } } diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/contracts.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/contracts.kt index 5544954f536..670c8f047cf 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/contracts.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/dfa/contracts.kt @@ -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 { diff --git a/compiler/fir/tree/build.gradle.kts b/compiler/fir/tree/build.gradle.kts index 7715c39a2dd..569f17ac199 100644 --- a/compiler/fir/tree/build.gradle.kts +++ b/compiler/fir/tree/build.gradle.kts @@ -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 diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirBinaryLogicExpression.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirBinaryLogicExpression.kt index fd89132b6d8..b679840898c 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirBinaryLogicExpression.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirBinaryLogicExpression.kt @@ -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.* diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirBinaryLogicExpressionBuilder.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirBinaryLogicExpressionBuilder.kt index c01aa9b1ca9..a17f1aab48b 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirBinaryLogicExpressionBuilder.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirBinaryLogicExpressionBuilder.kt @@ -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 diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirBinaryLogicExpressionImpl.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirBinaryLogicExpressionImpl.kt index d5c56dd9a7d..ca3734a0b59 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirBinaryLogicExpressionImpl.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirBinaryLogicExpressionImpl.kt @@ -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.* diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeContractDescription.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeContractDescription.kt deleted file mode 100644 index 8039baa25f7..00000000000 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeContractDescription.kt +++ /dev/null @@ -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 accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R - - val erroneous: Boolean -} - -abstract class ConeEffectDeclaration : ConeContractDescriptionElement { - override fun accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R = - contractDescriptionVisitor.visitEffectDeclaration(this, data) -} - -interface ConeBooleanExpression : ConeContractDescriptionElement { - override fun accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R = - contractDescriptionVisitor.visitBooleanExpression(this, data) -} diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeContractDescriptionVisitor.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeContractDescriptionVisitor.kt deleted file mode 100644 index 3cf27f4e0f8..00000000000 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeContractDescriptionVisitor.kt +++ /dev/null @@ -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 { - 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) -} diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeContractRenderer.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeContractRenderer.kt index 7e9da417520..d1a1ed33085 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeContractRenderer.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeContractRenderer.kt @@ -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() { +class ConeContractRenderer : KtContractDescriptionVisitor() { internal lateinit var components: FirRendererComponents private val printer get() = components.printer @@ -74,54 +77,54 @@ class ConeContractRenderer : ConeContractDescriptionVisitor() { printer.println(">") } - override fun visitConditionalEffectDeclaration(conditionalEffect: ConeConditionalEffectDeclaration, data: Nothing?) { + override fun visitConditionalEffectDeclaration(conditionalEffect: KtConditionalEffectDeclaration, 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, data: Nothing?) { printer.print("Returns(") returnsEffect.value.accept(this, data) printer.print(")") } - override fun visitCallsEffectDeclaration(callsEffect: ConeCallsEffectDeclaration, data: Nothing?) { + override fun visitCallsEffectDeclaration(callsEffect: KtCallsEffectDeclaration, 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, 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, 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, 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, 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, data: Nothing?) { printer.print(constantReference.name) } - override fun visitValueParameterReference(valueParameterReference: ConeValueParameterReference, data: Nothing?) { + override fun visitValueParameterReference(valueParameterReference: KtValueParameterReference, data: Nothing?) { printer.print(valueParameterReference.name) } - private fun inBracketsIfNecessary(parent: ConeContractDescriptionElement, child: ConeContractDescriptionElement, block: () -> Unit) { + private fun inBracketsIfNecessary(parent: KtContractDescriptionElement, child: KtContractDescriptionElement, block: () -> Unit) { if (needsBrackets(parent, child)) { printer.print("(") block() @@ -131,12 +134,12 @@ class ConeContractRenderer : ConeContractDescriptionVisitor() { } } - private fun ConeContractDescriptionElement.isAtom(): Boolean = - this is ConeValueParameterReference || this is ConeConstantReference || this is ConeIsNullPredicate || this is ConeIsInstancePredicate + private fun KtContractDescriptionElement.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, child: KtContractDescriptionElement): Boolean { if (child.isAtom()) return false - if (parent is ConeLogicalNot) return true + if (parent is KtLogicalNot) return true return parent::class != child::class } } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeErroneousContractElement.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeErroneousContractElement.kt deleted file mode 100644 index f207f588a62..00000000000 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeErroneousContractElement.kt +++ /dev/null @@ -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 accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R { - return contractDescriptionVisitor.visitErroneousElement(this, data) - } -} diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeLogicalCombinators.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeLogicalCombinators.kt deleted file mode 100644 index 17ff4cd86a2..00000000000 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeLogicalCombinators.kt +++ /dev/null @@ -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 accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R { - return contractDescriptionVisitor.visitLogicalBinaryOperationContractExpression(this, data) - } -} - -class ConeLogicalNot(val arg: ConeBooleanExpression) : ConeBooleanExpression { - override val erroneous: Boolean - get() = arg.erroneous - - override fun accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R = - contractDescriptionVisitor.visitLogicalNot(this, data) -} diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConePredicates.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConePredicates.kt deleted file mode 100644 index c3875109d3b..00000000000 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConePredicates.kt +++ /dev/null @@ -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 accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, 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 accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, 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 accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R = - contractDescriptionVisitor.visitIsNullPredicate(this, data) - - fun negated(): ConeIsNullPredicate = - ConeIsNullPredicate(arg, isNegated.not()) -} diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeValues.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeValues.kt index b2876ebf66f..375424e47c3 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeValues.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeValues.kt @@ -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 accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R = - contractDescriptionVisitor.visitValue(this, data) +object ConeContractConstantValues { + val NULL = KtConstantReference("NULL") + val WILDCARD = KtConstantReference("WILDCARD") + val NOT_NULL = KtConstantReference("NOT_NULL") + val TRUE = KtBooleanConstantReference("TRUE") + val FALSE = KtBooleanConstantReference("FALSE") } -open class ConeConstantReference protected constructor(val name: String) : ConeContractDescriptionValue { - override val erroneous: Boolean - get() = false - - override fun accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, 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 accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, 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 accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, 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 accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R = - contractDescriptionVisitor.visitValueParameterReference(this, data) -} - -class ConeBooleanValueParameterReference(parameterIndex: Int, name: String) : ConeValueParameterReference(parameterIndex, name), ConeBooleanExpression { - override fun accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, 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 accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R = - contractDescriptionVisitor.visitErroneousValueParameterReference(this, data) -} +typealias ConeEffectDeclaration = KtEffectDeclaration +typealias ConeContractDescriptionElement = KtContractDescriptionElement +typealias ConeCallsEffectDeclaration = KtCallsEffectDeclaration +typealias ConeConditionalEffectDeclaration = KtConditionalEffectDeclaration +typealias ConeReturnsEffectDeclaration = KtReturnsEffectDeclaration +typealias ConeConstantReference = KtConstantReference +typealias ConeIsNullPredicate = KtIsNullPredicate +typealias ConeIsInstancePredicate = KtIsInstancePredicate +typealias ConeLogicalNot = KtLogicalNot +typealias ConeBooleanExpression = KtBooleanExpression +typealias ConeBinaryLogicExpression = KtBinaryLogicExpression +typealias ConeBooleanConstantReference = KtBooleanConstantReference +typealias ConeValueParameterReference = KtValueParameterReference +typealias ConeBooleanValueParameterReference = KtBooleanValueParameterReference diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/Types.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/Types.kt index f21d4051a80..06a65453492 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/Types.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/Types.kt @@ -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) diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/KotlinStubVersions.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/KotlinStubVersions.kt index 043bca9d096..22b30ca02c6 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/KotlinStubVersions.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/KotlinStubVersions.kt @@ -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. diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtFunctionElementType.java b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtFunctionElementType.java index 8f8de4aac97..41d9ba912db 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtFunctionElementType.java +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtFunctionElementType.java @@ -51,7 +51,8 @@ public class KtFunctionElementType extends KtStubElementType) 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) parentStub, name, isTopLevel, fqName, isExtension, hasBlockBody, hasBody, - hasTypeParameterListBeforeFunctionName, mayHaveContract + hasTypeParameterListBeforeFunctionName, mayHaveContract, mayHaveContract ? KotlinFunctionStubImpl.Companion.deserializeContract(dataStream) : null ); } diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtUserTypeElementType.java b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtUserTypeElementType.java index deec528effd..ed5163142c5 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtUserTypeElementType.java +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtUserTypeElementType.java @@ -61,7 +61,7 @@ public class KtUserTypeElementType extends KtStubElementType?, elementType: KtContractEffectElementType -) : KotlinPlaceHolderStubImpl(parent, elementType), KotlinContractEffectStub { +) : KotlinPlaceHolderStubImpl(parent, elementType), KotlinContractEffectStub + +enum class KotlinContractEffectType { + CALLS { + override fun deserialize(dataStream: StubInputStream): KtCallsEffectDeclaration { + 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 { + return KtReturnsEffectDeclaration(CONSTANT.deserialize(dataStream) as KtConstantReference) + } + }, + CONDITIONAL { + override fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement { + 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 { + return KtIsNullPredicate( + PARAMETER_REFERENCE.deserialize(dataStream) as KtValueParameterReference, + dataStream.readBoolean() + ) + } + }, + IS_INSTANCE { + override fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement { + return KtIsInstancePredicate( + PARAMETER_REFERENCE.deserialize(dataStream) as KtValueParameterReference, + deserializeType(dataStream)!!, + dataStream.readBoolean() + ) + } + }, + NOT { + override fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement { + return KtLogicalNot(values()[dataStream.readInt()].deserialize(dataStream) as KtBooleanExpression) + } + }, + BOOLEAN_LOGIC { + override fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement { + 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 { + return KtValueParameterReference(dataStream.readInt(), IGNORE_REFERENCE_PARAMETER_NAME) + } + }, + CONSTANT { + override fun deserialize(dataStream: StubInputStream): KtContractDescriptionElement { + 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 + + companion object { + const val IGNORE_REFERENCE_PARAMETER_NAME = "" + } +} + +class KotlinContractSerializationVisitor(val dataStream: StubOutputStream) : + KtContractDescriptionVisitor() { + override fun visitConditionalEffectDeclaration( + conditionalEffect: KtConditionalEffectDeclaration, + data: Nothing? + ) { + dataStream.writeInt(KotlinContractEffectType.CONDITIONAL.ordinal) + conditionalEffect.effect.accept(this, data) + conditionalEffect.condition.accept(this, data) + } + + override fun visitReturnsEffectDeclaration(returnsEffect: KtReturnsEffectDeclaration, data: Nothing?) { + dataStream.writeInt(KotlinContractEffectType.RETURNS.ordinal) + returnsEffect.value.accept(this, data) + } + + override fun visitCallsEffectDeclaration(callsEffect: KtCallsEffectDeclaration, data: Nothing?) { + dataStream.writeInt(KotlinContractEffectType.CALLS.ordinal) + callsEffect.valueParameterReference.accept(this, data) + dataStream.writeInt(callsEffect.kind.ordinal) + } + + override fun visitLogicalBinaryOperationContractExpression( + binaryLogicExpression: KtBinaryLogicExpression, + 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, data: Nothing?) { + dataStream.writeInt(KotlinContractEffectType.NOT.ordinal) + logicalNot.arg.accept(this, data) + } + + override fun visitIsInstancePredicate(isInstancePredicate: KtIsInstancePredicate, 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, data: Nothing?) { + dataStream.writeInt(KotlinContractEffectType.IS_NULL.ordinal) + isNullPredicate.arg.accept(this, data) + dataStream.writeBoolean(isNullPredicate.isNegated) + } + + + override fun visitConstantDescriptor(constantReference: KtConstantReference, data: Nothing?) { + dataStream.writeInt(KotlinContractEffectType.CONSTANT.ordinal) + dataStream.writeName(constantReference.name) + } + + override fun visitValueParameterReference(valueParameterReference: KtValueParameterReference, data: Nothing?) { + dataStream.writeInt(KotlinContractEffectType.PARAMETER_REFERENCE.ordinal) + dataStream.writeInt(valueParameterReference.parameterIndex) + } +} + +object KotlinContractConstantValues { + val NULL = KtConstantReference("NULL") + val WILDCARD = KtConstantReference("WILDCARD") + val NOT_NULL = KtConstantReference("NOT_NULL") + + val TRUE = KtBooleanConstantReference("TRUE") + val FALSE = KtBooleanConstantReference("FALSE") } \ No newline at end of file diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/impl/KotlinFunctionStubImpl.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/impl/KotlinFunctionStubImpl.kt index 47b9e2be385..53c04741fe9 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/impl/KotlinFunctionStubImpl.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/impl/KotlinFunctionStubImpl.kt @@ -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?, @@ -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>? ) : KotlinStubBaseImpl(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>? = contract + dataStream.writeInt(effects?.size ?: 0) + val visitor = KotlinContractSerializationVisitor(dataStream) + effects?.forEach { it.accept(visitor, null) } + } + + companion object { + fun deserializeContract(dataStream: StubInputStream): List> { + val effects = mutableListOf>() + 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 + } + } } diff --git a/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtContractDescription.kt b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtContractDescription.kt new file mode 100644 index 00000000000..95261d775cd --- /dev/null +++ b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtContractDescription.kt @@ -0,0 +1,22 @@ +/* + * 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.contracts.description + +interface KtContractDescriptionElement { + fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R + + val erroneous: Boolean +} + +abstract class KtEffectDeclaration : KtContractDescriptionElement { + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitEffectDeclaration(this, data) +} + +interface KtBooleanExpression : KtContractDescriptionElement { + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitBooleanExpression(this, data) +} diff --git a/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtContractDescriptionVisitor.kt b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtContractDescriptionVisitor.kt new file mode 100644 index 00000000000..94ad7122594 --- /dev/null +++ b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtContractDescriptionVisitor.kt @@ -0,0 +1,69 @@ +/* + * 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.contracts.description + + +abstract class KtContractDescriptionVisitor { + open fun visitContractDescriptionElement(contractDescriptionElement: KtContractDescriptionElement, data: D): R { + throw IllegalStateException("Top of hierarchy reached, no overloads were found for element: $contractDescriptionElement") + } + + // Effects + open fun visitEffectDeclaration(effectDeclaration: KtEffectDeclaration, data: D): R = visitContractDescriptionElement(effectDeclaration, data) + + open fun visitConditionalEffectDeclaration(conditionalEffect: KtConditionalEffectDeclaration, data: D): R = + visitEffectDeclaration(conditionalEffect, data) + + open fun visitReturnsEffectDeclaration(returnsEffect: KtReturnsEffectDeclaration, data: D): R = + visitEffectDeclaration(returnsEffect, data) + + open fun visitCallsEffectDeclaration(callsEffect: KtCallsEffectDeclaration, data: D): R = + visitEffectDeclaration(callsEffect, data) + + open fun visitErroneousCallsEffectDeclaration(callsEffect: KtErroneousCallsEffectDeclaration, data: D): R = + visitCallsEffectDeclaration(callsEffect, data) + + // Expressions + open fun visitBooleanExpression(booleanExpression: KtBooleanExpression, data: D): R = + visitContractDescriptionElement(booleanExpression, data) + + open fun visitLogicalBinaryOperationContractExpression(binaryLogicExpression: KtBinaryLogicExpression, data: D): R = + visitBooleanExpression(binaryLogicExpression, data) + + open fun visitLogicalNot(logicalNot: KtLogicalNot, data: D): R = visitBooleanExpression(logicalNot, data) + + open fun visitIsInstancePredicate(isInstancePredicate: KtIsInstancePredicate, data: D): R = + visitBooleanExpression(isInstancePredicate, data) + + open fun visitErroneousIsInstancePredicate(isInstancePredicate: KtErroneousIsInstancePredicate, data: D): R = + visitIsInstancePredicate(isInstancePredicate, data) + + open fun visitIsNullPredicate(isNullPredicate: KtIsNullPredicate, data: D): R = visitBooleanExpression(isNullPredicate, data) + + // Values + open fun visitValue(value: KtContractDescriptionValue, data: D): R = visitContractDescriptionElement(value, data) + + open fun visitConstantDescriptor(constantReference: KtConstantReference, data: D): R = visitValue(constantReference, data) + + open fun visitBooleanConstantDescriptor(booleanConstantDescriptor: KtBooleanConstantReference, data: D): R = + visitConstantDescriptor(booleanConstantDescriptor, data) + + open fun visitErroneousConstantReference(erroneousConstantReference: KtErroneousConstantReference, data: D): R = + visitConstantDescriptor(erroneousConstantReference, data) + + open fun visitValueParameterReference(valueParameterReference: KtValueParameterReference, data: D): R = + visitValue(valueParameterReference, data) + + open fun visitBooleanValueParameterReference(booleanValueParameterReference: KtBooleanValueParameterReference, data: D): R = + visitValueParameterReference(booleanValueParameterReference, data) + + open fun visitErroneousValueParameterReference(valueParameterReference: KtErroneousValueParameterReference, data: D): R = + visitValueParameterReference(valueParameterReference, data) + + // Error + open fun visitErroneousElement(element: KtErroneousContractElement, data: D): R = + visitContractDescriptionElement(element, data) +} diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeEffects.kt b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtEffects.kt similarity index 55% rename from compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeEffects.kt rename to core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtEffects.kt index 41317c93ab9..e48f9ac9e36 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/contracts/description/ConeEffects.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtEffects.kt @@ -1,12 +1,9 @@ /* - * 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.EventOccurrencesRange -import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic +package org.jetbrains.kotlin.contracts.description /** * Effect with condition attached to it. @@ -22,11 +19,14 @@ import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic * - 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() { +class KtConditionalEffectDeclaration( + val effect: KtEffectDeclaration, + val condition: KtBooleanExpression +) : KtEffectDeclaration() { override val erroneous: Boolean get() = effect.erroneous || condition.erroneous - override fun accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R = + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = contractDescriptionVisitor.visitConditionalEffectDeclaration(this, data) } @@ -34,11 +34,12 @@ class ConeConditionalEffectDeclaration(val effect: ConeEffectDeclaration, val co /** * Effect which specifies that subroutine returns some particular value */ -class ConeReturnsEffectDeclaration(val value: ConeConstantReference) : ConeEffectDeclaration() { +class KtReturnsEffectDeclaration(val value: KtConstantReference) : + KtEffectDeclaration() { override val erroneous: Boolean get() = value.erroneous - override fun accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R = + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = contractDescriptionVisitor.visitReturnsEffectDeclaration(this, data) } @@ -47,24 +48,24 @@ class ConeReturnsEffectDeclaration(val value: ConeConstantReference) : ConeEffec * 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, +open class KtCallsEffectDeclaration( + val valueParameterReference: KtValueParameterReference, val kind: EventOccurrencesRange -) : ConeEffectDeclaration() { +) : KtEffectDeclaration() { override val erroneous: Boolean get() = valueParameterReference.erroneous - override fun accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R = + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = contractDescriptionVisitor.visitCallsEffectDeclaration(this, data) } -class ConeErroneousCallsEffectDeclaration( - valueParameterReference: ConeValueParameterReference, - val diagnostic: ConeDiagnostic -) : ConeCallsEffectDeclaration(valueParameterReference, EventOccurrencesRange.UNKNOWN) { +class KtErroneousCallsEffectDeclaration( + valueParameterReference: KtValueParameterReference, + val diagnostic: Diagnostic +) : KtCallsEffectDeclaration(valueParameterReference, EventOccurrencesRange.UNKNOWN) { override val erroneous: Boolean get() = true - override fun accept(contractDescriptionVisitor: ConeContractDescriptionVisitor, data: D): R = + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = contractDescriptionVisitor.visitErroneousCallsEffectDeclaration(this, data) } diff --git a/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtErroneousContractElement.kt b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtErroneousContractElement.kt new file mode 100644 index 00000000000..8b7ee7b3a57 --- /dev/null +++ b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtErroneousContractElement.kt @@ -0,0 +1,17 @@ +/* + * 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.contracts.description + +class KtErroneousContractElement( + val diagnostic: Diagnostic +) : KtEffectDeclaration(), KtBooleanExpression, KtContractDescriptionValue { + override val erroneous: Boolean + get() = true + + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R { + return contractDescriptionVisitor.visitErroneousElement(this, data) + } +} diff --git a/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtLogicalCombinators.kt b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtLogicalCombinators.kt new file mode 100644 index 00000000000..c9b9df5cfe7 --- /dev/null +++ b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtLogicalCombinators.kt @@ -0,0 +1,27 @@ +/* + * 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.contracts.description + +class KtBinaryLogicExpression( + val left: KtBooleanExpression, + val right: KtBooleanExpression, + val kind: LogicOperationKind +) : KtBooleanExpression { + override val erroneous: Boolean + get() = left.erroneous || right.erroneous + + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R { + return contractDescriptionVisitor.visitLogicalBinaryOperationContractExpression(this, data) + } +} + +class KtLogicalNot(val arg: KtBooleanExpression) : KtBooleanExpression { + override val erroneous: Boolean + get() = arg.erroneous + + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitLogicalNot(this, data) +} diff --git a/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtPredicates.kt b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtPredicates.kt new file mode 100644 index 00000000000..a8454e18b91 --- /dev/null +++ b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtPredicates.kt @@ -0,0 +1,43 @@ +/* + * 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.contracts.description + +open class KtIsInstancePredicate(val arg: KtValueParameterReference, val type: Type, val isNegated: Boolean) : + KtBooleanExpression { + override val erroneous: Boolean + get() = arg.erroneous + + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitIsInstancePredicate(this, data) + + fun negated(): KtIsInstancePredicate = + KtIsInstancePredicate(arg, type, isNegated.not()) +} + +class KtErroneousIsInstancePredicate( + arg: KtValueParameterReference, + type: Type, + isNegated: Boolean, + val diagnostic: Diagnostic +) : KtIsInstancePredicate(arg, type, isNegated) { + override val erroneous: Boolean + get() = true + + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitErroneousIsInstancePredicate(this, data) +} + +class KtIsNullPredicate(val arg: KtValueParameterReference, val isNegated: Boolean) : + KtBooleanExpression { + override val erroneous: Boolean + get() = arg.erroneous + + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitIsNullPredicate(this, data) + + fun negated(): KtIsNullPredicate = + KtIsNullPredicate(arg, isNegated.not()) +} diff --git a/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtValues.kt b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtValues.kt new file mode 100644 index 00000000000..5576781194f --- /dev/null +++ b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/KtValues.kt @@ -0,0 +1,64 @@ +/* + * 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.contracts.description + +interface KtContractDescriptionValue : KtContractDescriptionElement { + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitValue(this, data) +} + +open class KtConstantReference(val name: String) : KtContractDescriptionValue { + override val erroneous: Boolean + get() = false + + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitConstantDescriptor(this, data) +} + +class KtBooleanConstantReference(name: String) : KtConstantReference(name), + KtBooleanExpression { + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitBooleanConstantDescriptor(this, data) +} + +class KtErroneousConstantReference(val diagnostic: Diagnostic) : KtConstantReference("ERROR") { + override val erroneous: Boolean + get() = true + + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitErroneousConstantReference(this, data) +} + +/* + * Index of value parameter of function + * -1 means that it is reference to extension receiver + */ +open class KtValueParameterReference(val parameterIndex: Int, val name: String) : + KtContractDescriptionValue { + init { + assert(parameterIndex >= -1) + } + + override val erroneous: Boolean + get() = false + + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitValueParameterReference(this, data) +} + +class KtBooleanValueParameterReference(parameterIndex: Int, name: String) : KtValueParameterReference(parameterIndex, name), + KtBooleanExpression { + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitBooleanValueParameterReference(this, data) +} + +class KtErroneousValueParameterReference(val diagnostic: Diagnostic) : KtValueParameterReference(Int.MAX_VALUE, "ERROR") { + override val erroneous: Boolean + get() = true + + override fun accept(contractDescriptionVisitor: KtContractDescriptionVisitor, data: D): R = + contractDescriptionVisitor.visitErroneousValueParameterReference(this, data) +} diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/LogicOperationKind.kt b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/LogicOperationKind.kt similarity index 65% rename from compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/LogicOperationKind.kt rename to core/compiler.common/src/org/jetbrains/kotlin/contracts/description/LogicOperationKind.kt index 4975d88b4d3..549c43a7b6b 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/expressions/LogicOperationKind.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/contracts/description/LogicOperationKind.kt @@ -1,10 +1,10 @@ /* - * 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.expressions +package org.jetbrains.kotlin.contracts.description enum class LogicOperationKind(val token: String) { AND("&&"), OR("||") -} +} \ No newline at end of file diff --git a/core/deserialization.common/src/org/jetbrains/kotlin/serialization/deserialization/ProtoBufContractDeserializer.kt b/core/deserialization.common/src/org/jetbrains/kotlin/serialization/deserialization/ProtoBufContractDeserializer.kt new file mode 100644 index 00000000000..d28f44d9401 --- /dev/null +++ b/core/deserialization.common/src/org/jetbrains/kotlin/serialization/deserialization/ProtoBufContractDeserializer.kt @@ -0,0 +1,196 @@ +/* + * 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.serialization.deserialization + +import org.jetbrains.kotlin.contracts.description.* +import org.jetbrains.kotlin.contracts.description.LogicOperationKind +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.Flags +import org.jetbrains.kotlin.utils.addIfNotNull + +abstract class ProtoBufContractDeserializer { + protected fun loadPossiblyConditionalEffect( + proto: ProtoBuf.Effect, + owner: Owner + ): KtEffectDeclaration? { + if (proto.hasConclusionOfConditionalEffect()) { + val conclusion = loadExpression(proto.conclusionOfConditionalEffect, owner) ?: return null + val effect = loadSimpleEffect(proto, owner) ?: return null + return KtConditionalEffectDeclaration(effect, conclusion) + } + return loadSimpleEffect(proto, owner) + } + + private fun loadSimpleEffect(proto: ProtoBuf.Effect, owner: Owner): KtEffectDeclaration? { + 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) { + getWildcard() + } else { + @Suppress("UNCHECKED_CAST") + loadExpression(argument, owner) as? KtConstantReference ?: return null + } + KtReturnsEffectDeclaration(returnValue) + } + ProtoBuf.Effect.EffectType.RETURNS_NOT_NULL -> { + KtReturnsEffectDeclaration(getNotNull()) + } + 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() + else + EventOccurrencesRange.UNKNOWN + KtCallsEffectDeclaration(callable, invocationKind) + } + } + } + + private fun loadExpression(proto: ProtoBuf.Expression, owner: Owner): KtBooleanExpression? { + val primitiveType = getPrimitiveType(proto) + val primitiveExpression = extractPrimitiveExpression(proto, primitiveType, owner) + + val complexType = getComplexType(proto) + val childs: MutableList> = mutableListOf() + childs.addIfNotNull(primitiveExpression) + + return when (complexType) { + ComplexExpressionType.AND_SEQUENCE -> { + proto.andArgumentList.mapTo(childs) { loadExpression(it, owner) ?: return null } + childs.reduce { acc, booleanExpression -> KtBinaryLogicExpression(acc, booleanExpression, LogicOperationKind.AND) } + } + + ComplexExpressionType.OR_SEQUENCE -> { + proto.orArgumentList.mapTo(childs) { loadExpression(it, owner) ?: return null } + childs.reduce { acc, booleanExpression -> KtBinaryLogicExpression(acc, booleanExpression, LogicOperationKind.OR) } + } + + null -> primitiveExpression + } + } + + private fun extractPrimitiveExpression(proto: ProtoBuf.Expression, primitiveType: PrimitiveExpressionType?, owner: Owner): KtBooleanExpression? { + val isInverted = Flags.IS_NEGATED.get(proto.flags) + + return when (primitiveType) { + PrimitiveExpressionType.VALUE_PARAMETER_REFERENCE, PrimitiveExpressionType.RECEIVER_REFERENCE -> { + (extractVariable(proto, owner) as? KtBooleanValueParameterReference?)?.invertIfNecessary(isInverted) + } + + PrimitiveExpressionType.CONSTANT -> + (loadConstant(proto.constantValue) as? KtBooleanConstantReference)?.invertIfNecessary(isInverted) + + PrimitiveExpressionType.INSTANCE_CHECK -> { + val variable = extractVariable(proto, owner) ?: return null + val type = extractType(proto) ?: return null + KtIsInstancePredicate(variable, type, isInverted) + } + + PrimitiveExpressionType.NULLABILITY_CHECK -> { + val variable = extractVariable(proto, owner) ?: return null + KtIsNullPredicate(variable, isInverted) + } + + null -> null + } + } + + private fun KtBooleanExpression.invertIfNecessary(shouldInvert: Boolean): KtBooleanExpression = + if (shouldInvert) KtLogicalNot(this) else this + + private fun extractVariable(proto: ProtoBuf.Expression, owner: Owner): KtValueParameterReference? { + if (!proto.hasValueParameterReference()) return null + + return extractVariable(proto.valueParameterReference - 1, owner) + } + + 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 + } + + abstract fun extractVariable(valueParameterIndex: Int, owner: Owner): KtValueParameterReference? + + abstract fun extractType(proto: ProtoBuf.Expression): Type? + + abstract fun loadConstant(value: ProtoBuf.Expression.ConstantValue): KtConstantReference + + + abstract fun getNotNull(): KtConstantReference + + abstract fun getWildcard(): KtConstantReference + + + 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 + } + } + + 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 = 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() + } + + 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 + + } + +} \ No newline at end of file