Skip to content

respecify wildcard type arguments for Event and Instance#982

Open
Ladicek wants to merge 3 commits into
jakartaee:mainfrom
Ladicek:event-instance-meaningful-wildcards
Open

respecify wildcard type arguments for Event and Instance#982
Ladicek wants to merge 3 commits into
jakartaee:mainfrom
Ladicek:event-instance-meaningful-wildcards

Conversation

@Ladicek

@Ladicek Ladicek commented Jun 1, 2026

Copy link
Copy Markdown
Member

The type parameter of Event is naturally contravariant, so injecting Event<? super FooBar> is meaningful. Injecting Event<?> or Event<? extends FooBar> is meaningless.

The type parameter of Instance is genuinely invariant (even though some might claim it should be treated as covariant), so injecting Instance<? extends|super FooBar> is meaningless.

This commit adjusts the specification so that only meaningful wildcards are allowed. It uses a different approach than before, where extra rules were added. These additions are removed and instead, the existing rules that prohibit injecting raw Event / Instance, are extended so that injecting these types with meaningless wildcard arguments is prohibited as well.

Further, the only meaningful wildcard (Event<? super FooBar>) gets special treatment: the specified type is the wildcard's bound. Also, looking up Event<? extends FooBar> throws IllegalArgumentException, whereas previously, this was just non-portable behavior.

@Ladicek Ladicek added this to the CDI 5.0 milestone Jun 1, 2026
@Ladicek
Ladicek requested review from Azquelt and manovotn June 1, 2026 12:28
@Ladicek
Ladicek force-pushed the event-instance-meaningful-wildcards branch from 1899887 to 79d50b2 Compare June 4, 2026 14:52
@Ladicek
Ladicek force-pushed the event-instance-meaningful-wildcards branch from 79d50b2 to 1b5a7aa Compare June 16, 2026 11:11
@Ladicek Ladicek changed the title allow meaningful wildcard type arguments to Event and Instance respecify wildcard type arguments for Event and Instance Jun 16, 2026
@Ladicek

Ladicek commented Jun 16, 2026

Copy link
Copy Markdown
Member Author

Updated.

@Ladicek
Ladicek force-pushed the event-instance-meaningful-wildcards branch from 1b5a7aa to bfc0f2a Compare June 18, 2026 11:28
@Ladicek

Ladicek commented Jun 18, 2026

Copy link
Copy Markdown
Member Author

Updated once again. Event may have a wildcard with lower bound as a type argument, Instance may have a wildcard with upper bound as a type argument.

I also reverted the change for CDI-232, because that change made the specification of the built-in bean for Instance a lot worse (all beans must have a well defined set of bean types!).

@manovotn

Copy link
Copy Markdown
Contributor

