[FIR] Add contracts resolve for property accessors

This commit is contained in:
Dmitriy Novozhilov
2020-04-13 10:55:29 +03:00
parent d808f3e4fb
commit 1074e4bf29
4 changed files with 119 additions and 2 deletions
@@ -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.<!INAPPLICABLE_CANDIDATE!>foo<!>()
}
}
fun test_2(a: A?) {
a.isNotNull = true
a.<!INAPPLICABLE_CANDIDATE!>foo<!>()
}
@@ -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|<local>/a|.R|/isNotNull| -> {
R|<local>/a|.<Inapplicable(WRONG_RECEIVER): [/A.foo]>#()
}
}
}
public final fun test_2(a: R|A?|): R|kotlin/Unit| {
R|<local>/a|.R|/isNotNull| = Boolean(true)
R|<local>/a|.<Inapplicable(WRONG_RECEIVER): [/A.foo]>#()
}
@@ -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");
@@ -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<FirSimpleFunction> {
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<FirProperty> {
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<FirStatement> {
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 <T : FirContractDescriptionOwner> transformContractDescriptionOwner(
owner: T
): CompositeTransformResult<T> {
@@ -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)
}
}
}