quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.56k stars 2.62k forks source link

Can't inject list of interfaces with wildcards twice into beans #32080

Closed insideworld closed 1 year ago

insideworld commented 1 year ago

Describe the bug

ARC has a problem while resolving injection points for types with wilcards. If create an parametirized interface and several implementation and after try to inject List (with All annotion) more than one time - ARC can't resolve syntetic bean more than one time.

More details with screenshoots in additional information.

Expected behavior

Shold inject list of beans in any injection point.

Actual behavior

If inject List in two or more beans will cause exception about unsatisfied dependency.

How to Reproduce?

Example here https://github.com/insideworld/quarkusbug Just try to build it using maven and take exception about Unsatisfied dependency. If comment BeanB - all works fine.

Test case:

  1. Need to create an interface with generic parameter.
  2. Create two or more implementation of generic interface and mark it as singleton.
  3. Create two or more independed beans and try to inject list of interfaces with all annotation.
  4. Try to build it or launch.

Output of uname -a or ver

No response

Output of java -version

java version "17.0.5" 2022-10-18 LTS

GraalVM version (if different from Java)

No response

Quarkus version or git rev

3.0.0.Beta1

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63)

Additional information

ARC try to resolve bean at first iteration in io.quarkus.arc.processor.Beans#resolveInjectionPoint and found possible syntetic bean to inject. In io.quarkus.arc.processor.BeanResolverImpl#matchesNoBoxing all good because comparation by reference get true: image

But in the second (and other) times when ARC try to compare 2 types, references are different: image

After some recursive operation, arc try to resolve wildcards types: image But here we don't have comparation by references or comparation by 2 wildcards and validation return negative result.

quarkus-bot[bot] commented 1 year ago

/cc @Ladicek (arc), @manovotn (arc), @mkouba (arc)

manovotn commented 1 year ago

If nobody beats me to it, I can take a stab at this on Mon.

manovotn commented 1 year ago

So, the issue here is that for given injection point, we create a synthetic bean which contains a wildcard in its bean type (in this case we create List<Some<?>>).

This is normally not OK from CDI spec standpoint:

A parameterized type that contains a wildcard type parameter is not a legal bean type.

and we therefore don't have the assignability rules to deal with it, hence the unsatisfied resolution.

There are two ways we can fix this:

As a side note - the fact that the reproducer provided here passes with only one bean implementation is coincidental as it only passes because the two compared parameterized types will be completely identical, hence passing the reference equality check.

mkouba commented 1 year ago

Thanks for your analysis Matej. Indeed, we don't have a test for parameterized types. There's only test to verify that @All List<T> and @All List<?> are illegal.

In theory, the List<Some<?>> should be satisfied with List<Some<T>> but I think that we need a more robust solution to cover all use cases with generics.

Maybe we should reimplement the @Inject @All completely. Right now, we discover all @All injection points, then we add a @Identified qualifier where the id is based on the required type and all annotations of an IP. Later we register a synthetic bean that satisfies these requirements. The bean delegates to io.quarkus.arc.impl.Instances.listOf() at runtime.

manovotn commented 1 year ago

Thanks for the comment Martin, I am digging deeper into it as I discovered some other discrepancies (part of which you described here). For this very case, I have a fix ready but I am currently exploring how to handle wildcard with upper/lower bounds.