I would approve the rest (despite not agreeing with it as it is IMO useless in practice but we've wasted enough time on it already).

I also reverted the change for CDI-232, because that change made the specification of the built-in bean for Instance a lot worse (all beans must have a well defined set of bean types!).

Though this I don't like - I understand the current wording, as-is, won't work but I would rather tweak it then revert to the original one.
I get that they are both equal in their meaning but I really don't like the way this one directly implies an infinite set. That's IMO making it harder to read for no real gain.
I need to think about it some more. Also curious to hear @Azquelt thoughts on it.

(I also know @mkouba wasn't fond of this when he found out)


Original CDI issue link for reference - https://redhat.atlassian.net/browse/CDI-232

@Ladicek

Ladicek commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

I get that they are both equal in their meaning

As much as I hate wasting time on this, I have to disagree. The CDI 2.0 wording does not define a bean properly, because it doesn't say what the set of bean types is.

I understand that an infinite set is inconvenient, and it is in fact impossible to implement Bean.getTypes() for this bean properly, but I'd much rather specify that the outcome of Bean.getTypes() is non-portable for the bean for Instance (and Event) than having a bean that is not properly defined.

@Azquelt

Azquelt commented Jun 19, 2026

Copy link
Copy Markdown
Member

Bean types

The CDI 1.0 wording for the bean types of the Instance bean does imply an infinite set, and I think that's not correct.

I would expect that the set of bean types for the built-in Instance should contain Instance<X> and Provider<X>, where X is a type variable with no bounds. Dependent bean types are allowed to include type variables and I think this would meet the requirements of Instance without having an infinite set.

The CDI 2.0 wording does not define a bean properly, because it doesn't say what the set of bean types is.

I'm not sure what you want here. I would argue that we should not specify the set of bean types in absolute terms.

For example, if an implementation had some kind of ExtendedInstance<X> interface, and wanted to include that in the bean types of the built-in instance bean, that should be fine.

Qualifiers

My main objection to the CDI 2.0 wording is the bullet point has any qualifiers which I don't think makes any sense. If the bean is allowed to have any qualifiers, you can't guarantee that it can be injected anywhere.

The CDI 1.0 wording is not much better. has every qualifier type in its set of qualifier types. Typesafe resolution is done using qualifiers (including the values of members not annotated @Nonbinding), not qualifier types.

If the intention here is to allow the Instance bean to be injected into any injection point regardless of its qualifiers, I don't think it's possible to express that using the Bean interface. The implementation will have to cheat and the Instance bean will have to be special in some way.

Looking at this example in the spec, I think that is indeed the intention:

@Inject @PayBy(CHEQUE) Instance<PaymentProcessor> chequePaymentProcessor;

Conclusion

  • there's no way to specify the return value of getQualifiers() which results in the behaviour we want
  • we could specify that getTypes() must include Instance<X> and Provider<X>, where X is a type variable with no bounds, but given that getQualifiers() won't reflect the actual behaviour of the bean, I don't know if that's worthwhile.
  • it's more important to specify that the built-in Instance bean must allow the behaviour we want

I would change the wording like this:

The container must provide a built-in bean that:

* is eligible for injection to any injection point with required type `Instance<X>` or `Provider<X>`, for any legal bean type `X`, with any qualifiers,
* has scope `@Dependent`,
* has no bean name, and
* has an implementation provided automatically by the container.

This is largely the current wording, but with the change that it's the injection point that's allowed to have any qualifiers, not the bean.

Technically, stating that the injection point can have any qualifiers doesn't actually change anything (it's implied by omission), but I think it's helpful to explicitly state that requirement because it's weird and can't be fulfilled by a regular bean.

In the same way, I guess in the CDI 2.0 wording the requirement "has any qualifiers" doesn't change anything either. The bean can have any qualifiers you like, as long as it fulfils the first requirement that you can inject it into any Instance or Provider injection point.

For an injected `Instance`:

* the _required type_ is the type parameter specified at the injection point, and
* the _required type_ is the type argument specified at the injection point, unless the type argument is a wildcard with upper bound (or an unbounded wildcard), in which case the required type is the wildcard's upper bound (or `java.lang.Object`), and

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* the _required type_ is the type argument specified at the injection point, unless the type argument is a wildcard with upper bound (or an unbounded wildcard), in which case the required type is the wildcard's upper bound (or `java.lang.Object`), and
* the _required type_ is the type argument specified at the injection point, unless the type argument is a wildcard, in which case the required type is the wildcard's upper bound (or `java.lang.Object`), and

or

Suggested change
* the _required type_ is the type argument specified at the injection point, unless the type argument is a wildcard with upper bound (or an unbounded wildcard), in which case the required type is the wildcard's upper bound (or `java.lang.Object`), and
* the _required type_ is the type argument specified at the injection point, unless the type argument is a wildcard without a lower bound, in which case the required type is the wildcard's upper bound (or `java.lang.Object`), and

or

Suggested change
* the _required type_ is the type argument specified at the injection point, unless the type argument is a wildcard with upper bound (or an unbounded wildcard), in which case the required type is the wildcard's upper bound (or `java.lang.Object`), and
* the _required type_ is the type argument specified at the injection point, unless the type argument is a wildcard with upper bound or an unbounded wildcard, in which case the required type is the wildcard's upper bound (or `java.lang.Object`), and

It's odd to put the unbounded wildcard part in brackets, it's just as important as the wildcard with an upper bound.

However, what we really mean is "a wildcard without a lower bound", so we could just say that instead.

However, we could also leave that part out altogether to simplify this bullet point since we forbid a wildcard with a lower bound later.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just say:

the type argument of Instance as specified at the injection point, or its upper bound if the type argument is a wildcard

much shorter and means the same thing.

@Azquelt

Azquelt commented Jun 19, 2026

Copy link
Copy Markdown
Member

I've left my comments above, everything else I think is fine.

@gavinking

gavinking commented Jun 19, 2026

Copy link
Copy Markdown
Member

The CDI 1.0 wording for the bean types of the Instance bean does imply an infinite set, and I think that's not correct.

OK, but why do you think that's not correct? You can't just assert this with no argumentation.

Infinite sets are perfectly normal and mathematically well-defined, and there's no particular reason why the CDI spec needs to avoid them when defining a bean in an abstract sense.

But the wording of the 1.0 spec does not even imply that the set is infinite, because the CDI bean container lives in a closed world where the complete set of bean types known, so it is in fact a finite set.

It's perfectly acceptable for an implementation to interpret "every bean type", and "every qualifier type" to mean "every bean type known to the container" and "every qualifier type known to the container".

Comment on lines +207 to +208
* type `Event<?>`, or
* type `Event<? extends X>`, for any type `X`,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two bullet points could be collapsed, since Event<?> is the same type as Event<? extends Object>.

@gavinking gavinking left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this change, I very much appreciate it.

The change that was made to this language in CDI 2.0 really messed up the original intention, and I'm very happy to see the original language restored.

Comment on lines +799 to +800
* `Instance<X>` and `Provider<X>` for every legal bean type `X` in its set of bean types,
* every qualifier type in its set of qualifier types,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following suggestion is in my opinion completely unnecessary, but I do get that people with CS degrees tend to be quite uncomfortable with the notion of infinity in general. This should resolve such discomforts.

Suggested change
* `Instance<X>` and `Provider<X>` for every legal bean type `X` in its set of bean types,
* every qualifier type in its set of qualifier types,
* `Instance<X>` and `Provider<X>` for every legal bean type `X` in its set of bean types,footnote:[An implementation is permitted to consider the universe of legal bean types to encompass only those bean types known to the bean manager at initialization time.]
* every qualifier type in its set of qualifier types,footnote:[An implementation is permitted to consider the universe of qualifiers to encompass only those qualifiers known to the bean manager at initialization time.]

@mkouba

mkouba commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Infinite sets are perfectly normal and mathematically well-defined, and there's no particular reason why the CDI spec needs to avoid them when defining a bean in an abstract sense.

@gavinking No, they're not, because then you wouldn't be able to perform the typesafe resolution in a finite time interval. "The bean has a bean type that matches the required type." implies iteration over the set of bean types.

But the wording of the 1.0 spec does not even imply that the set is infinite, because the CDI bean container lives in a closed world where the complete set of bean types known, so it is in fact a finite set.

You're right, it's not infinite. It's just a very large set which is often not easy/possible to collect.

The change that was made to this language in CDI 2.0 really messed up the original intention, and I'm very happy to see the original language restored.

Not really, the change merely reflected the current status of implementations because it would be very impractical to follow the spec in this regard, i.e. Bean.getTypes() and typesafe resolution in general. All implementations I've seen have some special treatment that does not follow the general rules for typesafe resolution. So even if we revert the wording, nothing will really change in practice.

@Ladicek @manovotn @Azquelt In any case, I think that this discussion is quite useless, waste of time and resources, and it does not bring any real benefits to CDI users. We should devote time to more important topics.

@Ladicek

Ladicek commented Jun 22, 2026

Copy link
Copy Markdown
Member Author

@Azquelt

Bean types

The CDI 1.0 wording for the bean types of the Instance bean does imply an infinite set, and I think that's not correct.

I would expect that the set of bean types for the built-in Instance should contain Instance<X> and Provider<X>, where X is a type variable with no bounds. Dependent bean types are allowed to include type variables and I think this would meet the requirements of Instance without having an infinite set.

That makes sense to me.

The CDI 2.0 wording does not define a bean properly, because it doesn't say what the set of bean types is.

I'm not sure what you want here. I would argue that we should not specify the set of bean types in absolute terms.

I want a well defined set of bean types, because that's one of the core properties every bean has. Your suggestion above does that in an elegant way which I'd be fine with.

It is technically somewhat different than the original intent, but I think we can just have a well-defined and small set of bean types and qualifiers and use a special provision to override the rules of bean to IP assignability.

For example, if an implementation had some kind of ExtendedInstance<X> interface, and wanted to include that in the bean types of the built-in instance bean, that should be fine.

This is an extra-specification behavior that doesn't break the spec in any way, so I think it should be fine without the spec going an extra mile to allow this?

Qualifiers

My main objection to the CDI 2.0 wording is the bullet point has any qualifiers which I don't think makes any sense. If the bean is allowed to have any qualifiers, you can't guarantee that it can be injected anywhere.

The CDI 1.0 wording is not much better. has every qualifier type in its set of qualifier types. Typesafe resolution is done using qualifiers (including the values of members not annotated @Nonbinding), not qualifier types.

Good point.

If the intention here is to allow the Instance bean to be injected into any injection point regardless of its qualifiers, I don't think it's possible to express that using the Bean interface. The implementation will have to cheat and the Instance bean will have to be special in some way.

Correct. Instance and Event are special, but it seems they are specified in a somewhat strange way so that no other place in the specification has to treat them as special. This makes sense to me, and I'd be fine with saying "has every possible qualifier in its set of qualifiers", even though that cannot possibly be expressed through Bean.getQualifiers().

On the other hand, I'm certainly not married to it. I think we can just say that the built-in Instance bean has a single qualifier of @Any and add a special provision to override the rules of bean to IP assignability.

Looking at this example in the spec, I think that is indeed the intention:

@Inject @PayBy(CHEQUE) Instance<PaymentProcessor> chequePaymentProcessor;

Conclusion

[...]

As mentioned above, I would personally be fine with saying:

The container must provide a built-in bean that:

  • has Instance<X> and Provider<X> in its set of bean types, where X is a type variable with no bounds,
  • has the @Any qualifier,
  • has scope @Dependent,
  • has no bean name, and
  • has an implementation provided automatically by the container.

This bean is assignable to all injection points with a required type of Instance<X> or Provider<X>, where X is any legal bean type, regardless of the required qualifiers of the injection point.

And we could do something similar for the built-in Event bean.


@gavinking

The CDI 1.0 wording for the bean types of the Instance bean does imply an infinite set, and I think that's not correct.

OK, but why do you think that's not correct? You can't just assert this with no argumentation.

Infinite sets are perfectly normal and mathematically well-defined, and there's no particular reason why the CDI spec needs to avoid them when defining a bean in an abstract sense.

The issue I see is that java.util.Set cannot represent an infinite set, because it is way more than a predicate (which is one way of defining a set in abstract). In particular, it is impossible to enumerate all bean types of the Instance, so you can't say what Set.iterator() produces.

But the wording of the 1.0 spec does not even imply that the set is infinite, because the CDI bean container lives in a closed world where the complete set of bean types known, so it is in fact a finite set.

This is unfortunately not true, because even if there's no bean of type e.g. String, you can still declare an IP of type Instance<String>. So the set of bean types of the Instance is in fact infinite. Well, it is technically finite, because there's a finite set of loaded classes in the JVM, but there's no way to enumerate them (and all of them have to be present in the set).


@mkouba

Infinite sets are perfectly normal and mathematically well-defined, and there's no particular reason why the CDI spec needs to avoid them when defining a bean in an abstract sense.

@gavinking No, they're not, because then you wouldn't be able to perform the typesafe resolution in a finite time interval. "The bean has a bean type that matches the required type." implies iteration over the set of bean types.

Not really. For typesafe resolution, the set of bean types is used merely as a predicate of "it this required type present in this set of bean types?". If the set of bean types is representable as a finite predicate (which it is in this case), it doesn't matter that the set itself is infinite.

@Ladicek @manovotn @Azquelt In any case, I think that this discussion is quite useless, waste of time and resources, and it does not bring any real benefits to CDI users. We should devote time to more important topics.

Well, it's one of the corner cases we find, and if we can close them, we're improving the spec. Granted, it's not as fun as adding new features, but it still feels relatively important.

@mkouba

mkouba commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Infinite sets are perfectly normal and mathematically well-defined, and there's no particular reason why the CDI spec needs to avoid them when defining a bean in an abstract sense.

@gavinking No, they're not, because then you wouldn't be able to perform the typesafe resolution in a finite time interval. "The bean has a bean type that matches the required type." implies iteration over the set of bean types.

Not really. For typesafe resolution, the set of bean types is used merely as a predicate of "it this required type present in this set of bean types?". If the set of bean types is representable as a finite predicate (which it is in this case), it doesn't matter that the set itself is infinite.

I'm not sure how this would work, but it does not really matter since we already agreed that the set is actually finite in this case.

@Ladicek @manovotn @Azquelt In any case, I think that this discussion is quite useless, waste of time and resources, and it does not bring any real benefits to CDI users. We should devote time to more important topics.

Well, it's one of the corner cases we find, and if we can close them, we're improving the spec.

In theory, yes. In practice, I don't think so.

@Ladicek

Ladicek commented Jun 22, 2026

Copy link
Copy Markdown
Member Author

Infinite sets are perfectly normal and mathematically well-defined, and there's no particular reason why the CDI spec needs to avoid them when defining a bean in an abstract sense.

@gavinking No, they're not, because then you wouldn't be able to perform the typesafe resolution in a finite time interval. "The bean has a bean type that matches the required type." implies iteration over the set of bean types.

Not really. For typesafe resolution, the set of bean types is used merely as a predicate of "it this required type present in this set of bean types?". If the set of bean types is representable as a finite predicate (which it is in this case), it doesn't matter that the set itself is infinite.

I'm not sure how this would work

You know very well how this could work: say you have a set of all even numbers -- it is clearly infinite, but the predicate behind is i % 2 == 0. In other words, the fact that you cannot implement Set.iterator() doesn't mean you cannot implement Set.contains(), which is all that matters here.

but it does not really matter since we already agreed that the set is actually finite in this case.

There are 2 ways to interpret "finite" here: only bean types of all beans, or only types loaded in the JVM. Both are finite, but while the first is straightforward to enumerate, the second is not. You might have thought it's the first, but it is in fact the second -- you can inject Instance<String> even if there's no bean with type String in its set of bean types.

@Ladicek

Ladicek commented Jun 22, 2026

Copy link
Copy Markdown
Member Author

Actually I might be wrong about Set.contains() being all that matters. I would have to look more deeply, perhaps we actually do need to iterate through all bean types of each bean during resolution... Eh. Monday morning :-)

