Closed
Description
Describe the bug
In a kotlin quarkus project compiled with the compiler argument -Xemit-jvm-type-annotations
, the build fails if a nested type parameter of a method parameter is annotated with a type parameter, for example
fun foo(bar: List<List<@Valid String>>) {
}
Expected behavior
The build should complete without exceptions
Actual behavior
The build fails with the following exception:
Failed to execute goal io.quarkus.platform:quarkus-maven-plugin:2.14.0.Final:build (default) on project (...): Failed to build quarkus application: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[error]: Build step io.quarkus.deployment.steps.ApplicationIndexBuildStep#build threw an exception: java.lang.IllegalArgumentException: Not a parameterized type!
at org.jboss.jandex.Type.asParameterizedType(Type.java:248)
at org.jboss.jandex.Indexer.resolveTypePath(Indexer.java:1227)
at org.jboss.jandex.Indexer.resolveTypePath(Indexer.java:1234)
at org.jboss.jandex.Indexer.resolveTypeAnnotation(Indexer.java:1098)
at org.jboss.jandex.Indexer.resolveTypeAnnotations(Indexer.java:950)
at org.jboss.jandex.Indexer.indexWithSummary(Indexer.java:2323)
at org.jboss.jandex.Indexer.index(Indexer.java:2277)
at io.quarkus.deployment.steps.ApplicationIndexBuildStep$1.visitFile(ApplicationIndexBuildStep.java:49)
at io.quarkus.deployment.steps.ApplicationIndexBuildStep$1.visitFile(ApplicationIndexBuildStep.java:34)
at java.base/java.nio.file.Files.walkFileTree(Files.java:2811)
at java.base/java.nio.file.Files.walkFileTree(Files.java:2882)
at io.quarkus.deployment.steps.ApplicationIndexBuildStep.build(ApplicationIndexBuildStep.java:34)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:909)
at io.quarkus.builder.BuildContext.run(BuildContext.java:281)
at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
at java.base/java.lang.Thread.run(Thread.java:833)
at org.jboss.threads.JBossThread.run(JBossThread.java:501)
How to Reproduce?
Try to build the following example project:
Output of uname -a
or ver
No response
Output of java -version
17.0.4
GraalVM version (if different from Java)
No response
Quarkus version or git rev
2.14.0.Final
Build tool (ie. output of mvnw --version
or gradlew --version
)
No response
Additional information
The corresponding java code does not seem to trigger the same problem, even though the annotation information shown in javap -v
of the generated class files doesn't look different to me.
Activity
quarkus-bot commentedon Nov 21, 2022
/cc @evanchooly, @geoand
geoand commentedon Nov 21, 2022
@Ladicek I am sure you'll have fun with this one 😉
Ladicek commentedon Nov 21, 2022
Looks like a Jandex bug indeed.
Ladicek commentedon Nov 22, 2022
Okay so this one is interesting. The method
is compiled to a JVM method with the following
Signature
attribute:In Java syntax, this corresponds to
The type annotation is compiled down to this:
So the annotation is correctly attributed to the 1st (or 0th if you wish) parameter of the method, but the type annotation path is wrong.
The type annotation path is basically:
The 2nd step fails -- because the type obtained in step 1 is a wildcard type, not a parameterized type. Remember that the type is effectively
List<? extends List<@Valid String>>
.For the Java snippet above, javac emits the annotation like this:
Where the type annotation path is basically:
That navigates to the
String
type correctly.Ladicek commentedon Nov 22, 2022
TBH, I'm not sure what's the best solution here. I agree Jandex shouldn't fail like this, but it can't be denied that the Kotlin compiler emits internally inconsistent bytecode. I'm thinking Jandex should probably just skip this type annotation.
Ladicek commentedon Nov 22, 2022
So this will be fixed in Jandex 3.0.4, but note that the annotation will be just ignored. Kotlin needs to fix their compiler to emit the type annotation path correctly.
geoand commentedon Nov 22, 2022
@Ladicek maybe open a Kotlin issue?
Ladicek commentedon Nov 22, 2022
Yea, I should do that. Tomorrow :-)
grasfrosch commentedon Nov 23, 2022
Awesome, thanks for the quick fix! Just ignoring the annotation should be good enough for most cases. It's certainly good enough for our case where that bug got triggered by the implicit
Continuation
parameter of a suspending method - I would expect that's probably the easiest way to trigger it.Ladicek commentedon Nov 23, 2022
@grasfrosch if you have some other code that also fails with this error, especially if it includes suspending functions, please share, I'd like to check my fix with it. And thanks for the report!
grasfrosch commentedon Nov 23, 2022
@Ladicek I can't share the original code, but effectively we had a suspending lambda looking something like this:
When Quarkus 2.14 updated the jetbrains annotations dependency,
TYPE_USE
got included as target for the@NotNull
-Annotation and so the inferred return type changed fromPair<String,String>
toPair<@NotNull String, String>
. The generated bytecode for the lambda's class has a methodpublic final java.lang.Object invoke(kotlinx.coroutines.CoroutineScope, kotlin.coroutines.Continuation<? super kotlin.Pair<java.lang.String, java.lang.String>>);
with theand the whole thing blew up in our faces. That one took a while to find.
From playing around a bit, some other functions that produce the error in the current version:
suspend fun foo(): List<@NotNull String> = listOf()
suspend fun foo(): Pair<String, @NotNull String>? = null
fun foo(bar: List<List<List<@NotNull String>>>) = Unit
Both runtime visible and invisible type annotations (for example
@Valid
and@NotNull
) seem to trigger the bug.Interesting to note is that the generic types so far seem to be declared as
List<out E>
andContinuation<in T>
, which seems to get translated to the wildcard during compilation. I can't reproduce the issue using classes likeOptional
orArray
.Thanks again for looking into this!
Ladicek commentedon Nov 23, 2022
Thanks, I'll take a look at the additional cases, but I expect my fix I merged yesterday should cover them.
That would be expected, because Jandex since version 3.0 looks at class-retained (aka runtime-invisible) annotations as well.
That sounds about right. Invariant declaration sites in Kotlin would naturally lead to invariant use sites in the bytecode -- it's declaration sites that use variance that end up being compiled as wildcards.
Thanks again!
Ladicek commentedon Nov 24, 2022
OK so the fix in Jandex seems to work fine with the other cases. I'll release 3.0.4 today.
I also reported the issue in https://youtrack.jetbrains.com/issue/KT-55128
Ladicek commentedon Nov 24, 2022
It also turns out that this bug was discovered before: https://youtrack.jetbrains.com/issue/KT-13228/Generate-annotations-on-types-in-JVM-8-target-mode#focus=Comments-27-4762338.0-0 Unfortunately the reporter didn't file a Jandex bug, so I didn't learn about it sooner.
mschorsch commentedon Nov 24, 2022
Seems there is a workaround: