-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
Implement sealed inline classes feature #4903
base: master
Are you sure you want to change the base?
Conversation
91bc4fb
to
4a87c9a
Compare
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.
I'm still going through the commits, but here's a first batch of comments.
compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java
Outdated
Show resolved
Hide resolved
compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt
Outdated
Show resolved
Hide resolved
...er/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt
Outdated
Show resolved
Hide resolved
+irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single()) | ||
+irDelegatingConstructorCall(valueClass.superTypes.single { | ||
it.classOrNull?.owner?.kind == ClassKind.CLASS | ||
}.classOrNull!!.constructors.single().owner) |
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.
I'm confused. This code (and the example in the test) seems to imply that we generate wrapper classes for inline subclasses of sealed inline classes, while the specification explicitly states that these classes are not generated. Why are they needed and when are they used?
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.
Yeah. Sorry. The problem here is that design has changed middle-way and first commits became obsolete. To avoid the confusion, I should have squashed the commits. I did it now and split it into several relevant parts.
...er/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt
Outdated
Show resolved
Hide resolved
val sealedKeyword = declaration.modifierList?.getModifier(KtTokens.SEALED_KEYWORD) ?: declaration | ||
trace.report( | ||
Errors.INLINE_CLASS_UNDERLYING_VALUE_IS_SUBCLASS_OF_ANOTHER_UNDERLYING_VALUE.on(sealedKeyword) | ||
) |
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.
At least with the design in the KEEP this check seems superfluous, since intermediate sealed classes would never actually be materialized.
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.
Plans here are restrict the check even further and disallow non-final types as underlying types of inline subclasses of sealed inline classes, due to Kotlin/KEEP#318 (comment)
...er/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt
Outdated
Show resolved
Hide resolved
...er/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt
Show resolved
Hide resolved
...er/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt
Outdated
Show resolved
Hide resolved
...er/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt
Outdated
Show resolved
Hide resolved
There's two more comments that I couldn't add because they were from earlier commits: I don't think the code for type parameters in
while the generated call is
I think it's probably for the best to avoid folding any additional casts into this intrinsic in any case, since it really should only be used for boxing/unboxing inline classes. Casts from/to upper bounds can always be added around it. The workaround for nullable receivers in value class A(val x: String) and now, e.g., the debugger is asked to evaluate an expression In fact, this should probably be done when we generate code for checked calls in general, since an |
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.
I've only reviewed ~2/3 of the changes. Will continue next week.
compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt
Outdated
Show resolved
Hide resolved
compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt
Outdated
Show resolved
Hide resolved
compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt
Outdated
Show resolved
Hide resolved
compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineClassDeclarationChecker.kt
Outdated
Show resolved
Hide resolved
* | ||
* First, we check for noinline children, then for inline children and finally, just run the top's method body. | ||
*/ | ||
private fun rewriteOpenAndAbstractMethodsForSealed(info: SealedInlineClassInfo) { |
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 would be amazing to split this function into several logically independent functions to simplify reading/navigation
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.
Split it a bit.
compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java
Outdated
Show resolved
Hide resolved
get() = this.isValue && valueClassRepresentation is InlineClassRepresentation | ||
|
||
val IrClass.isInline: Boolean | ||
get() = (isSingleFieldValueClass && annotations.hasAnnotation(JVM_INLINE_ANNOTATION_FQ_NAME)) || (isValue && modality == Modality.SEALED) |
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 almost seems like it would be a good idea to add another subclass of ValueClassRepresentation
, SealedInlineClassRepresentation
, for example to keep the invariant isValue == (valueClassRepresentation != null)
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.
Introduced SealedInlineClassRepresentation
, and used it everywhere.
+--------------------------+-------------+-------------+-------------+-------------+-------------+ | ||
|inline class |nope |nope |nope |nope |nope | | ||
+--------------------------+-------------+-------------+-------------+-------------+-------------+ | ||
|sealed interface |yep |OOS |yep |yep |nope | |
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.
Not really clear what "OOS" means :)
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.
Probably "out of scope"
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.
Yep
generateAdditionalMembersForMultiFieldValueClasses(irClass, ktClassOrObject) | ||
} | ||
|
||
if (DescriptorUtils.isEnumClass(classDescriptor)) { | ||
generateAdditionalMembersForEnumClass(irClass) | ||
} | ||
|
||
if (classDescriptor.isInline && !classDescriptor.annotations.hasAnnotation(JVM_INLINE_ANNOTATION_FQ_NAME)) { |
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 feels weird that we add an annotation which is not there on the declaration site, to supposedly fix the lack of information of whether the class was value/inline in IR... or something? And because of that, a lot of tests depend on stdlib now. Can we invent some other approach, like adding some new information to IR nodes? Maybe you can explain the problem?
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.
Well, it is by design - the annotation is used by libraries, which use reflection, like mockito, to determine, whether a class is inline of not and to pass underlying value instead of boxed one. So, we have to generate the annotation.
...er/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt
Outdated
Show resolved
Hide resolved
4a87c9a
to
122be41
Compare
@sfs, @udalov Thanks a lot for your comments. I reorganized the code, so it is hopefully easier to review, since there were a lot of obsolete code, due to design change midway. Sorry I kept you waiting - I was waiting for value classes, which required some refactoring, so, I would have to adopt the changes anyway. |
They are always mapped to `Any?`, so, there is no need to serialize inline class representation for sealed inline classes. #KT-27576
#KT-27576
`inline` modifier during PSI2IR instead of during lowering. This is to distinguish inline classes and value classes with one property, but which are not inline. #KT-27576
#KT-27576
black box test. #KT-27576
TODO: generate method for is checks. #KT-27576
#KT-27576: Fixed
122be41
to
126d074
Compare
KEEP: Kotlin/KEEP#312