@Azquelt

Azquelt commented Jun 22, 2026

Copy link
Copy Markdown
Member

@gavinking

The CDI 1.0 wording for the bean types of the Instance bean does imply an infinite set, and I think that's not correct.

OK, but why do you think that's not correct? You can't just assert this with no argumentation.

Sorry, reading back what I wrote, it's not very clear. I will try again to explain what I'm getting at.

The original wording implies that the set of bean types must be an infinite set, but I think it's quite possible to fulfil all of the other requirements for Instance (from 5.6 Programmatic Lookup) by instead having the set of bean types be Instance<X> and Provider<X> where X is an unbound type variable.

Therefore, this part of the spec is unnecessarily requiring a specific implementation when I think there are other valid options.

Infinite sets are perfectly normal and mathematically well-defined, and there's no particular reason why the CDI spec needs to avoid them when defining a bean in an abstract sense.

I agree, in an abstract sense, but here we're defining a bean in very concrete sense. The bean must have a Bean<T>, and getTypes() must return the set of bean types, as defined here.

In an abstract sense, I think it's fine to describe a bean that behaves during type resolution as if it has an infinite set of bean types and qualifiers, as long as the spec is worded so that getTypes() and getQualifiers() don't actually have to return an infinite set.

I agree that you could implement it that way, as long as all you don't need to call iterator or size on the set, but personally I would rather not, due to the risk of some code assuming that it can enumerate the set of bean types for any bean.

