[FIR] Add stage for resolve contract desription
This commit is contained in:
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildAnonymousFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildTypeParameter
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.*
|
||||
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
|
||||
@@ -18,10 +19,8 @@ import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirReference
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirIntegerOperatorCall
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirIntegerOperatorCallBuilder
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeProjection
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
|
||||
fun FirFunctionCall.copy(
|
||||
@@ -100,6 +99,15 @@ fun FirTypeRef.resolvedTypeFromPrototype(
|
||||
}
|
||||
}
|
||||
|
||||
fun FirTypeRef.errorTypeFromPrototype(
|
||||
diagnostic: ConeDiagnostic
|
||||
): FirErrorTypeRef {
|
||||
return buildErrorTypeRef {
|
||||
source = this@errorTypeFromPrototype.source
|
||||
this.diagnostic = diagnostic
|
||||
}
|
||||
}
|
||||
|
||||
fun FirTypeParameter.copy(
|
||||
bounds: List<FirTypeRef> = this.bounds,
|
||||
annotations: List<FirAnnotationCall> = this.annotations
|
||||
|
||||
@@ -953,6 +953,16 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
return controlFlowGraph
|
||||
}
|
||||
|
||||
// ----------------------------------- Contract description -----------------------------------
|
||||
|
||||
fun enterContractDescription() {
|
||||
graphBuilder.enterContractDescription().mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitContractDescription() {
|
||||
graphBuilder.exitContractDescription()
|
||||
}
|
||||
|
||||
// ------------------------------------------------------ Utils ------------------------------------------------------
|
||||
|
||||
private var CFGNode<*>.flow: FLOW
|
||||
|
||||
@@ -104,6 +104,8 @@ fun CFGNode<*>.render(): String =
|
||||
is LocalClassExitNode -> "Exit local class ${owner.name}"
|
||||
is AnonymousObjectExitNode -> "Exit anonymous object"
|
||||
|
||||
is ContractDescriptionEnterNode -> "Enter contract description"
|
||||
|
||||
is AbstractBinaryExitNode -> throw IllegalStateException()
|
||||
},
|
||||
)
|
||||
|
||||
@@ -520,6 +520,13 @@ class StubNode(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<FirStub>(
|
||||
}
|
||||
}
|
||||
|
||||
class ContractDescriptionEnterNode(owner: ControlFlowGraph, level: Int, id: Int) : CFGNode<FirStub>(owner, level, id) {
|
||||
override val fir: FirStub = FirStub
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitContractDescriptionEnterNode(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class VariableDeclarationNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int, id: Int) : CFGNode<FirProperty>(owner, level, id) {
|
||||
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
|
||||
return visitor.visitVariableDeclarationNode(this, data)
|
||||
|
||||
+16
@@ -870,6 +870,22 @@ class ControlFlowGraphBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Contract description -----------------------------------
|
||||
|
||||
fun enterContractDescription(): CFGNode<*> {
|
||||
graphs.push(ControlFlowGraph(null, "contract description", ControlFlowGraph.Kind.TopLevel))
|
||||
val node = createContractDescriptionEnterNode().also {
|
||||
graph.enterNode = it
|
||||
}
|
||||
lexicalScopes.push(stackOf(node))
|
||||
return node
|
||||
}
|
||||
|
||||
fun exitContractDescription() {
|
||||
lexicalScopes.pop()
|
||||
graphs.pop()
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
private fun addNodeThatReturnsNothing(node: CFGNode<*>, preferredKind: EdgeKind = EdgeKind.Simple) {
|
||||
|
||||
+3
@@ -11,6 +11,9 @@ import org.jetbrains.kotlin.fir.expressions.*
|
||||
|
||||
fun ControlFlowGraphBuilder.createStubNode(): StubNode = StubNode(graph, levelCounter, createId())
|
||||
|
||||
fun ControlFlowGraphBuilder.createContractDescriptionEnterNode(): ContractDescriptionEnterNode =
|
||||
ContractDescriptionEnterNode(graph, levelCounter, createId())
|
||||
|
||||
fun ControlFlowGraphBuilder.createLoopExitNode(fir: FirLoop): LoopExitNode = LoopExitNode(graph, fir, levelCounter, createId())
|
||||
|
||||
fun ControlFlowGraphBuilder.createLoopEnterNode(fir: FirLoop): LoopEnterNode = LoopEnterNode(graph, fir, levelCounter, createId())
|
||||
|
||||
+4
@@ -269,6 +269,10 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitContractDescriptionEnterNode(node: ContractDescriptionEnterNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
open fun visitVariableDeclarationNode(node: VariableDeclarationNode, data: D): R {
|
||||
return visitNode(node, data)
|
||||
}
|
||||
|
||||
+2
@@ -53,6 +53,8 @@ class ConeTypeMismatchError(val expectedType: ConeKotlinType, val actualType: Co
|
||||
get() = "Type mismatch. Expected: $expectedType, Actual: $actualType"
|
||||
}
|
||||
|
||||
class ConeContractDescriptionError(override val reason: String) : ConeDiagnostic()
|
||||
|
||||
private fun describeSymbol(symbol: AbstractFirBasedSymbol<*>): String {
|
||||
return when (symbol) {
|
||||
is FirClassLikeSymbol<*> -> symbol.classId.asString()
|
||||
|
||||
+2
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvePhase.*
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformerAdapter
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirImplicitTypeBodyResolveTransformerAdapter
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.contracts.FirContractResolveTransformerAdapter
|
||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||
|
||||
// TODO: add FirSession parameter
|
||||
@@ -22,6 +23,7 @@ fun FirResolvePhase.createTransformerByPhase(scopeSession: ScopeSession): FirTra
|
||||
SEALED_CLASS_INHERITORS -> FirSealedClassInheritorsTransformer()
|
||||
TYPES -> FirTypeResolveTransformerAdapter(scopeSession)
|
||||
STATUS -> FirStatusResolveTransformerAdapter()
|
||||
CONTRACTS -> FirContractResolveTransformerAdapter(scopeSession)
|
||||
IMPLICIT_TYPES_BODY_RESOLVE -> FirImplicitTypeBodyResolveTransformerAdapter(scopeSession)
|
||||
BODY_RESOLVE -> FirBodyResolveTransformerAdapter(scopeSession)
|
||||
}
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ open class FirBodyResolveTransformer(
|
||||
BodyResolveTransformerComponents(session, scopeSession, this, context)
|
||||
|
||||
private val expressionsTransformer = FirExpressionsResolveTransformer(this)
|
||||
private val declarationsTransformer = FirDeclarationsResolveTransformer(this)
|
||||
protected open val declarationsTransformer = FirDeclarationsResolveTransformer(this)
|
||||
private val controlFlowStatementsTransformer = FirControlFlowStatementsResolveTransformer(this)
|
||||
|
||||
override fun transformFile(file: FirFile, data: ResolutionMode): CompositeTransformResult<FirFile> {
|
||||
|
||||
+3
-3
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.visitors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer) : FirPartialBodyResolveTransformer(transformer) {
|
||||
open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer) : FirPartialBodyResolveTransformer(transformer) {
|
||||
private var primaryConstructorParametersScope: FirLocalScope? = null
|
||||
|
||||
private var containingClass: FirRegularClass? = null
|
||||
@@ -66,7 +66,7 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T> withTypeParametersOf(declaration: FirMemberDeclaration, crossinline l: () -> T): T {
|
||||
protected inline fun <T> withTypeParametersOf(declaration: FirMemberDeclaration, crossinline l: () -> T): T {
|
||||
if (declaration.typeParameters.isEmpty()) return l()
|
||||
|
||||
for (typeParameter in declaration.typeParameters) {
|
||||
@@ -670,7 +670,7 @@ class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransformer)
|
||||
return this
|
||||
}
|
||||
|
||||
private inline fun <T> withLabelAndReceiverType(
|
||||
protected inline fun <T> withLabelAndReceiverType(
|
||||
labelName: Name?,
|
||||
owner: FirDeclaration,
|
||||
type: ConeKotlinType?,
|
||||
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.resolve.transformers.contracts
|
||||
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
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.FirContractDescriptionOwner
|
||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
|
||||
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
|
||||
|
||||
class ConeEffectExtractor(
|
||||
private val session: FirSession,
|
||||
private val owner: FirContractDescriptionOwner,
|
||||
private val valueParameters: List<FirValueParameter>
|
||||
) : FirDefaultVisitor<ConeContractDescriptionElement?, Nothing?>() {
|
||||
companion object {
|
||||
private val BOOLEAN_AND = FirContractsDslNames.id("kotlin", "Boolean", "and")
|
||||
private val BOOLEAN_OR = FirContractsDslNames.id("kotlin", "Boolean", "or")
|
||||
private val BOOLEAN_NOT = FirContractsDslNames.id("kotlin", "Boolean", "not")
|
||||
}
|
||||
|
||||
override fun visitElement(element: FirElement, data: Nothing?): ConeContractDescriptionElement? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun visitReturnExpression(returnExpression: FirReturnExpression, data: Nothing?): ConeContractDescriptionElement? {
|
||||
return returnExpression.result.accept(this, data)
|
||||
}
|
||||
|
||||
override fun visitFunctionCall(functionCall: FirFunctionCall, data: Nothing?): ConeContractDescriptionElement? {
|
||||
val resolvedId = functionCall.toResolvedCallableSymbol()?.callableId ?: return null
|
||||
return when (resolvedId) {
|
||||
FirContractsDslNames.IMPLIES -> {
|
||||
val effect = functionCall.explicitReceiver?.accept(this, null) as? ConeEffectDeclaration
|
||||
?: return null
|
||||
val condition = functionCall.argument.accept(this, null) as? ConeBooleanExpression
|
||||
?: return null
|
||||
ConeConditionalEffectDeclaration(effect, condition)
|
||||
}
|
||||
|
||||
FirContractsDslNames.RETURNS -> {
|
||||
val argument = functionCall.arguments.firstOrNull()
|
||||
val value = if (argument == null) {
|
||||
ConeConstantReference.WILDCARD
|
||||
} else {
|
||||
argument.accept(this, null) as? ConeConstantReference
|
||||
?: return null
|
||||
}
|
||||
ConeReturnsEffectDeclaration(value)
|
||||
}
|
||||
|
||||
FirContractsDslNames.RETURNS_NOT_NULL -> {
|
||||
ConeReturnsEffectDeclaration(ConeConstantReference.NULL)
|
||||
}
|
||||
|
||||
FirContractsDslNames.CALLS_IN_PLACE -> {
|
||||
val reference = functionCall.arguments[0].accept(this, null) as? ConeValueParameterReference
|
||||
?: return null
|
||||
val kind = functionCall.arguments.getOrNull(1)?.parseInvocationKind() ?: InvocationKind.UNKNOWN
|
||||
ConeCallsEffectDeclaration(reference, kind)
|
||||
}
|
||||
|
||||
BOOLEAN_AND, BOOLEAN_OR -> {
|
||||
val left = functionCall.explicitReceiver?.accept(this, null) as? ConeBooleanExpression ?: return null
|
||||
val right = functionCall.argument.accept(this, null) as? ConeBooleanExpression ?: return null
|
||||
val kind = when (resolvedId) {
|
||||
BOOLEAN_AND -> LogicOperationKind.AND
|
||||
BOOLEAN_OR -> LogicOperationKind.OR
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
ConeBinaryLogicExpression(left, right, kind)
|
||||
}
|
||||
|
||||
BOOLEAN_NOT -> {
|
||||
val arg = functionCall.explicitReceiver?.accept(this, null) as? ConeBooleanExpression ?: return null
|
||||
ConeLogicalNot(arg)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitBinaryLogicExpression(
|
||||
binaryLogicExpression: FirBinaryLogicExpression,
|
||||
data: Nothing?
|
||||
): ConeContractDescriptionElement? {
|
||||
val left = binaryLogicExpression.leftOperand.accept(this, null) as? ConeBooleanExpression ?: return null
|
||||
val right = binaryLogicExpression.rightOperand.accept(this, null) as? ConeBooleanExpression ?: return null
|
||||
return ConeBinaryLogicExpression(left, right, binaryLogicExpression.kind)
|
||||
}
|
||||
|
||||
override fun visitOperatorCall(operatorCall: FirOperatorCall, data: Nothing?): ConeContractDescriptionElement? {
|
||||
val isNegated = when (operatorCall.operation) {
|
||||
FirOperation.EQ -> false
|
||||
FirOperation.NOT_EQ -> true
|
||||
else -> return null
|
||||
}
|
||||
val const = operatorCall.arguments[1] as? FirConstExpression<*> ?: return null
|
||||
if (const.kind != FirConstKind.Null) return null
|
||||
val arg = operatorCall.arguments[0].accept(this, null) as? ConeValueParameterReference ?: return null
|
||||
return ConeIsNullPredicate(arg, isNegated)
|
||||
}
|
||||
|
||||
override fun visitQualifiedAccessExpression(
|
||||
qualifiedAccessExpression: FirQualifiedAccessExpression,
|
||||
data: Nothing?
|
||||
): ConeContractDescriptionElement? {
|
||||
val symbol = qualifiedAccessExpression.toResolvedCallableSymbol() ?: return null
|
||||
val parameter = symbol.fir as? FirValueParameter ?: return null
|
||||
val index = valueParameters.indexOf(parameter).takeUnless { it < 0 } ?: return null
|
||||
val type = parameter.returnTypeRef.coneTypeUnsafe<ConeKotlinType>()
|
||||
|
||||
val name = parameter.name.asString()
|
||||
return toValueParameterReference(type, index, name)
|
||||
}
|
||||
|
||||
private fun toValueParameterReference(
|
||||
type: ConeKotlinType,
|
||||
index: Int,
|
||||
name: String
|
||||
): ConeValueParameterReference {
|
||||
return if (type == session.builtinTypes.booleanType.type) {
|
||||
ConeBooleanValueParameterReference(index, name)
|
||||
} else {
|
||||
ConeValueParameterReference(index, name)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitThisReceiverExpression(
|
||||
thisReceiverExpression: FirThisReceiverExpression,
|
||||
data: Nothing?
|
||||
): ConeContractDescriptionElement? {
|
||||
val declaration = thisReceiverExpression.calleeReference.boundSymbol?.fir ?: return null
|
||||
return if (declaration == owner) {
|
||||
val type = thisReceiverExpression.typeRef.coneTypeSafe<ConeKotlinType>() ?: return null
|
||||
toValueParameterReference(type, -1, "this")
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T> visitConstExpression(constExpression: FirConstExpression<T>, data: Nothing?): ConeContractDescriptionElement? {
|
||||
return when (constExpression.kind) {
|
||||
FirConstKind.Null -> ConeConstantReference.NULL
|
||||
FirConstKind.Boolean -> when (constExpression.value as Boolean) {
|
||||
true -> ConeBooleanConstantReference.TRUE
|
||||
false -> ConeBooleanConstantReference.FALSE
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Nothing?): ConeContractDescriptionElement? {
|
||||
val arg = typeOperatorCall.argument.accept(this, data) as? ConeValueParameterReference ?: return null
|
||||
val type = typeOperatorCall.conversionTypeRef.coneTypeSafe<ConeKotlinType>() ?: return null
|
||||
val isNegated = typeOperatorCall.operation == FirOperation.NOT_IS
|
||||
return ConeIsInstancePredicate(arg, type, isNegated)
|
||||
}
|
||||
|
||||
private fun FirExpression.parseInvocationKind(): InvocationKind? {
|
||||
if (this !is FirQualifiedAccessExpression) return null
|
||||
val resolvedId = toResolvedCallableSymbol()?.callableId ?: return null
|
||||
return when (resolvedId) {
|
||||
FirContractsDslNames.EXACTLY_ONCE_KIND -> InvocationKind.EXACTLY_ONCE
|
||||
FirContractsDslNames.AT_LEAST_ONCE_KIND -> InvocationKind.AT_LEAST_ONCE
|
||||
FirContractsDslNames.AT_MOST_ONCE_KIND -> InvocationKind.AT_MOST_ONCE
|
||||
FirContractsDslNames.UNKNOWN_KIND -> InvocationKind.UNKNOWN
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.resolve.transformers.contracts
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.contracts.FirRawContractDescription
|
||||
import org.jetbrains.kotlin.fir.contracts.builder.buildResolvedContractDescription
|
||||
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.errorTypeFromPrototype
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeContractDescriptionError
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirBodyResolveTransformer
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirLocalScope
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
|
||||
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
|
||||
import org.jetbrains.kotlin.fir.visitors.compose
|
||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||
|
||||
class FirContractResolveTransformer(session: FirSession, scopeSession: ScopeSession) : FirBodyResolveTransformer(
|
||||
session,
|
||||
FirResolvePhase.CONTRACTS,
|
||||
implicitTypeOnly = false,
|
||||
scopeSession
|
||||
) {
|
||||
override val declarationsTransformer: FirDeclarationsResolveTransformer = FirDeclarationsContractResolveTransformer(this)
|
||||
|
||||
private class FirDeclarationsContractResolveTransformer(transformer: FirBodyResolveTransformer) : FirDeclarationsResolveTransformer(transformer) {
|
||||
override fun transformSimpleFunction(
|
||||
simpleFunction: FirSimpleFunction,
|
||||
data: ResolutionMode
|
||||
): CompositeTransformResult<FirSimpleFunction> {
|
||||
if (!simpleFunction.hasContractToResolve) {
|
||||
simpleFunction.replaceResolvePhase(FirResolvePhase.CONTRACTS)
|
||||
return simpleFunction.compose()
|
||||
}
|
||||
val containingDeclaration = context.containerIfAny
|
||||
if (containingDeclaration != null && containingDeclaration !is FirClass<*>) {
|
||||
simpleFunction.replaceResolvePhase(FirResolvePhase.CONTRACTS)
|
||||
simpleFunction.replaceReturnTypeRef(
|
||||
simpleFunction.returnTypeRef.errorTypeFromPrototype(
|
||||
ConeContractDescriptionError("Local function can not be used in contract description")
|
||||
)
|
||||
)
|
||||
return simpleFunction.compose()
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return withTypeParametersOf(simpleFunction) {
|
||||
val receiverTypeRef = simpleFunction.receiverTypeRef
|
||||
if (receiverTypeRef != null) {
|
||||
withLabelAndReceiverType(simpleFunction.name, simpleFunction, receiverTypeRef.coneTypeUnsafe()) {
|
||||
transformContractDescriptionOwner(simpleFunction)
|
||||
}
|
||||
} else {
|
||||
transformContractDescriptionOwner(simpleFunction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : FirContractDescriptionOwner> transformContractDescriptionOwner(
|
||||
owner: T
|
||||
): CompositeTransformResult<T> {
|
||||
dataFlowAnalyzer.enterContractDescription()
|
||||
val contractDescription = owner.contractDescription
|
||||
require(contractDescription is FirRawContractDescription)
|
||||
val valueParameters = owner.valueParameters
|
||||
val contractCall = withLocalScopeCleanup {
|
||||
addLocalScope(FirLocalScope())
|
||||
for (valueParameter in valueParameters) {
|
||||
context.storeVariable(valueParameter)
|
||||
}
|
||||
context.withContainer(owner as FirDeclaration) {
|
||||
contractDescription.contractCall.transformSingle(transformer, ResolutionMode.ContextIndependent)
|
||||
}
|
||||
}
|
||||
val resolvedId = contractCall.toResolvedCallableSymbol()?.callableId ?: return transformOwnerWithUnresolvedContract(owner)
|
||||
if (resolvedId != FirContractsDslNames.CONTRACT) return transformOwnerWithUnresolvedContract(owner)
|
||||
if (contractCall.arguments.size != 1) return transformOwnerOfErrorContract(owner)
|
||||
val argument = contractCall.argument as? FirLambdaArgumentExpression ?: return transformOwnerOfErrorContract(owner)
|
||||
val lambdaBody = (argument.expression as FirAnonymousFunction).body ?: return transformOwnerOfErrorContract(owner)
|
||||
|
||||
val resolvedContractDescription = buildResolvedContractDescription {
|
||||
val effectExtractor = ConeEffectExtractor(session, owner, valueParameters)
|
||||
for (statement in lambdaBody.statements) {
|
||||
val effect = statement.accept(effectExtractor, null) as? ConeEffectDeclaration
|
||||
if (effect == null) {
|
||||
unresolvedEffects += statement
|
||||
} else {
|
||||
effects += effect
|
||||
}
|
||||
}
|
||||
}
|
||||
owner.replaceContractDescription(resolvedContractDescription)
|
||||
dataFlowAnalyzer.exitContractDescription()
|
||||
return owner.compose()
|
||||
}
|
||||
|
||||
private fun <T : FirContractDescriptionOwner> transformOwnerWithUnresolvedContract(owner: T): CompositeTransformResult<T> {
|
||||
val contractDescription = owner.contractDescription as FirRawContractDescription
|
||||
val functionCall = contractDescription.contractCall
|
||||
owner.replaceContractDescription(FirEmptyContractDescription)
|
||||
owner.body.replaceFirstStatement(functionCall)
|
||||
dataFlowAnalyzer.exitContractDescription()
|
||||
return owner.compose()
|
||||
}
|
||||
|
||||
private fun <T : FirContractDescriptionOwner> transformOwnerOfErrorContract(owner: T): CompositeTransformResult<T> {
|
||||
// TODO
|
||||
dataFlowAnalyzer.exitContractDescription()
|
||||
return owner.compose()
|
||||
}
|
||||
|
||||
private val FirContractDescriptionOwner.hasContractToResolve: Boolean
|
||||
get() = contractDescription is FirRawContractDescription
|
||||
}
|
||||
}
|
||||
|
||||
private val FirContractDescriptionOwner.valueParameters: List<FirValueParameter>
|
||||
get() = when (this) {
|
||||
is FirSimpleFunction -> valueParameters
|
||||
is FirPropertyAccessor -> valueParameters
|
||||
else -> error()
|
||||
}
|
||||
|
||||
private val FirContractDescriptionOwner.body: FirBlock
|
||||
get() = when (this) {
|
||||
is FirSimpleFunction -> body!!
|
||||
is FirPropertyAccessor -> body!!
|
||||
else -> error()
|
||||
}
|
||||
|
||||
private fun FirContractDescriptionOwner.error(): Nothing = throw IllegalStateException("${this::class} can not be a contract owner")
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.resolve.transformers.contracts
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.AdapterForResolvePhase
|
||||
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
|
||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||
import org.jetbrains.kotlin.fir.visitors.compose
|
||||
|
||||
@AdapterForResolvePhase
|
||||
class FirContractResolveTransformerAdapter(private val scopeSession: ScopeSession) : FirTransformer<Nothing?>() {
|
||||
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
|
||||
return element.compose()
|
||||
}
|
||||
|
||||
override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirDeclaration> {
|
||||
val transformer = FirContractResolveTransformer(
|
||||
file.session,
|
||||
scopeSession
|
||||
)
|
||||
return file.transform(transformer, ResolutionMode.ContextIndependent)
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.resolve.transformers.contracts
|
||||
|
||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
object FirContractsDslNames {
|
||||
// Internal marker-annotation for distinguishing our API
|
||||
val CONTRACTS_DSL_ANNOTATION_FQN = id("kotlin.internal", "ContractsDsl")
|
||||
|
||||
// Types
|
||||
val EFFECT = id("Effect")
|
||||
val CONDITIONAL_EFFECT = id("ConditionalEffect")
|
||||
val SIMPLE_EFFECT = id("SimpleEffect")
|
||||
val RETURNS_EFFECT = id("Returns")
|
||||
val RETURNS_NOT_NULL_EFFECT = id("ReturnsNotNull")
|
||||
val CALLS_IN_PLACE_EFFECT = id("CallsInPlace")
|
||||
|
||||
// Structure-defining calls
|
||||
val CONTRACT = id("contract")
|
||||
val IMPLIES = simpleEffect("implies")
|
||||
|
||||
// Effect-declaration calls
|
||||
val RETURNS = contractBuilder("returns")
|
||||
val RETURNS_NOT_NULL = contractBuilder("returnsNotNull")
|
||||
val CALLS_IN_PLACE = contractBuilder("callsInPlace")
|
||||
|
||||
// enum class InvocationKind
|
||||
val INVOCATION_KIND_ENUM = id("InvocationKind")
|
||||
val EXACTLY_ONCE_KIND = invocationKind("EXACTLY_ONCE")
|
||||
val AT_LEAST_ONCE_KIND = invocationKind("AT_LEAST_ONCE")
|
||||
val UNKNOWN_KIND = invocationKind("UNKNOWN")
|
||||
val AT_MOST_ONCE_KIND = invocationKind("AT_MOST_ONCE")
|
||||
|
||||
private const val CONTRACT_BUILDER = "ContractBuilder"
|
||||
|
||||
private fun contractBuilder(name: String): CallableId = id(CONTRACT_PACKAGE, CONTRACT_BUILDER, name)
|
||||
private fun invocationKind(name: String): CallableId = id(CONTRACT_PACKAGE, INVOCATION_KIND_ENUM.callableName.asString(), name)
|
||||
private fun simpleEffect(name: String): CallableId = id(CONTRACT_PACKAGE, SIMPLE_EFFECT.callableName.asString(), name)
|
||||
private fun id(name: String): CallableId = id(CONTRACT_PACKAGE, name)
|
||||
private fun id(packageName: String, name: String): CallableId = id(packageName, className = null, name)
|
||||
internal fun id(packageName: String, className: String?, name: String): CallableId {
|
||||
return CallableId(
|
||||
FqName(packageName),
|
||||
className?.let { FqName(it) },
|
||||
Name.identifier(name)
|
||||
)
|
||||
}
|
||||
|
||||
private const val CONTRACT_PACKAGE = "kotlin.contracts"
|
||||
}
|
||||
@@ -12,6 +12,7 @@ enum class FirResolvePhase {
|
||||
SEALED_CLASS_INHERITORS,
|
||||
TYPES,
|
||||
STATUS,
|
||||
CONTRACTS,
|
||||
IMPLICIT_TYPES_BODY_RESOLVE,
|
||||
BODY_RESOLVE;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user