From 1074e4bf295101fe6c8233ec55e590455c1001be Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Mon, 13 Apr 2020 10:55:29 +0300 Subject: [PATCH] [FIR] Add contracts resolve for property accessors --- .../good/returnsImplies/propertyAccessors.kt | 30 +++++++++++ .../good/returnsImplies/propertyAccessors.txt | 35 +++++++++++++ ...FirDiagnosticsWithStdlibTestGenerated.java | 5 ++ .../FirContractResolveTransformer.kt | 51 ++++++++++++++++++- 4 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.kt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.txt diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.kt new file mode 100644 index 00000000000..9ca97d57d65 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.kt @@ -0,0 +1,30 @@ +import kotlin.contracts.* + +interface A { + fun foo() +} + +var Any?.isNotNull: Boolean + get() { + contract { + returns(true) implies (this@isNotNull != null) + } + return this != null + } + set(value) { + contract { + returns() implies (this@isNotNull != null) + require(this != null) + } + } + +fun test_1(a: A?) { + if (a.isNotNull) { + a.foo() + } +} + +fun test_2(a: A?) { + a.isNotNull = true + a.foo() +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.txt new file mode 100644 index 00000000000..df434f4d6ea --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.txt @@ -0,0 +1,35 @@ +FILE: propertyAccessors.kt + public abstract interface A : R|kotlin/Any| { + public abstract fun foo(): R|kotlin/Unit| + + } + public final var R|kotlin/Any?|.isNotNull: R|kotlin/Boolean| + public get(): R|kotlin/Boolean| + [R|Contract description] + < + Returns(TRUE) -> this != null + > + { + [StubStatement] + ^ !=(this@R|/isNotNull|, Null(null)) + } + public set(value: R|kotlin/Boolean|): R|kotlin/Unit| + [R|Contract description] + < + Returns(WILDCARD) -> this != null + > + { + [StubStatement] + } + public final fun test_1(a: R|A?|): R|kotlin/Unit| { + when () { + R|/a|.R|/isNotNull| -> { + R|/a|.#() + } + } + + } + public final fun test_2(a: R|A?|): R|kotlin/Unit| { + R|/a|.R|/isNotNull| = Boolean(true) + R|/a|.#() + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java index 3fb562af0bf..74468a80a0e 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java @@ -593,6 +593,11 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/eqNotEq.kt"); } + @TestMetadata("propertyAccessors.kt") + public void testPropertyAccessors() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/propertyAccessors.kt"); + } + @TestMetadata("receivers.kt") public void testReceivers() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/fromSource/good/returnsImplies/receivers.kt"); diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/FirContractResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/FirContractResolveTransformer.kt index 3a324ad6f88..09a73cdefe7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/FirContractResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/FirContractResolveTransformer.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.contracts.builder.buildResolvedContractDescripti import org.jetbrains.kotlin.fir.contracts.description.ConeEffectDeclaration import org.jetbrains.kotlin.fir.contracts.impl.FirEmptyContractDescription import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty import org.jetbrains.kotlin.fir.errorTypeFromPrototype import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.resolve.ResolutionMode @@ -38,12 +39,12 @@ class FirContractResolveTransformer(session: FirSession, scopeSession: ScopeSess data: ResolutionMode ): CompositeTransformResult { if (!simpleFunction.hasContractToResolve) { - simpleFunction.replaceResolvePhase(FirResolvePhase.CONTRACTS) + simpleFunction.updatePhase() return simpleFunction.compose() } val containingDeclaration = context.containerIfAny if (containingDeclaration != null && containingDeclaration !is FirClass<*>) { - simpleFunction.replaceResolvePhase(FirResolvePhase.CONTRACTS) + simpleFunction.updatePhase() simpleFunction.replaceReturnTypeRef( simpleFunction.returnTypeRef.errorTypeFromPrototype( ConeContractDescriptionError("Local function can not be used in contract description") @@ -64,6 +65,48 @@ class FirContractResolveTransformer(session: FirSession, scopeSession: ScopeSess } } + override fun transformProperty(property: FirProperty, data: ResolutionMode): CompositeTransformResult { + if ( + property.getter?.hasContractToResolve != true && property.setter?.hasContractToResolve != true || + property.isLocal || property.delegate != null + ) { + property.updatePhase() + return property.compose() + } + if (property is FirSyntheticProperty) { + transformSimpleFunction(property.getter.delegate, data) + return property.compose() + } + withTypeParametersOf(property) { + withLocalScopeCleanup { + context.withContainer(property) { + property.getter?.let { transformPropertyAccessor(it, property) } + property.setter?.let { transformPropertyAccessor(it, property) } + } + } + } + property.updatePhase() + return property.compose() + } + + private fun transformPropertyAccessor( + propertyAccessor: FirPropertyAccessor, + owner: FirProperty + ): CompositeTransformResult { + if (!propertyAccessor.hasContractToResolve) { + propertyAccessor.updatePhase() + return propertyAccessor.compose() + } + val receiverTypeRef = owner.receiverTypeRef + return if (receiverTypeRef != null) { + withLabelAndReceiverType(owner.name, propertyAccessor, receiverTypeRef.coneTypeUnsafe()) { + transformContractDescriptionOwner(propertyAccessor) + } + } else { + transformContractDescriptionOwner(propertyAccessor) + } + } + private fun transformContractDescriptionOwner( owner: T ): CompositeTransformResult { @@ -119,6 +162,10 @@ class FirContractResolveTransformer(session: FirSession, scopeSession: ScopeSess private val FirContractDescriptionOwner.hasContractToResolve: Boolean get() = contractDescription is FirRawContractDescription + + private fun FirDeclaration.updatePhase() { + replaceResolvePhase(FirResolvePhase.CONTRACTS) + } } }