@Ladicek

Ladicek commented Jun 24, 2026

Copy link
Copy Markdown
Member Author

I think the biggest issue with infinite sets of bean types and qualifiers is that we also expose these sets through the BeanAttributes interface as a java.util.Set.

It seems obvious that these sets should be immutable, so all mutating methods can freely throw an exception. Implementing Set.contains() is trivial. Implementing Set.size() seems trivial too, because the javadoc clearly says "If this set contains more than Integer.MAX_VALUE elements, returns Integer.MAX_VALUE" and infinity is obviously more than 2^31 :-) Remains: iterator() and toArray(). Since the bean types of the Instance and Event both contain all classes accessible from the application, which is not enumerable, these 2 methods are impossible to implement.

I see 2 possible solutions:

  1. We specify that both the built-in beans (Instance and Event) have a finite set of bean types and qualifiers, plus we provide special rules for bean to IP assignability for these beans.
  2. We specify that both the built-in beans have an infinite set of bean types and qualifiers, plus we specify that BeanAttributes.getTypes() and BeanAttributes.getQualifiers() return special sets for these beans, where contains() and containsAll() are guaranteed to work properly, but calling the other methods leads to non-portable behavior.

I personally don't mind which way we go, but we should choose one path for both Instance and Event.

@Ladicek

