Skip to content
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

Defer optics generation of invalid classes to later ksp rounds #3537

Merged
merged 2 commits into from
Nov 21, 2024
Merged

Defer optics generation of invalid classes to later ksp rounds #3537

merged 2 commits into from
Nov 21, 2024

Conversation

johnmave126
Copy link
Contributor

Currently the optics ksp plugin always return emptyList() in every round of ksp processing:

// the docs say that [process] should return
// "deferred symbols that the processor can't process"
// and in theory we have none
return emptyList()

However, this becomes problematic when:

  1. The project is using other processor(s) other than arrow-optics-ksp-plugin
  2. Some other processor generates type A in a single round, and returns emptyList() in its process()
  3. Non-generated code with @optics annotation depends on A:
    @optics
    data class B(val a: A)
  4. ksp processing terminates in a single round, since all the processors has no deferred symbol.
  5. When optics generates code for B, A is unresolvable, resulting in <ERROR TYPE: A> in the generated code.

Kotlin can actually check whether an annotated class is fully resolvable by calling .validate(), and invalid classes should be deferred to later rounds.

This PR checks and splits the annotated symbols into symbols that can be immediately codegen-ed and symbols that should be deferred, enabling better collaboration with other processors.

Copy link
Member

@serras serras left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this useful addition :)

@johnmave126
Copy link
Contributor Author

Alright, fixed the build check error, turns out I need to explicitly import com.google.devtools.ksp.validate, which was weirdly not a problem on my machine when arrow lives as a subproject of mine, but became a problem when it is standalone.

@serras serras merged commit 5026cfd into arrow-kt:main Nov 21, 2024
@FWest98
Copy link
Contributor

FWest98 commented Jan 6, 2025

This change is causing issues when the to-be-generated optics are used in the class definition itself, for example in its functions. When this is the case, the class will never be valid (since its validity depends on the optics to be generated), and thus will be deferred indefinitely. At the end, KSP will report:

w: [ksp] Unable to process:arrow.optics.plugin.OpticsProcessor:   Foo;Bar

@johnmave126
Copy link
Contributor Author

johnmave126 commented Jan 6, 2025

Emmm, that definitely could happen. I am not sure what the most "correct" way to handle the defer is, but let me propose a "reasonable" quick-n-dirty heuristic: if the deferred symbol list is stale for 3 (an arbitrary number I think appropriate) rounds, arrow will then generate the optics no matter what.

@FWest98
Copy link
Contributor

FWest98 commented Jan 6, 2025

Here is a minimal reproducible example. The key thing is that the type of the function can only be resolved once the lens exists - this causes a stalemate. When I manually specify the function type, the optics can be generated and it all works fine.

@optics
data class UsingLens(val field: String) {
  fun getLens() = UsingLens.field
  companion object
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants