Do not use DefaultBuiltIns in contracts

Default built-ins represent built-ins loaded from the compiler jar via
class loader, and they may not be equivalent to the built-ins present in
the standard library that is used in compilation dependencies, in case
the compiler and stdlib versions do not match. Use built-ins from the
given module instead.

This commit deals with more-or-less obvious usages of DefaultBuiltIns;
next commits refactor the ESConstant values and related code to support
injected built-ins
This commit is contained in:
Alexander Udalov
2018-12-21 16:35:59 +01:00
parent 7561502956
commit 9516d6e89b
7 changed files with 77 additions and 60 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.contracts.description
import org.jetbrains.kotlin.contracts.interpretation.ContractInterpretationDispatcher
import org.jetbrains.kotlin.contracts.model.Functor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.storage.StorageManager
/**
@@ -38,12 +39,11 @@ open class ContractDescription(
val ownerFunction: FunctionDescriptor,
storageManager: StorageManager
) {
private val functorLazyValue = storageManager.createNullableLazyValue {
ContractInterpretationDispatcher().convertContractDescriptorToFunctor(this)
private val computeFunctor = storageManager.createMemoizedFunctionWithNullableValues<ModuleDescriptor, Functor> { module ->
ContractInterpretationDispatcher(module).convertContractDescriptorToFunctor(this)
}
val functor: Functor?
get() = functorLazyValue()
fun getFunctor(usageModule: ModuleDescriptor): Functor? = computeFunctor(usageModule)
}
interface ContractDescriptionElement {
@@ -58,4 +58,4 @@ interface EffectDeclaration : ContractDescriptionElement {
interface BooleanExpression : ContractDescriptionElement {
override fun <R, D> accept(contractDescriptionVisitor: ContractDescriptionVisitor<R, D>, data: D): R =
contractDescriptionVisitor.visitBooleanExpression(this, data)
}
}
@@ -16,21 +16,24 @@
package org.jetbrains.kotlin.contracts.interpretation
import org.jetbrains.kotlin.contracts.description.*
import org.jetbrains.kotlin.contracts.description.BooleanExpression
import org.jetbrains.kotlin.contracts.description.ConditionalEffectDeclaration
import org.jetbrains.kotlin.contracts.description.ContractDescription
import org.jetbrains.kotlin.contracts.description.EffectDeclaration
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
import org.jetbrains.kotlin.contracts.description.expressions.VariableReference
import org.jetbrains.kotlin.contracts.model.functors.SubstitutingFunctor
import org.jetbrains.kotlin.contracts.model.structure.ESConstant
import org.jetbrains.kotlin.contracts.model.structure.ESVariable
import org.jetbrains.kotlin.contracts.model.ESEffect
import org.jetbrains.kotlin.contracts.model.ESExpression
import org.jetbrains.kotlin.contracts.model.Functor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.contracts.model.functors.SubstitutingFunctor
import org.jetbrains.kotlin.contracts.model.structure.ESConstant
import org.jetbrains.kotlin.contracts.model.structure.ESVariable
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
/**
* This class manages conversion of [ContractDescription] to [Functor]
*/
class ContractInterpretationDispatcher {
class ContractInterpretationDispatcher(internal val module: ModuleDescriptor) {
private val constantsInterpreter = ConstantValuesInterpreter()
private val conditionInterpreter = ConditionInterpreter(this)
private val conditionalEffectInterpreter = ConditionalEffectInterpreter(this)
@@ -48,7 +51,7 @@ class ContractInterpretationDispatcher {
}
}
return SubstitutingFunctor(resultingClauses, contractDescription.ownerFunction)
return SubstitutingFunctor(resultingClauses, contractDescription.ownerFunction, module.builtIns)
}
internal fun interpretEffect(effectDeclaration: EffectDeclaration): ESEffect? {
@@ -63,4 +66,4 @@ class ContractInterpretationDispatcher {
booleanExpression.accept(conditionInterpreter, Unit)
internal fun interpretVariable(variableReference: VariableReference): ESVariable? = ESVariable(variableReference.descriptor)
}
}
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.contracts.model.functors
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.contracts.model.*
import org.jetbrains.kotlin.contracts.model.structure.*
import org.jetbrains.kotlin.contracts.model.visitors.Substitutor
@@ -23,8 +24,11 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueDescriptor
import org.jetbrains.kotlin.utils.addIfNotNull
class SubstitutingFunctor(private val basicEffects: List<ESEffect>, private val ownerFunction: FunctionDescriptor) :
AbstractReducingFunctor() {
class SubstitutingFunctor(
private val basicEffects: List<ESEffect>,
private val ownerFunction: FunctionDescriptor,
private val builtIns: KotlinBuiltIns
) : AbstractReducingFunctor() {
override fun doInvocation(arguments: List<Computation>): List<ESEffect> {
if (basicEffects.isEmpty()) return emptyList()
@@ -37,7 +41,7 @@ class SubstitutingFunctor(private val basicEffects: List<ESEffect>, private val
}
val substitutions = parameters.zip(arguments).toMap()
val substitutor = Substitutor(substitutions)
val substitutor = Substitutor(substitutions, builtIns)
val substitutedClauses = mutableListOf<ESEffect>()
effectsLoop@ for (effect in basicEffects) {
@@ -16,8 +16,10 @@
package org.jetbrains.kotlin.contracts.model.visitors
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.contracts.model.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.contracts.model.Computation
import org.jetbrains.kotlin.contracts.model.ESExpression
import org.jetbrains.kotlin.contracts.model.ESExpressionVisitor
import org.jetbrains.kotlin.contracts.model.structure.*
/**
@@ -25,36 +27,39 @@ import org.jetbrains.kotlin.contracts.model.structure.*
* and then flattens resulting tree, producing an [EffectSchema], which describes effects
* of this [ESExpression] with effects of arguments taken into consideration.
*/
class Substitutor(private val substitutions: Map<ESVariable, Computation>) : ESExpressionVisitor<Computation?> {
class Substitutor(
private val substitutions: Map<ESVariable, Computation>,
private val builtIns: KotlinBuiltIns
) : ESExpressionVisitor<Computation?> {
override fun visitIs(isOperator: ESIs): Computation? {
val arg = isOperator.left.accept(this) ?: return null
return CallComputation(DefaultBuiltIns.Instance.booleanType, isOperator.functor.invokeWithArguments(arg))
return CallComputation(builtIns.booleanType, isOperator.functor.invokeWithArguments(arg))
}
override fun visitNot(not: ESNot): Computation? {
val arg = not.arg.accept(this) ?: return null
return CallComputation(DefaultBuiltIns.Instance.booleanType, not.functor.invokeWithArguments(arg))
return CallComputation(builtIns.booleanType, not.functor.invokeWithArguments(arg))
}
override fun visitEqual(equal: ESEqual): Computation? {
val left = equal.left.accept(this) ?: return null
val right = equal.right.accept(this) ?: return null
return CallComputation(DefaultBuiltIns.Instance.booleanType, equal.functor.invokeWithArguments(listOf(left, right)))
return CallComputation(builtIns.booleanType, equal.functor.invokeWithArguments(listOf(left, right)))
}
override fun visitAnd(and: ESAnd): Computation? {
val left = and.left.accept(this) ?: return null
val right = and.right.accept(this) ?: return null
return CallComputation(DefaultBuiltIns.Instance.booleanType, and.functor.invokeWithArguments(left, right))
return CallComputation(builtIns.booleanType, and.functor.invokeWithArguments(left, right))
}
override fun visitOr(or: ESOr): Computation? {
val left = or.left.accept(this) ?: return null
val right = or.right.accept(this) ?: return null
return CallComputation(DefaultBuiltIns.Instance.booleanType, or.functor.invokeWithArguments(left, right))
return CallComputation(builtIns.booleanType, or.functor.invokeWithArguments(left, right))
}
override fun visitVariable(esVariable: ESVariable): Computation? = substitutions[esVariable] ?: esVariable
override fun visitConstant(esConstant: ESConstant): Computation? = esConstant
}
}