-
Notifications
You must be signed in to change notification settings - Fork 5.7k
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
ObjCName annotation #4815
ObjCName annotation #4815
Conversation
64ceb74
to
ffa9465
Compare
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Outdated
Show resolved
Hide resolved
It is not related: the issue is about producing C API for Kotlin code, the implemented annotation doesn't affect it, IIUC. |
Yeah you are correct this doesn't affect the C API just Objective-C. |
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Outdated
Show resolved
Hide resolved
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Show resolved
Hide resolved
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Show resolved
Hide resolved
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Outdated
Show resolved
Hide resolved
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Outdated
Show resolved
Hide resolved
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Outdated
Show resolved
Hide resolved
@@ -902,6 +922,45 @@ private fun ObjCExportMapper.canHaveSameName(first: PropertyDescriptor, second: | |||
return bridgePropertyType(first) == bridgePropertyType(second) | |||
} | |||
|
|||
private fun DeclarationDescriptor.getObjCName(forSwift: Boolean): String { |
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.
If the compiler has to mangle an @ObjCName
-annotated declaration to prevent a name clash, should it report something in this case?
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.
Ideally yes. But IMO it should report a warning for all mangled names (even if the annotation isn't present).
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.
Yes, I agree. So, in case of a clash, it probably makes sense to emit
- an error, if the mangling is applied to something with
@ObjCName
- a warning, otherwise
What do you think?
Generally, current implementation of naming in ObjCExport makes this not simple, so here I'm mostly trying to bring us at the same page regarding the concept, not insisting on implementing this as a part of this PR.
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 I like that! Specifying conflicting @ObjCName
annotations is definitely an error.
But that would indeed be a big addition for this PR, so probably best to add that separately.
ffa9465
to
1620108
Compare
...ve/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt
Outdated
Show resolved
Hide resolved
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Outdated
Show resolved
Hide resolved
native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCNameChecker.kt
Outdated
Show resolved
Hide resolved
native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCNameChecker.kt
Show resolved
Hide resolved
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.
Sorry for jumping in late in this review. At Google we are interested in controlling Objective-C and Swift names as well. We were going to look into this until I noticed that you are already doing this Rick. Thank you! A couple of questions came to mind while reading the change, so added them as review comments. :)
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Outdated
Show resolved
Hide resolved
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Show resolved
Hide resolved
4f11a7e
to
5a3eb1a
Compare
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Outdated
Show resolved
Hide resolved
...ve/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt
Outdated
Show resolved
Hide resolved
native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCNameChecker.kt
Outdated
Show resolved
Hide resolved
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Outdated
Show resolved
Hide resolved
native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCNameChecker.kt
Outdated
Show resolved
Hide resolved
native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCNameChecker.kt
Show resolved
Hide resolved
Btw, does the annotation work for enum entries? |
Turns out it can be applied to enum entries, but at the moment it doesn't have any effect. |
It should, but this would be a breaking change, which makes fixing it properly harder. Having |
native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCNameChecker.kt
Outdated
Show resolved
Hide resolved
native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCNameChecker.kt
Outdated
Show resolved
Hide resolved
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Show resolved
Hide resolved
} | ||
|
||
private fun CallableDescriptor.getObjCName(): ObjCName = | ||
overriddenDescriptors.firstOrNull()?.getObjCName() ?: (this as DeclarationDescriptor).getObjCName() |
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.
Can we do this without casts? (also below).
Motivation: Although these are cast to supertypes, the compiler doesn't check that. Now, imagine we are going to rewrite this code to a different representation for declarations. If we forget to change a target type in a particular cast, and we don't have tests covering this case, the compiler won't help us to detect this either, and this bug will remain unnoticed.
Let me propose alternatives:
- Implement and use a special generic
upcast
function that doesn't doas
, but simply returns the argument as its supertype. - Use different names for "conflicting"
getObjCName
functions.
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 actually like that the functions have the same name (and naming is hard), so I went with the upcast
function.
Not sure if this is a common use case, but I guess upcast
could even be part of stdlib.
...piler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportNamer.kt
Show resolved
Hide resolved
} | ||
|
||
fun ValueArgument.getBooleanValue(): Boolean = | ||
(getArgumentExpression() as? KtConstantExpression)?.node?.text?.toBoolean() ?: false |
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.
This code is used in IDE.
toBoolean
will probably throw an exception if the string is not true
or false
.
This is not fine:
- What if it is not a boolean literal, but a reference to a
const val
? Supporting this case would be complicated, but at least IDE shouldn't throw an exception in this case. - A user might have simply written incorrect code. For example,
@ObjCName("abc", exact = "def")
. IDE should highlight this as error, but not throw an exception.
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 I wasn't sure if this was actually the correct way to get the boolean.
We could use toBooleanOrNull
to solve the second issue, but the first one seems harder to fix.
The following compiles fine, but ignores name
and exact
:
private const val name = "ObjCNameObjCObject"
private const val exact = true
@ObjCName(name, "ObjCNameSwiftObject", exact)
object ObjCNameKotlinObject
Do you have any pointers for this?
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.
As far as I can tell, now that the @ObjCName
annotation can change the name, we can no longer compute the name based on the PSI structure, but need to perform resolution to get the name.
I hacked together the following: google@d0232bf
Which just removes the eager computation of the name in the lazy objective-c export code and replaces it with lazy computation based on descriptors which means that resolution has to happen to get the name. I didn't have the time to clean it up, but wanted to share in case it is useful.
@SvyatoslavScherbina does that seem like the right way to go? I don't really see how the lazy objective-c exporter can get the right name unless we go through resolution for it?
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 hacked together the following: google@d0232bf
Interesting. Where are the tests for the lazy exporter located?
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.
IIUC testObjCExport
is using the lazy exporter and the output (expectedLazy.h
) contains the correct names.
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.
but the first one seems harder to fix.
Exactly, that's why I proposed not to support it in IDE for now :)
I hacked together the following: google@d0232bf
@SvyatoslavScherbina does that seem like the right way to go?
Hard to say, I'm not actually an expert in IDE. IIUC, this would mean that indexing would trigger resolution for every class, right? This might be a huge performance hit.
I don't really see how the lazy objective-c exporter can get the right name unless we go through resolution for it?
I see a couple of options. For example, we could prohibit using anything but boolean literals for exact
values, or make it a separate annotation (e.g. @ExactObjCName
or @ObjCName.Exact
).
Interesting. Where are the tests for the lazy exporter located?
expectedLazy.h
is the only test I'm aware of.
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.
Alright I think I get the issues now (correct me if I am wrong).
- using
const val
references as values for the annotation isn't currently working and would require resolution.
I don't think there is a real use case for this (since the names and exact value are tightly coupled to a declaration). Meaning we could prohibit it, only allowing literal string and boolean values, correct? - exceptions are thrown in case the arguments aren't in the expected format.
In this case we should make sure to silently ignore such values.
The compiler will fail with an appropriate error message anyway.
expectedLazy.h
is the only test I'm aware of.
That one is passing, so we shouldn't have any issues if we prohibit the usage of reference values.
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.
Alright I think I get the issues now (correct me if I am wrong).
- using
const val
references as values for the annotation isn't currently working and would require resolution.
I don't think there is a real use case for this (since the names and exact value are tightly coupled to a declaration). Meaning we could prohibit it, only allowing literal string and boolean values, correct?
Yes. I agree that it is probably fine to limit to string constants and boolean constants for now. If we would like to expand on it in the future, that should be relatively easy as well. In what I hacked up we do resolution always to get the name. However, we can see the @ObjCName
in the PSI, so we could do resolution when getting the name only when that annotation is present. Then at least the cost would only be incurred for the declarations where it is necessary. I would be happy to do that if we end up seeing a need, but I would support just limiting to constants for now.
- exceptions are thrown in case the arguments aren't in the expected format.
In this case we should make sure to silently ignore such values.
The compiler will fail with an appropriate error message anyway.
That is my understanding as well. If the code cannot be compiled, the editor does a best effort. :)
expectedLazy.h
is the only test I'm aware of.That one is passing, so we shouldn't have any issues if we prohibit the usage of reference values.
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.
toBoolean
wouldn't actually throw an exception, but instead return false
. Updated to toBooleanStrictOrNull
to make the intended behaviour more clear.
The frontend checker will now prohibit non literal string and boolean values, making sure we don't need resolution in the lazy exporter.
I have also added a test case to verify that there aren't any exceptions in case the user writes invalid code.
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.
Alright I think I get the issues now (correct me if I am wrong).
Totally correct, thank you!
Enum entries are now correctly supported and the frontend checker will prohibit |
native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeObjCNameChecker.kt
Outdated
Show resolved
Hide resolved
...ackend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportHeaderGenerator.kt
Show resolved
Hide resolved
Status: IIUC, there are no more unresolved comments. We are waiting for the review of the annotation declaration from the Kotlin Libraries team. |
f6be798
to
215ec19
Compare
803968f
to
8287ce1
Compare
...s/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt
Outdated
Show resolved
Hide resolved
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.
Status: everything seems fine, the CI is green, we are good to go!
GitHub reports some merge conflicts (?), @rickclephas could you please rebase the branch to master?
Also, please squash the commits and update the message(s). Otherwise, I will squash everything to a single commit and write some commit message myself.
@@ -465,12 +491,12 @@ internal class ObjCExportNamerImpl( | |||
parameters.forEachIndexed { index, (bridge, it) -> | |||
val name = when (bridge) { | |||
is MethodBridgeValueParameter.Mapped -> when { | |||
it is ReceiverParameterDescriptor -> "" | |||
it is ReceiverParameterDescriptor -> it.getObjCName().asIdentifier(false) { "" } | |||
method is PropertySetterDescriptor -> when (parameters.size) { |
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.
(Possible future improvement:)
In certain cases Kotlin property accessors are translated to methods. In this case, @ObjCName
can be applied to the setter parameter but likely has no effect.
4256c4e
to
2f9ca92
Compare
@SvyatoslavScherbina rebased and squashed 👍🏻 |
2f9ca92
to
2cecd9b
Compare
Merged. |
Relates to KT-42297, KT-50767, KT-48076, KT-48072, KT-43087 and KT-40796.
Sample
Logic
The
ObjCName
accepts an ObjC name and a Swift name, both are optional.The name of the Swift/ObjC declaration is determined as follows:
ObjCName
annotation (Swift only)ObjCName
annotation