Skip to content

Commit

Permalink
Generic Run Analogues (#218)
Browse files Browse the repository at this point in the history
With this pull request functions analogous to Kotlin `run` successfully
compile to Viper if they're inline.
For instance, call to
```inline fun <T, R> T.copiedRun(block: T.() -> R): R = block()```
will be correctly inlined.

Support for real stdlib `run` and other notable extension functions (`also`, `let` etc.) as well as the cases with multiple possible receivers will be implemented in future PRs.
  • Loading branch information
GrigoriiSolnyshkin authored Jul 15, 2024
1 parent 58b64db commit 85731bf
Show file tree
Hide file tree
Showing 16 changed files with 685 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,14 @@ val FirPropertySymbol.isCustom: Boolean
}

val FirFunctionCall.functionCallArguments: List<FirExpression>
get() = listOfNotNull(dispatchReceiver) + argumentList.arguments
get() {
val receiverArg = when {
dispatchReceiver != null -> dispatchReceiver
extensionReceiver != null -> extensionReceiver
else -> null
}
return listOfNotNull(receiverArg) + argumentList.arguments
}
val FirFunctionSymbol<*>.effects: List<FirEffectDeclaration>
get() = this.resolvedContractDescription?.effects ?: emptyList()
val KtSourceElement?.asPosition: Position
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ interface MethodConversionContext : ProgramConversionContext {
fun resolveLocal(name: Name): VariableEmbedding
fun registerLocalProperty(symbol: FirPropertySymbol)
fun registerLocalVariable(symbol: FirVariableSymbol<*>)
fun resolveReceiver(): ExpEmbedding?

fun <R> withScopeImpl(scopeDepth: Int, action: () -> R): R
fun addLoopIdentifier(labelName: String, index: Int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class MethodConverter(
paramResolver.tryResolveParameter(name) ?: parent?.resolveParameter(name)
?: throw IllegalArgumentException("Parameter $name not found in scope.")

override fun resolveReceiver(): ExpEmbedding? = paramResolver.tryResolveReceiver() ?: parent?.resolveReceiver()

override val defaultResolvedReturnTarget = paramResolver.defaultResolvedReturnTarget
override fun resolveNamedReturnTarget(sourceName: String): ReturnTarget? {
return paramResolver.resolveNamedReturnTarget(sourceName) ?: parent?.resolveNamedReturnTarget(sourceName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.formver.embeddings.callables.FunctionSignature
import org.jetbrains.kotlin.formver.embeddings.expression.ExpEmbedding
import org.jetbrains.kotlin.formver.names.embedParameterName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.utils.addToStdlib.ifTrue

/**
Expand All @@ -18,6 +19,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.ifTrue
*/
interface ParameterResolver {
fun tryResolveParameter(name: Name): ExpEmbedding?
fun tryResolveReceiver(): ExpEmbedding?

val sourceName: String?
val defaultResolvedReturnTarget: ReturnTarget
Expand All @@ -33,7 +35,9 @@ class RootParameterResolver(
override val defaultResolvedReturnTarget: ReturnTarget,
) : ParameterResolver {
private val parameters = signature.params.associateBy { it.name }
private val receiver = signature.receiver
override fun tryResolveParameter(name: Name): ExpEmbedding? = parameters[name.embedParameterName()]
override fun tryResolveReceiver() = receiver
}

class InlineParameterResolver(
Expand All @@ -42,4 +46,5 @@ class InlineParameterResolver(
override val defaultResolvedReturnTarget: ReturnTarget,
) : ParameterResolver {
override fun tryResolveParameter(name: Name): ExpEmbedding? = substitutions[name]
override fun tryResolveReceiver(): ExpEmbedding? = substitutions[SpecialNames.THIS]
}
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ class ProgramConverter(val session: FirSession, override val config: PluginConfi
this,
signature,
RootParameterResolver(this, signature, signature.sourceName, returnTarget),
scopeIndexProducer.getFresh(),
scopeDepth = scopeIndexProducer.getFresh(),
)
val stmtCtx = StmtConverter(methodCtx)
val body = stmtCtx.convert(firBody)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ fun StmtConversionContext.embedPropertyAccess(accessExpression: FirPropertyAcces
else -> embedLocalProperty(calleeSymbol)
}
}
else -> error("Property access symbol $calleeSymbol has unsupported type.")
else ->
error("Property access symbol $calleeSymbol has unsupported type.")
}

fun StmtConversionContext.getInlineFunctionCallArgs(
Expand Down Expand Up @@ -165,7 +166,7 @@ fun StmtConversionContext.insertInlineFunctionCall(
val methodCtxFactory = MethodContextFactory(
calleeSignature,
InlineParameterResolver(subs, returnTargetName, returnTarget),
parentCtx,
parent = parentCtx,
)
return withMethodCtx(methodCtxFactory) {
Block(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ package org.jetbrains.kotlin.formver.conversion
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.evaluateAs
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition
import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.references.toResolvedSymbol
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
Expand All @@ -23,6 +26,7 @@ import org.jetbrains.kotlin.formver.embeddings.callables.FullNamedFunctionSignat
import org.jetbrains.kotlin.formver.embeddings.callables.insertCall
import org.jetbrains.kotlin.formver.embeddings.expression.*
import org.jetbrains.kotlin.formver.functionCallArguments
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.text
import org.jetbrains.kotlin.types.ConstantValueKind

Expand Down Expand Up @@ -70,6 +74,13 @@ object StmtConversionVisitor : FirVisitor<ExpEmbedding, StmtConversionContext>()
else -> handleUnimplementedElement("Constant Expression of type ${constExpression.kind} is not yet implemented.", data)
}

override fun visitIntegerLiteralOperatorCall(
integerLiteralOperatorCall: FirIntegerLiteralOperatorCall,
data: StmtConversionContext
): ExpEmbedding {
return visitFunctionCall(integerLiteralOperatorCall, data)
}

override fun visitWhenSubjectExpression(
whenSubjectExpression: FirWhenSubjectExpression,
data: StmtConversionContext,
Expand Down Expand Up @@ -271,9 +282,10 @@ object StmtConversionVisitor : FirVisitor<ExpEmbedding, StmtConversionContext>()
override fun visitThisReceiverExpression(
thisReceiverExpression: FirThisReceiverExpression,
data: StmtConversionContext,
): ExpEmbedding =
data.signature.receiver
): ExpEmbedding {
return data.resolveReceiver()
?: throw IllegalArgumentException("Can't resolve the 'this' receiver since the function does not have one.")
}

override fun visitTypeOperatorCall(
typeOperatorCall: FirTypeOperatorCall,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.formver.embeddings.callables

import org.jetbrains.kotlin.formver.conversion.StmtConversionContext
import org.jetbrains.kotlin.formver.embeddings.expression.ExpEmbedding
import org.jetbrains.kotlin.formver.embeddings.expression.withType

/**
* Kotlin entity that can be called.
Expand All @@ -21,9 +20,4 @@ interface CallableEmbedding : CallableSignature {
fun CallableEmbedding.insertCall(
args: List<ExpEmbedding>,
ctx: StmtConversionContext,
): ExpEmbedding {
return args.zip(formalArgTypes)
.map { (arg, type) -> arg.withType(type) }
.let { insertCallImpl(it, ctx) }
.withType(returnType)
}
): ExpEmbedding = insertCallImpl(args, ctx)
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.formver.conversion.StmtConversionContext
import org.jetbrains.kotlin.formver.conversion.insertInlineFunctionCall
import org.jetbrains.kotlin.formver.embeddings.TypeEmbedding
import org.jetbrains.kotlin.formver.embeddings.expression.ExpEmbedding
import org.jetbrains.kotlin.name.SpecialNames

class InlineNamedFunction(
val signature: FullNamedFunctionSignature,
Expand All @@ -21,7 +22,7 @@ class InlineNamedFunction(
args: List<ExpEmbedding>,
ctx: StmtConversionContext,
): ExpEmbedding {
val paramNames = symbol.valueParameterSymbols.map { it.name }
val paramNames = listOfNotNull(receiver?.let { SpecialNames.THIS }) + symbol.valueParameterSymbols.map { it.name }
return ctx.insertInlineFunctionCall(signature, paramNames, args, firBody, signature.sourceName)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,28 +117,6 @@ object KotlinBooleanNotFunctionImplementation : KotlinBooleanSpecialFunction() {
Not(args[0])
}

object KotlinRunSpecialFunction : SpecialKotlinFunction {
override val packageName: List<String> = listOf("kotlin")
override val name: String = "run"

override val receiverType: TypeEmbedding? = null
override val paramTypes: List<TypeEmbedding> =
listOf(FunctionTypeEmbedding(CallableSignatureData(null, emptyList(), NullableTypeEmbedding(AnyTypeEmbedding))))
override val returnType: TypeEmbedding = NullableTypeEmbedding(AnyTypeEmbedding)

override fun insertCallImpl(
args: List<ExpEmbedding>,
ctx: StmtConversionContext,
): ExpEmbedding {
val lambda = when (val arg = args[0].ignoringCastsAndMetaNodes()) {
is LambdaExp -> arg
else -> error("kotlin.run must be called with a lambda argument at the moment")
}

return lambda.insertCallImpl(listOf(), ctx)
}
}

/**
* Represents the `verify` function defined in `org.jetbrains.kotlin.formver.plugin`.
*/
Expand All @@ -164,6 +142,5 @@ object SpecialKotlinFunctions {
KotlinIntTimesFunctionImplementation,
KotlinIntDivFunctionImplementation,
KotlinBooleanNotFunctionImplementation,
KotlinRunSpecialFunction,
).associateBy { it.embedName() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.formver.embeddings.callables.asData
import org.jetbrains.kotlin.formver.embeddings.expression.debug.PlaintextLeaf
import org.jetbrains.kotlin.formver.embeddings.expression.debug.TreeView
import org.jetbrains.kotlin.formver.linearization.LinearizationContext
import org.jetbrains.kotlin.name.SpecialNames

class LambdaExp(
val signature: FunctionSignature,
Expand All @@ -34,9 +35,17 @@ class LambdaExp(
args: List<ExpEmbedding>,
ctx: StmtConversionContext,
): ExpEmbedding {
val inlineBody = function.body ?: throw IllegalArgumentException("Lambda $function has a null body")
val paramNames = function.valueParameters.map { it.name }
return ctx.insertInlineFunctionCall(signature, paramNames, args, inlineBody, ctx.signature.sourceName, parentCtx)
val inlineBody = function.body ?: throw IllegalArgumentException("Lambda $function has a null body.")
val nonReceiverParamNames = function.valueParameters.map { it.name }
val receiverParamNames = if (function.receiverParameter != null) listOf(SpecialNames.THIS) else emptyList()
return ctx.insertInlineFunctionCall(
signature,
receiverParamNames + nonReceiverParamNames,
args,
inlineBody,
ctx.signature.sourceName,
parentCtx,
)
}

override val debugTreeView: TreeView
Expand Down
Loading

0 comments on commit 85731bf

Please sign in to comment.