Ladicek commented Jun 24, 2026

Copy link
Copy Markdown
Member Author

I added one new commit that implements solution 2 from my previous comment. It feels a lot less intrusive to the core part of the specification and only affects an API that noone actually calls on the Instance and Event beans.

I believe this is now ready for merge.

@gavinking gavinking left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM now, I left a note about one minor grammatical issue.

The specified type must not be a wildcard type.
If an injected `Event` has a specified type that is a wildcard type, the container treats it as a definition error.
If a programmatically obtained `Event` has a specified type that is a wildcard type, non-portable behavior results.
If programmatically obtaining an `Event` whose specified type is a wildcard without lower bound is attempted, an `IllegalArgumentException` is thrown.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a grammar issue here: "If programmatically obtaining ...".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean this should read "If programmatically obtaining an Event is attempted whose ..." instead of "If programmatically obtaining an Event whose ... is attempted"? I'm not a native speaker, so I'll gladly be corrected here -- I know the sentence is hard to read.

@gavinking gavinking Jun 26, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, first, the sentence is missing a subject, which makes it hard to tell who or what is doing the "programmatic obtaining". But also "If programmatically obtaining an Event is attempted" is very difficult to parse because the passive voice often feels very awkward in English.

You could say:

If the application attempts to programmatically obtain an Event whose specified type is a wildcard without a lower bound, an IllegalArgumentException must be thrown.

