diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/FirAbstractProofComponent.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/FirAbstractProofComponent.kt index f4782d7..93ac024 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/FirAbstractProofComponent.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/FirAbstractProofComponent.kt @@ -48,6 +48,16 @@ internal interface FirAbstractProofComponent { val session: FirSession + /** + * (FIR API review and comments) + * + * I can see how providing a list of predicates to search up front + * can be efficient to avoid traversing the tree searching for declarations. + * In this case though we are forcing the user to always use or look for annotated declarations. + * + * Ideally predicates are more expressive than just searching for annotations and allow the user to + * search for any of the parts in the signature declaration. + */ val contextPredicate: DeclarationPredicate get() = has(ProofAnnotationsFqName.ContextAnnotation) diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/FirArrowInjectExtensionRegistrar.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/FirArrowInjectExtensionRegistrar.kt index a49d528..45b7b3c 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/FirArrowInjectExtensionRegistrar.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/FirArrowInjectExtensionRegistrar.kt @@ -18,6 +18,16 @@ public class FirArrowInjectExtensionRegistrar( private val proofCache: ProofCache, ) : FirExtensionRegistrar() { + /** + * (FIR API review and comments) + * + * The `+` operator makes for a nice API in simple plugins whose extensions + * only take a `session` as single argument but becomes more unreadable when like in our + * case we need to pass extra arguments to the constructor. + * + * An alternative could be that our `proofCache` argument becomes a session component. + * We tried that but then in the IR generation part we don't have access to the session anymore. + */ override fun ExtensionRegistrarContext.configurePlugin() { +{ session: FirSession -> ContextProvidersResolutionExtension(proofCache, session) } +{ session: FirSession -> ResolvedFunctionGenerationExtension(proofCache, session) } diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/FirProofIdSignature.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/FirProofIdSignature.kt index 34dbcbe..3dbe784 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/FirProofIdSignature.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/FirProofIdSignature.kt @@ -7,6 +7,14 @@ import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.signaturer.FirBasedSignatureComposer import org.jetbrains.kotlin.ir.util.IdSignature +/** + * (FIR API review and comments) + * + * We tried getting signatures or the composer from the FIR session + * but could not find a better way, so we create our own here. + * The generated signatures are used to look up declarations in backend IR related + * to the providers found suitable for resolved calls + */ interface FirProofIdSignature { val session: FirSession diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/ProofKey.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/ProofKey.kt index 6e1b15d..1869184 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/ProofKey.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/ProofKey.kt @@ -2,4 +2,13 @@ package arrow.inject.compiler.plugin.fir import org.jetbrains.kotlin.fir.declarations.FirPluginKey +/** + * (FIR API review and comments) + * + * # what's the plugin key for? + * + * We have used this one across the frontend to set the `origin` + * property in nodes, but we are not sure what good it does or if + * it's needed at all + */ internal object ProofKey : FirPluginKey() diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/codegen/ResolvedFunctionGenerationExtension.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/codegen/ResolvedFunctionGenerationExtension.kt index f0c071e..18d8d05 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/codegen/ResolvedFunctionGenerationExtension.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/codegen/ResolvedFunctionGenerationExtension.kt @@ -163,7 +163,14 @@ internal class ResolvedFunctionGenerationExtension( valueParameters += buildValueParameters(firNamedFunctionSymbol) + unambiguousUnitValueParameter() } - .apply { this.originalForSubstitutionOverrideAttr = firNamedFunctionSymbol.fir } + .apply { + /** + * (FIR API review and comments) + * We have run into runtime errors a few time when compiling because some of the properties + * in the FIR and IR builders have `lateinit var` that are not required on construction. + */ + this.originalForSubstitutionOverrideAttr = firNamedFunctionSymbol.fir + } .symbol } diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/collectors/ExternalProofCollector.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/collectors/ExternalProofCollector.kt index 37bc01a..9d8d6d4 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/collectors/ExternalProofCollector.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/collectors/ExternalProofCollector.kt @@ -23,6 +23,19 @@ import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +/** + * (FIR API review and comments) + * + * In this service we had to resort to a very costly and slow process which + * involves scanning the classpath. For that we had to use the classgraph library. + * Seems like all FIR apis are geared toward searching and finding things in the same module + * but there is no way to search for declarations in third party jar packages. + * Since our plugin exports declaration as providers that ultimately get compiled in a jar we are + * in need of scanning the classpath. + * + * A suggested alternative was to codegen a known file on each jar that pointed to the + * declarations but we did not want to add additional resources or binaries to it. + */ internal class ExternalProofCollector( override val session: FirSession, ) : FirAbstractProofComponent, FirProofIdSignature { diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/errors/FirMetaErrorsDefaultMessages.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/errors/FirMetaErrorsDefaultMessages.kt index 55b76fb..8f02c10 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/errors/FirMetaErrorsDefaultMessages.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/errors/FirMetaErrorsDefaultMessages.kt @@ -10,6 +10,13 @@ import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory import org.jetbrains.kotlin.diagnostics.rendering.Renderer import org.jetbrains.kotlin.types.model.KotlinTypeMarker +/** + * (FIR API review and comments) + * + * This API regarding how errors are managed and subscribed seems to be + * bringing patterns, name and conventions from the IDEA open API java counterpart. + * Ideally users can just register or report these in the checkers or places where they get used. + */ internal object FirMetaErrorsDefaultMessages : BaseDiagnosticRendererFactory() { override val MAP: KtDiagnosticFactoryToRendererMap = diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/resolution/ProofResolutionCheckerExtension.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/resolution/ProofResolutionCheckerExtension.kt index b0d6905..082e3d5 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/resolution/ProofResolutionCheckerExtension.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/resolution/ProofResolutionCheckerExtension.kt @@ -19,6 +19,13 @@ import org.jetbrains.kotlin.fir.expressions.FirCall import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar import org.jetbrains.kotlin.fir.types.FirTypeRef +/** + * (FIR API review and comments) + * + * We abstracted away access to this service because + * the creation of instances and nesting of objects was too much boilerplate + * when registering each service + */ internal class ProofResolutionCheckerExtension( session: FirSession, declarationCheckers: List, diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/resolution/contexts/ContextProvidersResolutionExtension.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/resolution/contexts/ContextProvidersResolutionExtension.kt index d001209..dfd7077 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/resolution/contexts/ContextProvidersResolutionExtension.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/resolution/contexts/ContextProvidersResolutionExtension.kt @@ -6,10 +6,16 @@ import arrow.inject.compiler.plugin.model.Proof import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.extensions.FirExpressionResolutionExtension +import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.fir.resolvedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.FirTypeProjection import org.jetbrains.kotlin.fir.types.toConeTypeProjection import org.jetbrains.kotlin.fir.types.type +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name internal class ContextProvidersResolutionExtension( override val proofCache: ProofCache, @@ -18,6 +24,14 @@ internal class ContextProvidersResolutionExtension( override val allProofs: List by lazy { allCollectedProofs } + /** + * (FIR API review and comments) + * + * This extension is great and takes care of all the scoping resolution issues we have in the frontend. + * Something odd we detected is that for calls that you return additional types then in backend IR the + * expression will show up as an error expression that requires substitution. + * See [arrow.inject.compiler.plugin.ir.ProofsIrContextReceiversRecCodegen.replaceErrorExpressionsWithReceiverValues] + */ override fun addNewImplicitReceivers(functionCall: FirFunctionCall): List { // TODO add resolve proof here instead and iterate over all contexts in the proof return functionCall.contextSyntheticFunctionTypeArguments.mapNotNull { @@ -27,7 +41,12 @@ internal class ContextProvidersResolutionExtension( // TODO: Improve this equality private val FirFunctionCall.isCallToContextSyntheticFunction: Boolean - get() = calleeReference.name.asString() == "context" + get() = + (calleeReference as? FirResolvedNamedReference)?.let { + (it.resolvedSymbol as? FirNamedFunctionSymbol)?.let { + it.callableId == CallableId(FqName("arrow.inject.annotations"), Name.identifier("with")) + } + } ?: false private val FirFunctionCall.contextSyntheticFunctionTypeArguments: List get() = if (isCallToContextSyntheticFunction) typeArguments else emptyList() diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/resolution/resolver/ProofResolutionStageRunner.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/resolution/resolver/ProofResolutionStageRunner.kt index b99c888..ffa7d2d 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/resolution/resolver/ProofResolutionStageRunner.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/fir/resolution/resolver/ProofResolutionStageRunner.kt @@ -57,6 +57,15 @@ import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability import org.jetbrains.kotlin.types.Variance +/** + * (FIR API review and comments) + * + * This entire service has been ported more or less from the compiler + * FIR call resolver and similar services. + * This was done so that we could resolve a fir call given a list of + * provider candidates. At the time we are writing this FIR does not + * expose any public service or hook for call resolution. + */ internal class ProofResolutionStageRunner( override val session: FirSession, private val firResolutionProof: FirResolutionProof, diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/ir/ProofsIrAbstractCodegen.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/ir/ProofsIrAbstractCodegen.kt index bc75666..0f08acd 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/ir/ProofsIrAbstractCodegen.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/ir/ProofsIrAbstractCodegen.kt @@ -89,6 +89,13 @@ interface ProofsIrAbstractCodegen : IrPluginContext, TypeSystemContext { .filterNotNull() } + /** + * (FIR API review and comments) + * + * Looked around for an api that would allow me to generate a call given a + * declaration and fill in all the boilerplate but could not find one. + * Here based on the type of declaration we create an appropriate call for it. + */ fun IrDeclaration.irCall(): IrExpression = when (this) { is IrProperty -> { diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/ir/ProofsIrContextReceiversRecCodegen.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/ir/ProofsIrContextReceiversRecCodegen.kt index 7d82640..ece95d8 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/ir/ProofsIrContextReceiversRecCodegen.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/ir/ProofsIrContextReceiversRecCodegen.kt @@ -7,6 +7,7 @@ import arrow.inject.compiler.plugin.model.ProofResolution import arrow.inject.compiler.plugin.model.asProofCacheKey import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder +import org.jetbrains.kotlin.backend.jvm.functionByName import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration @@ -41,7 +42,9 @@ import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol +import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrTypeArgument @@ -53,17 +56,33 @@ import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.types.typeWith import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.dumpKotlinLike +import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable +import org.jetbrains.kotlin.ir.util.functions +import org.jetbrains.kotlin.ir.util.referenceFunction import org.jetbrains.kotlin.ir.util.statements import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.model.TypeArgumentMarker import org.jetbrains.kotlin.types.model.TypeSystemContext +/** + * (FIR API review and comments) + * + * When having FIR enabled the IrPluginContext does not + * allow to call any of the `referenceFunction` or similar methods + * available in the plugin context. + * + * Referencing functions and symbols with the `symbolTable` requires building + * a custom id signature. We found symbols such as `kotlin.with` which in the + * previous backend returned `kotlin.StandardKt.with`, in this impl returns just + * `kotlin.with` with a package fragment as parent instead of the generated multi file + * class expected in the intrinsics. Using such references results in codegen failure. + * This is currently a blocker in the project as we have not been able to properly + * reference `kotlin.with` from backend IR when the FIR impl is underneath. + */ internal class ProofsIrContextReceiversRecCodegen( override val proofCache: ProofCache, override val moduleFragment: IrModuleFragment, @@ -121,62 +140,122 @@ internal class ProofsIrContextReceiversRecCodegen( steps.isEmpty() -> body //done processing else -> { val currentStep = steps.first() - val returningBlockType = declarationParent.returningBlockType() - currentStep.replacementCall.addReplacedTypeArguments(currentStep.type, returningBlockType) val paramSymbol = IrValueParameterSymbolImpl() - if (previousStepLambda != null) { - val returned = previousStepLambda.function.createIrReturn(currentStep.replacementCall) - val previousLambdaBody = (previousStepLambda.function.body as? IrBlockBody) - previousLambdaBody?.statements?.add(returned) + val nestedLambda = currentStep.createLambdaAndArguments(declarationParent, paramSymbol) + if (previousStepLambda != null || steps.size == 1) { + currentStep.addContextualCallReturn(previousStepLambda ?: nestedLambda) } - val nestedLambda = - createLambdaExpressionWithoutParent(currentStep.type, returningBlockType, paramSymbol) { - blockBody { - } - } - nestedLambda.function.parent = declarationParent - val extensionReceiverParam = nestedLambda.function.extensionReceiverParameter - if (extensionReceiverParam != null) - processContextReceiver(0, currentStep.type, currentStep.replacementCall, previousStepLambda?.function?.extensionReceiverParameter) - currentStep.replacementCall.putValueArgument( - 1, - nestedLambda - ) + currentStep.processLambdaReceiver(previousStepLambda, nestedLambda) val lambdaBlockBody = nestedLambda.function.body if (lambdaBlockBody is IrBlockBody && steps.size == 1) { //last processing nests the remaining - remainingStatements.forEach { - val patchedStatement = - if (it is IrReturn) - nestedLambda.function.createIrReturn(it.value) - else it - lambdaBlockBody.statements.add(patchedStatement) - } + patchReturnToNestedlambda(remainingStatements, nestedLambda, lambdaBlockBody) } - val statementsBeforeContext = body.statementsBeforeContextCall() - val newReturn = declarationParent.createIrReturn(currentStep.replacementCall) - val newStatements = - if (steps.size != 1) - statementsBeforeContext + newReturn - else - statementsBeforeContext - val transformedBody = createBlockBody(newStatements) - replaceErrorExpressionsWithReceiverValues(transformedBody, currentStep.type, paramSymbol) - // TODO nest body with other recursive function - val nextSteps = steps.drop(1) - val remaining = body.remainingStatementsAfterCall(currentStep.contextCall) - transformedBody.statements.removeIf { it in remaining } + val (transformedBody, nextSteps, remaining) = + currentStep.transformBody(body, declarationParent, steps, paramSymbol) processBodiesRecursive(nestedLambda.function, transformedBody, nextSteps, nestedLambda, remaining + remainingStatements) } } + private fun ReceiverProcessStep.processLambdaReceiver( + previousStepLambda: IrFunctionExpression?, + nestedLambda: IrFunctionExpression + ) { + val extensionReceiverParam = + previousStepLambda?.function?.extensionReceiverParameter ?: nestedLambda.function.extensionReceiverParameter + if (extensionReceiverParam != null) + processContextReceiver(0, type, replacementCall, extensionReceiverParam) + } + + private fun ReceiverProcessStep.createLambdaAndArguments( + declarationParent: IrDeclarationParent, + paramSymbol: IrValueParameterSymbolImpl + ): IrFunctionExpression { + val returningBlockType = declarationParent.returningBlockType() + replacementCall.addReplacedTypeArguments(type, returningBlockType) + val nestedLambda = createNestedLambda(returningBlockType, paramSymbol, declarationParent) + replacementCall.putValueArgument(1, nestedLambda) + return nestedLambda + } + + private fun ReceiverProcessStep.transformBody( + body: IrBlockBody, + declarationParent: IrDeclarationParent, + steps: List, + paramSymbol: IrValueParameterSymbolImpl + ): Triple, List> { + val statementsBeforeContext = body.statementsBeforeContextCall() + val newStatements = + transformedStatements(declarationParent, steps, statementsBeforeContext) + val transformedBody = createBlockBody(newStatements) + replaceErrorExpressionsWithReceiverValues(transformedBody, type, paramSymbol) + val nextSteps = steps.drop(1) + val remaining = body.remainingStatementsAfterCall(contextCall) + transformedBody.statements.removeIf { it in remaining } + return Triple(transformedBody, nextSteps, remaining) + } + + private fun ReceiverProcessStep.transformedStatements( + declarationParent: IrDeclarationParent, + steps: List, + statementsBeforeContext: List + ): List { + val newReturn = declarationParent.createIrReturn(replacementCall) + return if (steps.size != 1) + statementsBeforeContext + newReturn + else + statementsBeforeContext + } + + private fun patchReturnToNestedlambda( + remainingStatements: List, + nestedLambda: IrFunctionExpression, + lambdaBlockBody: IrBlockBody + ) { + remainingStatements.forEach { + val patchedStatement = + if (it is IrReturn) + nestedLambda.function.createIrReturn(it.value) + else it + lambdaBlockBody.statements.add(patchedStatement) + } + } + + private fun ReceiverProcessStep.createNestedLambda( + returningBlockType: IrType, + paramSymbol: IrValueParameterSymbolImpl, + declarationParent: IrDeclarationParent + ) = + createLambdaExpressionWithoutParent(type, returningBlockType, paramSymbol) { + blockBody { + } + }.also { + it.function.parent = declarationParent + } + + + private fun ReceiverProcessStep.addContextualCallReturn( + previousStepLambda: IrFunctionExpression + ) { + val returned = previousStepLambda.function.createIrReturn(replacementCall) + val previousLambdaBody = (previousStepLambda.function.body as? IrBlockBody) + previousLambdaBody?.statements?.add(returned) + } + private fun IrBlockBody.statementsBeforeContextCall() = statements.takeWhile { it.findNestedContextCall() == null } - private val contextualFunction - get() = - irBuiltIns - .findFunctions(Name.identifier("contextual"), FqName("arrow.inject.annotations")) + private val contextualFunction: IrSimpleFunctionSymbol + get() { + val withFun = irBuiltIns + .findFunctions(Name.identifier("with"), FqName("kotlin")) .first() + return IrSimpleFunctionSymbolImpl( + withFun.descriptor + ).also { + symbolTable.referenceFunction(it.descriptor) + } + } + private fun IrDeclarationParent.returningBlockType() = (this as? IrFunction)?.returnType ?: irBuiltIns.nothingType @@ -444,7 +523,7 @@ internal class ProofsIrContextReceiversRecCodegen( } private val IrFunctionAccessExpression.isCallToContextSyntheticFunction - get() = symbol.owner.fqNameWhenAvailable == FqName("arrow.inject.annotations.ResolveKt.context") + get() = symbol.owner.fqNameForIrSerialization == FqName("arrow.inject.annotations.ProviderKt.with") private fun irTransformBlockBodies( transformBody: (IrDeclarationParent, IrBlockBody) -> IrBody? diff --git a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/model/Proof.kt b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/model/Proof.kt index 0af7daf..076fc95 100644 --- a/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/model/Proof.kt +++ b/arrow-inject-compiler-plugin/src/main/kotlin/arrow/inject/compiler/plugin/model/Proof.kt @@ -26,6 +26,17 @@ sealed class Proof { abstract val declaration: FirDeclaration + /** + * (FIR API review and comments) + * + * # Repeated common FIR properties such as `name` not under common interface + * + * `name` and other repeated properties that appear in many nodes + * are scattered in the FIR hierarchy. + * We'd like in this case `name` and others to appear in a shared + * interface or place like `FirNamedDeclaration` to avoid functions + * like this one. + */ val declarationName: Name get() = when (val decl = declaration) { is FirSimpleFunction -> decl.name