-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Few comments regarding the FIR and BIR apis #11
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, it may be not the best syntax. I just thought at some point that it looks pretty nice, but I'm not sure about it right now |
||
* 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, session components are not available in backend. But you can use something like that: class ProofCache
class ProofCacheStorage(session: FirSession, val proofCache: ProofCache) : FirExtensionSessionComponent(session)
private val FirSession.proofCacheStorage: ProofCacheStorage by FirSession.sessionComponentAccessor()
val FirSession.proofCache: ProofCache
get() = proofCacheStorage.proofCache There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice!, we will try to refactor to that |
||
* 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) } | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keys can be used for passing some information from FIR to IR. We save a plugin key in We're considering different approaches for such data translation (e.g. by introducing declaration attributes for IR declarations), but for now this is the only way except some external service There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense and this clarifies it. |
||
* property in nodes, but we are not sure what good it does or if | ||
* it's needed at all | ||
*/ | ||
internal object ProofKey : FirPluginKey() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a price for builder functions instead of constructor calls. There are two things which we possibly can do with it:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are very interested in safe builders as well, if there is anything we can do to help there even if in the future let us know. :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Design for them was done a long time ago. So now we need to actualize it and implement it in the FIR. |
||
* in the FIR and IR builders have `lateinit var` that are not required on construction. | ||
*/ | ||
this.originalForSubstitutionOverrideAttr = firNamedFunctionSymbol.fir | ||
} | ||
.symbol | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a dependenciesSymbolProvider which searches for declarations outside the current module (including other source modules in IDE). But in any case classpath scanning looks for me like a very questionable way to do anything in the compiler plugin |
||
* 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 { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll improve it as a part of FIR IDE plugins integration |
||
* 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 = | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't fully get the problem. There is an example of checkers component for parcelize and I don't think that there is too much boilerplate class FirParcelizeCheckersExtension(session: FirSession) : FirAdditionalCheckersExtension(session) {
override val expressionCheckers: ExpressionCheckers = object : ExpressionCheckers() {
override val annotationCallCheckers: Set<FirAnnotationCallChecker>
get() = setOf(FirParcelizeAnnotationChecker)
}
override val declarationCheckers: DeclarationCheckers = object : DeclarationCheckers() {
override val classCheckers: Set<FirClassChecker>
get() = setOf(FirParcelizeClassChecker)
override val propertyCheckers: Set<FirPropertyChecker>
get() = setOf(FirParcelizePropertyChecker)
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
get() = setOf(FirParcelizeFunctionChecker)
override val constructorCheckers: Set<FirConstructorChecker>
get() = setOf(FirParcelizeConstructorChecker)
}
} The point of having different sets for different checkers is to ensure that
|
||
* the creation of instances and nesting of objects was too much boilerplate | ||
* when registering each service | ||
*/ | ||
internal class ProofResolutionCheckerExtension( | ||
session: FirSession, | ||
declarationCheckers: List<FirAbstractDeclarationChecker>, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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<Proof> 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please report this as YT issue. It will be great if you attach a very simple reproducer project to it (not the whole arrow-inject) |
||
* 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<ConeKotlinType> { | ||
// 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<FirTypeProjection> | ||
get() = if (isCallToContextSyntheticFunction) typeArguments else emptyList() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeo, it won't be possible to modify existing resolution. But I think we can simplify API for custom calling of |
||
*/ | ||
internal class ProofResolutionStageRunner( | ||
override val session: FirSession, | ||
private val firResolutionProof: FirResolutionProof, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to have some helpers to simplify generating FIR and IR as part of the compiler API. I faced with such boilerplate issues myself and I don't like it. You can create such helpers by yourself and send them as PR, it would be very helpful |
||
* 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 -> { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's impossible, because it won't work in IDE, because it will require resolution of the whole module to
TYPES
stage, which will be very slow