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

Make hierarchy in KSP API sealed (attempt 2) #1592

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

lukellmann
Copy link
Contributor

Another try of #1380 with additional compilation fixes.


override val origin by lazy {
open /*override*/ val origin by lazy {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm a bit concerned with this. Having to cast the implementations is not only inconvenient, but also error prone and hard to maintain. Is there a better way to do it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this approach was the one with minimal changes. another one i can think of is to have these abstract classes but override and upcall in all implementation classes. this would be a bigger change but also less error prone.

Copy link
Contributor Author

@lukellmann lukellmann Nov 9, 2023

Choose a reason for hiding this comment

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

what about this: cda8056 (replacing casts with abstract functions with the desired return type that are overridden to simply return this)

Interfaces that were sealed: KSAnnotated, KSDeclarationContainer,
KSReferenceElement, KSModifierListOwner, KSPropertyAccessor,
KSDeclaration and PlatformInfo
Abstract implementation classes that inherited from now sealed types
have those supertypes removed and replace overridden with open members.
Additional casts had to be introduced in a few places.

A few when statements had to be made exhaustive (by specifying all
cases) because they now operate on subjects with a sealed type.
KSNodeDescriptorImpl, KSNodeJavaImpl and KSNodeKtImpl were removed
because they were unused but inherited from the now sealed KSNode.
@ting-yuan
Copy link
Collaborator

After thinking this again, I feel it might not be right to make the API sealed. The class hierarchy may be expanded in the future to reflect language changes. Making them sealed will make it very tricky. Could you justify why sealing it is the way to go?

@lukellmann
Copy link
Contributor Author

Like described in #1351, the motivation was to be able to have the compiler enforce code changes when the hierchy is changed. So changes in the hierachy leading to compiler errors in sealed whens is the desired outcome.

@lukellmann
Copy link
Contributor Author

[...] The class hierarchy may be expanded in the future to reflect language changes. Making them sealed will make it very tricky. [...]

To be clear, you are talking about the API side facing the KSP user here, right? Or do you also mean something else?

@ting-yuan
Copy link
Collaborator

Making the API sealed only helps discover source level incompatibility. From library users' point of view, sealed interface / class isn't truly sealed. A unhandled case can still arise in an exhaustive-when in the user code. If when is used as a statement, it could also become a silent error. I don't know how to alter the hierarchy of a sealed class in the API without potentially breaking users' code which relies on exhaustive-when.

It'd also be tricky when API users try to be future compatible; If their when is already exhaustive, adding an else clause would trigger a compiler warning.

@lukellmann
Copy link
Contributor Author

Making the API sealed only helps discover source level incompatibility. From library users' point of view, sealed interface / class isn't truly sealed. A unhandled case can still arise in an exhaustive-when in the user code.

And that's fine. The goal is to make writing the code easier. An unhandled case at runtime will throw and clearly tell what went wrong - the processor isn't built for handling this new feature.

If when is used as a statement, it could also become a silent error.

How would it be silent? Non-exhaustive when statements on sealed types report an error since Kotlin 1.7.0. So either you run into the else you are required to write or you get an exception if you didn't recompile after a new feature was added.

I don't know how to alter the hierarchy of a sealed class in the API without potentially breaking users' code which relies on exhaustive-when.

Tools working on source code (like KSP processors) will always break on new language features, in one way or another. The whole point is to get help by the compiler when things change. Visitors can still be used when preferred.

It'd also be tricky when API users try to be future compatible; If their when is already exhaustive, adding an else clause would trigger a compiler warning.

I'd argue if a user really wants to be future compatible to this extent, a suppression would be ok.

Comment on lines -321 to +324
return (container.declarations as? Sequence<AbstractKSDeclarationImpl>)
?.sortedWith(declarationOrdering.comparator) ?: container.declarations
return container.declarations
.sortedWith(compareBy(declarationOrdering.comparator) { it as AbstractKSDeclarationImpl })
Copy link
Contributor Author

Choose a reason for hiding this comment

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

explanation:
This was an unchecked cast (generics involved) and would always succeed - so replacing as? with as shouldn't change any behavior.

# Conflicts:
#	common-util/src/main/kotlin/com/google/devtools/ksp/common/IncrementalContextBase.kt
#	kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/AbstractKSDeclarationImpl.kt
#	kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSTypeAliasImpl.kt
# Conflicts:
#	kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/AbstractKSDeclarationImpl.kt
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.

None yet

2 participants