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

Can't differentiate between @A(int.class) and @A(Integer.class) #870

Closed
amandeepg opened this issue Feb 23, 2022 · 6 comments · Fixed by #1277
Closed

Can't differentiate between @A(int.class) and @A(Integer.class) #870

amandeepg opened this issue Feb 23, 2022 · 6 comments · Fixed by #1277
Assignees
Milestone

Comments

@amandeepg
Copy link

Given the following Java code:

@MyAnnotation(int.class)
class B {
}

@MyAnnotation(Integer.class)
class C {
}

and the following KSP code

val clsB = resolver.getClassDeclarationByName("com.example.B")
val clsC = resolver.getClassDeclarationByName("com.example.C")

val annB = clsB!!.annotations.first().arguments.first().value
val annC = clsC!!.annotations.first().arguments.first().value

val equal = annB === annC

equal is true, which is surprising given that the underlying code is not and int.class == Integer.class would return false.

@ting-yuan ting-yuan added this to the 1.0.5 milestone Mar 4, 2022
@Jeffset
Copy link
Contributor

Jeffset commented Mar 7, 2022

As of now, KSP behaves exactly as pure Kotlin here, meaning there's no such thing as int or java.lang.Integer in Kotlin, only kotlin.Int. The information about underlying Java type for primitive/wrapper types is not present in public KSP API (as far as I see).

The problem in this issue is, in fact, broader - there's no way to distinguish between Java's primitive and wrapper types not only in annotation arguments, but also in methods' and fields' originating from Java. This knowledge is required when overriding Java methods in Java code (there's an obscure way to recover this information by using experimental Resolver.mapToJvmSignature and parsing the result, which I managed to get working, but this is way too much complex for such a simple task).

As I understand it, to resolve the issue KSP needs to model Java's primitive and wrapper types as separate objects. In fact, I see that could be at least three equivalence classes for such types:

  • Java's explicitly primitive types
  • Java's explicitly wrapper types
  • Kotlin types ("primitiveness" yet to be decided by compiler backend, aka "flexible")

It looks very similar to nullability information, where Kotlin type has explicit mark and Java types are assumed "platform" (flexible).

Also maybe we could add this information on KSTypeReference level, which knows about its origin and could provide this additional "primitiveness" information. This would solve the issue for methods and fields, and it is less invasive a change; but it doesn't solve the issue for annotations, as KSAnnotation yields KSTypes directly, not KSTypeReferences.

Anyway, I would be glad to discuss a way to solve this, as I'm also affected.

@ting-yuan
Copy link
Collaborator

The Java int is converted to java.lang.Integer because Kotlin compiler's type system operates boxed values. We need to figure out how to model Java primitive types.

@ting-yuan ting-yuan modified the milestones: 1.0.5, 1.0.6 Apr 5, 2022
@neetopia neetopia modified the milestones: 1.0.6, 1.0.7 Jul 13, 2022
@ting-yuan ting-yuan self-assigned this Aug 16, 2022
@ting-yuan
Copy link
Collaborator

From Kotlin's point of view, primitive types can be distinguished from their boxed alternatives by nullability. In KSP, primitive types are NOT_NULL, while boxed types are PLATFORM.

Unfortunately, the information was lost in annotations in #703. I need to run some more tests to see if mapping boxed types would be OK.

@Jeffset
Copy link
Contributor

Jeffset commented Oct 9, 2022

In KSP, primitive types are NOT_NULL, while boxed types are PLATFORM.

Good point, but can this criteria be reliably used to tell boxed/primitve types apart? Is there a guarantee, that this will always be the case? What if one uses java.lang.Integer explicitly in Kotlin? What if a method in Kotlin (override fun getInt(): Int) overrides a method from Java superclass (Integer getInt()) with an explicitly non-null return-type?

@ting-yuan ting-yuan modified the milestones: 1.0.7, 1.0.8 Oct 12, 2022
@ting-yuan
Copy link
Collaborator

can this criteria be reliably used to tell boxed/primitve types apart? Is there a guarantee, that this will always be the case?

Yes, it is documented so KSP needs to follow.

What if one uses java.lang.Integer explicitly in Kotlin?

It'll be non-null java.lang.Integer. Only types in Java sources are mapped.

What if a method in Kotlin (override fun getInt(): Int) overrides a method from Java superclass (Integer getInt()) with an explicitly non-null return-type?

It'll be non-null kotlin.Int for the Kotlin overrider and kotlin.Int! for the Java overridee.

@ting-yuan
Copy link
Collaborator

The ksp.map.annotation.arguments.in.java gradle property has landed to solve this issue. It is disabled by default for now, and will be enabled and deprecated in the future.

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 a pull request may close this issue.

4 participants