or something like that.

But I would prefer something much more specific:

If a TypeLiteral representing a wildcard instantiation of Event with no lower bound on the specified type is passed to an operation which performs programmatic bean resolution, the operation must throw IllegalArgumentException.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could say:

If the application attempts to programmatically obtain an Event whose specified type is a wildcard without a lower bound, an IllegalArgumentException must be thrown.

Oh this sounds just about perfect! Much better than what I had.

But I would prefer something much more specific:

If a TypeLiteral representing a wildcard instantiation of Event with no lower bound on the specified type is passed to an operation which performs programmatic bean resolution, the operation must throw IllegalArgumentException.

This seems like the same thing, only more detailed? Unless you disagree, I'll use your first proposal.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either is fine; yes, they say the same thing.

I liked the second one because I thought the reader might not know how to programmatically request a type with a wildcard. But it's not a big deal.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks!

@Azquelt Azquelt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still have issues with these changes, particularly the requirement for the set of bean types and qualifiers to be infinite, but we've been around this enough, so if everyone else wants to merge it like this I'm not going to object any further.

Comment on lines +35 to +40
/**
* Obtains the {@linkplain jakarta.enterprise.inject bean types} of the bean.
* <p>
* If the set of bean types of the bean is infinite, only the {@link Set#contains(Object)}
* and {@link Set#containsAll(Collection)} methods may be called on the result;
* calling the other {@code Set} methods leads to non-portable behavior.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So really we're saying you can only safely call contains or containsAll on the result, since you don't know whether the set is infinite.

That might break existing debugging utilities that dump out information about a bean if the non-portable behaviour is to throw a runtime exception, or return an Iterator that does attempt to iterate over the types.

Seems unfortunate.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. I explicitly mentioned that all user-defined beans have a finite set of bean types and qualifiers, so all (read-only) methods are usable virtually always.

Comment on lines +799 to +800
* `Instance<X>` and `Provider<X>` for every legal bean type `X` in its set of bean types,
* all possible qualifiers in its set of qualifiers,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remain opposed to this requirement because:

  • it requires every implementation to create a badly behaved implementation of Set to represent an infinite set
  • it prevents other implementations of the Instance bean which would work fine and meet the other requirements of Instance

For example, I think you could have a bean with:

  • bean types Instance<X> and Provider<X> where X is a type variable
  • a special qualifier @IgnoreQualifiers that your implementation interprets as "Ignore qualifiers when resolving comparing this bean during typesafe resolution"

I think you could also just have a special bean that your implementation treats differently during typesafe resolution.

I think such an implementation could follow all of the other the rules for Instance, but would not be allowed due to this requirement. I don't understand why we feel it necessary to forbid these implementations.


As a final suggestion, we could put this:

The container must provide a built-in bean that:

* is assignable to an injection point during typesafe resolution as if it has
    * `Instance<X>` and `Provider<X>` for every legal bean type `X` in its set of bean types,
    * all possible qualifiers in its set of qualifiers,
* has scope `@Dependent`,
* has no bean name, and
* has an implementation provided automatically by the container.

However, this argument has gone on long enough, so if you still want to merge it as is I won't protest any longer.

Comment on lines +679 to +680

* the _required type_ is the type parameter specified at the injection point, and
* the _required type_ is the type argument specified at the injection point, unless the type argument is a wildcard with upper bound (or an unbounded wildcard), in which case the required type is the wildcard's upper bound (or `java.lang.Object`), and

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use Gavin's wording from https://github.com/jakartaee/cdi/pull/982/changes#r3444003069 here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

* <li>the <em>required type</em> is the type parameter specified at the injection point, and</li>
* <li>the <em>required type</em> is the type argument specified at the injection point, unless the type argument
* is a wildcard with an upper bound (or an unbounded wildcard), in which case the specified type is the wildcard's
* upper bound (or {@code java.lang.Object}), and</li>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The suggested change to the spec wording could be made here too.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@Ladicek
Ladicek force-pushed the event-instance-meaningful-wildcards branch from e62db01 to 11ed2a4 Compare July 13, 2026 14:41
@Ladicek

Ladicek commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

Hi @Azquelt, sorry for not replying sooner, I was too tired of dealing with this issue and was assigned some unexpected work too. I applied a few fixes that you pointed out, but I have no idea what @gavinking would say to just removing infinite sets of bean types / qualifiers and special-casing typesafe resolution instead :-) In any case, here's a specification proposal that uses this approach (solution 1 from my comment #982 (comment)): https://github.com/Ladicek/cdi-spec/commits/event-instance-meaningful-wildcards-alternative/

Ladicek and others added 3 commits July 24, 2026 13:52
The type parameter of `Event` is naturally contravariant, so injecting
`Event<? super FooBar>` is meaningful. Injecting `Event<?>` or
`Event<? extends FooBar>` is meaningless.

The type parameter of `Instance` is invariant, but all methods that require
typesafe resolution have the type parameter in a covariant position,
so for the purpose of typesafe resolution, `Instance<? extends FooBar>`
is meaningful.

For the meaningful wildcards, special cases exist in the definition of
an `Event`'s specified type and `Instance`'s required type that force
stripping of the wildcard and using its bound.

For the meaningless wildcards, a different approach than before is used.
Previously, extra rules were added. These additions are removed and instead,
the existing rules that prohibit injecting raw `Event` / `Instance` are
extended so that injecting these types with meaningless wildcard arguments
is prohibited as well.
The change was well-meant, but made the specification of built-in
`Instance` a lot worse. Each bean has a set of bean types, and this
change removed that definition from the built-in bean for `Instance`.
This commit specifies that the built-in beans for `Instance` and `Event` have:

- a finite set of bean types: `Instance<X>` and `Provider<X>` for `Instance`
  and `Event<X>` for `Event`, where `X` is a type variable without bounds
- a finite set of qualifiers: `@Default`

This doesn't override requirements stated elsewhere, so both the `Instance`
and `Event` beans also have `Object` in their set of bean types, as well as
`@Any` in their set of qualifiers.

This requires altering the rules for bean assignability. Instead of the usual
rules, the `Instance` bean is assignable to all injection points with required
types `Instance<X>` or `Provider<X>`, where `X` is a legal bean type, and
the `Event` bean is assignabile to all injection points with required type
`Event<X>` where `X` is a type without type variables. In both cases, required
qualifiers are ignored.
@Ladicek
Ladicek force-pushed the event-instance-meaningful-wildcards branch from 11ed2a4 to fb9120b Compare July 24, 2026 12:01
@Ladicek

Ladicek commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

I pushed 2 branches showing my 2 approaches:

This PR now contains the first approach.

@manovotn manovotn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @Ladicek.
I like the current approach (no infinite sets) a lot more, hence my approval.

Let's keep this PR open until Tue CDI meeting. Assuming no objections arise until then, I'd like to merge this so that we can move on to the actual CR/Final release.

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 this pull request may close these issues.

5 participants