-
Notifications
You must be signed in to change notification settings - Fork 45
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
org.jboss.cdi.tck.tests.definition.bean.BeanDefinitionTest #429
Comments
We recently encountered this in Quarkus as well but in the end concluded that the test is actually correct in that it follows the JLS. IIRC the reasoning was that once you inherit a raw type, then the types you discover and inherit from hierarchy will also be raw types. I can try to look it up tomorrow. |
@jeanouii Did some digging and here is the issue we had for it at the time - quarkusio/quarkus#30571 The JLS quote is here and goes like this:
Which IMO implicates that the TCK test is correct. |
Yeah, I also filed an issue to clarify this in the specification: jakartaee/cdi#645 But I'm pretty convinced the TCK is correct. It's just that at the point this part of the CDI spec was written, even the JLS didn't make a very good distinction between superclasses and superclass types (and interfaces / interface types). |
Thanks for the feedback, it helps. I think there are 2 main things here
And then, there is the question, is this what we want or do we want to change the spec in one way or the other? As part of this challenge, only the first part matters in my opinion. And in that area, I think the test is at least ambiguous or too restrictive compared to the spec. If we look at the spec again, it says
Per spec, both interface and parametrized type are valid. There is not additional information on when we pick one or the other if we have the choice. So either we update the test to accept both in the assert, or we update the test such as the super interface is not a parametrized type or we update the spec to be more clear on what we expect. What do you think? |
There's no choice though. The relevant section of the CDI specification says:
If the bean has a raw type in its supertype hierarchy, then all supertypes of that raw type are erasures. This is specified directly in the Java Language Specification, chapter 4.8. Here's a quote from JLS 17:
Here's a quote from JLS 8:
The JLS 8 quote is closer in time to when the relevant part of the CDI specification was written, so naturally the CDI specification uses similar wording. In my opinion, the JLS 17 wording is more precise (because it makes a distinction between supreclasses and superclass types), but the meaning is the same. |
Lemme discuss this with the team please. There is something I can't buy but it's probably a lack of understanding of the specification. My apologies for that. |
No worries -- it tooks us a while to figure this out as well. |
True, and there are tests for both - this test is specifically looking at what should happen if your bean extends a raw type that itself has a type hierarchy with parameterized typed in it. I am with Ladislav in that you need to follow JLS to determine what types you discover (and hence accept as bean types). |
I've discuss this with the team which does understand the choice but does not fully agree with it because it has some side effects (for example 2 decorators on parametrized types). I can't argue against the spec, so I'll maintain the challenge because the TCK is more restrictive than the specification which allows both. And it implies 3 possible choices 1/ The TCK forces 3/ by choice. |
I disagree that the specification allows both. What is a supertype of a raw type is clearly defined by the JLS. |
Actually, let me expand on that a little. Clearly a parameterized type is a legal bean type, that is, it may be present in the set of bean types. However, the set of bean types may not contain arbitrary types drawn out of thin air. The set of types that eventually become bean types is defined in the CDI specification here: https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0.html#managed_bean_types (for managed beans; similar definitions exist for producer beans).
Now, the managed bean class in question is @Dependent
class MyRawBean extends MyBean {
} @Dependent
class MyBean<T> implements MyInterface {
} interface MyInterface extends MySuperInterface<Number> {
} interface MySuperInterface<T> {
} Now, clearly From there, it follows that all supertypes of From that, it directly follows that And per the CDI specification, the set of bean types of Hope that's a bit more clear now. |
@Ladicek where exactly in the JLS did you think your argument is supported? Because e.g. 8.1.4 rather supports Jean-Louis argumentation. Note that whole JLS 8.1.4 does NOT mention |T|.C ! txs |
I quoted the precise sentence from the JLS above. See chapter 4.8:
EDIT: chapter 4.10.2 says the same:
|
Ladislav's explanation is correct. |
You missed my point. nowhere in 8.1.4 https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.1.4 is mentioned that |T|.C (type erasure that is) gets triggered! Nowhere in the CDI spec is it defined neither. |
The very first sentence of chapter 8.1.4 says:
What is a direct superclass type is defined by chapter 4.10.2, which I linked to above. |
The point is that the bean type resolution and the matching to the injection points DEPENDS on whether it is a bean injection point, a decorator or a producer bean. Thus whether type erasure is required or not resolved in the bean type calculation but at the point when it gets applied. |
I am not sure I can see how is existence of slightly different assignability rules for various scenarios related to my example? |
Thanks for your patience and references. I think I can fully understand the use case we are talking about and what this specific test is trying to do. This ONLY applies to Do you agree it would be different if The direct impact of this use case as defined in https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0.html#assignable_parameters is that Edit: Sorry, I realized @Ladicek rephrased and gave an example. I was reading and playing around and click send without noticing your replies. I rephrased it this way, with some examples to make sure I understood correctly. |
Yes.
Yes.
The
I believe the current CDI specification wording doesn't consider raw |
Awesome. Thanks, then I see a couple of actions for non expert eyes like mine
|
Hi guys, Read everything and think several statements are against CDI and Java SE as well (JLS), concretely I think the core issue comes from
The erasure happens and must be respected, this is right and written everywhere - not requoting what is already done - BUT you must take into account if the erasure happens on each type of the hierarchy and this is where the error comes from IMHO. If we try to phrase it easier and without the mathematical notations to really make it obvious: types must respect reflection, this is what java does at the end in terms of runtime (keep in mind CDI is 100% targetting a runtime whatever it is). I know the compiler got enhanced a bit and can solve more cases these days - due to lambdas mainly - so it can be the most restrictive source java has as of today and it is fully aligned on the CDI requirement to provide type assignment to extensions in all events - otherwise extensions must reimplement the resolution each time so I guess we are good and the test got a shortcut we should fix. |
It appears as though the term "raw type" is a term that describes a reference to a generic class, not the generic class itself. If that's the case, we're all technically wrong as we've all been using it to describe the class/interface/bean and argue if a bean is or isn't a raw type when in reality a generic class that has any unbound parameters can be referred to either way (raw or parameterized). Depending on what decision the referring side makes, it will get different compiler behavior (erasure) enforced on its use of the reference. What makes this ambiguous in the context of CDI is that we're using JLS semantics that 1) support either raw or parameterized, and 2) apply to referring side to argue CDI should support only one or the other, using just the bean definition. I think we should definitely work this out and update the spec. It would seem the behavior that would most match the JLS is that we are able to support injection points that use beans either as raw types or parameterized types. It's unclear if any implementation supports that at this point, however. Spec text
The key part is where it says the raw type is defined as "the reference type." Again, we should support both raw and parameterized types, but the JLS does actively steer people away from raw types.
That last sentence really helps me understand as if "raw type" was meant to apply to how the class/bean was defined, it'd basically be saying it's bad to leave undefined generic types in your class. That's obviously the point of generics, so it's more understandably saying "don't reference generic classes without supplying parameters." ExperimentationsNo unbound typesI created a class that has 3 generic types, but doesn't leave any of the types undefined (unbound).
No decisions were left to the referring code (i.e. it can't be a raw type, nor can parameters be supplied) and the compiler will enforce things like this: Mixed bound and unbound typesI updated that class that has 3 bound generic types so there is a 4th generic type that is unbound (
The value of referenced as a raw typeIf the referring code does not supply parameters (uses it as a raw type), the compiler does not seem to enforce any of the generics, even the other 3 that are defined. referenced as a parameterized typeIf the referring code does supply parameters, even if that is Final thoughtsSeems from a spec perspective we should ensure it requires us to support both: allowing generic classes that have unbound types to be referred to as raw or parameterized types. I'm not entirely sure how this would be done in the guts of a CDI implementation or how we would test what types would be exposed during deploy or build-time events. I'd need to think about it further, but the first instinct would be an approach where the bean definition had as much information it as possible -- all the generic types are visible in the bean definitions even if some are still unbound variables. Extensions can then see all the type information available (know what we know) and act on it or potentially modify it. For resolution, however, we would need to be prepared to permit both raw and parameterized injection points. This becomes incredibly tricky as CDI flags ambiguous references and this could create a lot of them. The JLS doesn't have that concept, so this would be our problem to solve. |
I find it incredibly important to make a distinction between declarations and types. On many places, the CDI specification doesn't, and so didn't the Java Language Specification many years ago (I'm looking at JLS 8 specifically). The TCK test being challenged here checks the set of bean types. For managed beans, that set is defined in the CDI specification like so:
Similar wording exists for producer beans. The very first sentence is inaccurate: classes in Java do not extend classes and implement interfaces -- they extend class types and implement interface types. As I said, even JLS 8 wasn't 100% precise about this. Compare chapter 8.1.4 from JLS 8:
with chapter 8.1.4 from JLS 17:
(Emphasis not mine.) But even JLS 8 admits that the As illustrated above, it is relatively common to refer to declarations when talking about types, which leads to shorter sentences in case the meaning is obvious. There's an inherent ambiguity in that, though. I will further use the terms as JLS 17 uses them and will try to avoid the ambiguity. In addition to a direct superclass type, JLS also defines what a direct superclass is: the class named by the direct superclass type. Further, the superclass relation is a transitive closure of the direct superclass relation. This is all chapter 8.1.4 (similar definitions exist for interfaces). Now, chapter 4.10 defines the supertype (and subtype) relation: it is a transitive closure over the direct supertype relation. The direct supertype relation is defined in the same chapter. For a type of a non-generic class, direct supertype = direct superclass type + direct superinterface types; for raw type of a generic class, direct supertype = erasure of direct superclass type + erasure of direct superinterface types; for a parameterized type of a generic class, direct supertype = application of type arguments to the direct superclass type + application of type arguments to the direct superinterface types (note that there's a little bit more to it, I'm shortening here, hopefully without harm). Finally, chapter 4.8 defines what a raw type is; among others, it defines a raw type of a generic class. It also says:
Unlike direct superclass type, the "superclass types" are not defined anywhere, which is a little confusing. I take it the implied meaning is "the types of superclasses, as defined by the supertype relation", or something like that. (The term is used on several other places outside of chapter 4.8, and I believe it is used in accordance with my interpretation here.) In that sense, I'd love to hear a different interpretation of the JLS that leads to different results, but so far, I haven't. |
I can agree it is maybe not explicit but it is the case both ways and ultimately the intent is to be aligned on java usage while it matches CDI usage and here there is no ambiguity that |
I don't see how this fits the JLS interpretation or proves Ladislav's earlier in-depth explanation wrong?
are very broad, vague and easily lead to inaccuracies. |
Ok so let's do the exercise:
In this ticket example there is NO link between the class generics and the interface so substitutions are only done in So there is no erasue per JLS. This is compliant with the compiler and runtime behaviors of Java. If we apply it we get that: Data:
Logic:
Then using 8.1.4 and 8.1.5+9.1.3 you get the transitive nature of the inheritance:
So at the end we comply to JLS, to java compilers, to java runtimes and CDI event and extension requirements so all good, no? QED? |
Okay, I'll repeat the classes, as you got them wrong:
It is very obvious that Quoting JLS 8, as you did, we have
So if we have
We also have
From there, we see how the following works:
So: any reason why the provisions of chapter 4.8 do not apply here? |
@Ladicek my point is that chapter 4.8 applies but does not involve
Start from 4.8 does not mean "if there is an erasure everything is erased" but only "if there is an erasure the the related types are erased recursively". It is better explained in parts 8. Gues the error is
This is not the case. I agree |
Without resolution to JDK-8044366, you IMO shouldn't jump to the conclusion that one of those behaviors is correct as you can then end up violating JLS with CDI requirement. |
@Ladicek I can send a PR of course |
…ce<Number> for raw-type types
@manovotn @Ladicek if you're both ok with the Implementation teams should really not be approving their own challenges, so that rules out @struberg @rmannibucau @jeanouii and myself. |
Oh, I thought I already added that yesterday, sorry - will fix it now |
@manovotn no worries and thank you everyone for the considerable amount of time on this. |
+1 Thank you @manovotn ! |
There is actually not a real raw type support in the Spec for this specific case as of yet. I remember we've been ambiguous in CDI-1.0, but this got pinned down a long time ago. The following cases are valid and do work:
and
But the following code must result in an
This is also not allowed afaict:
|
Typesafe resolution is based on bean types and in this issue, we've agreed that bean types in this scenario aren't clear. I understand that that's how it works for OWB because those are the types you end up getting. which is why I asked about it in my earlier comment (see #429 (comment)). |
@manovotn not sure I get your last comment, you mean with weld what would be the output of:
It is the same than openwebbeans so the bean types must (as must in the spec) be aligned on the resolution so the behavior you explained before is wrong (I guess weld uses a bean type + resolution pass to make it behaving properly but as we saw it is against the spec which must have a consistent list of types used for resolution without another filtering pass on this aspect for this case), no? |
As this challenge is now accepted, we should likely move the conversation of what to do next somewhere else. I've filed an issue jakartaee/cdi#653 to hopefully provide a place we can continue to discuss. I recommend we close this issue. |
It is more about the fix for the challenge. Shouldnt be relaxed but really refined. |
The other example:
This is unsatisfied and I don't think there is any dispute about that in this issue whatsoever; this follows assignability rules for beans with type params and there is no way you can assign
If that's what you think then I am not sure we've reached an agreement on how to fix it. |
Well, from weld types the string resolution should work - was even explained as intended in this thread. It is obviously not the case so weld and owb behave the same on resolution, spec does align resolution and types so means weld should be aligned on that in its type list. Then once done raw type of super interface has nothing to do there due to cdi parameterized type resolution (so types) rules where param match exactly. |
Sorry but I can't see where you're headed with this. Firstly, you need to determine types of any given bean. This is where Weld and OWB differs and we've gone over why and that none of the approaches is right or wrong and that both should be accepted due to the JDK bug and possibly aligned after that JDK bug is resolved. Agreed? Secondly, you then use previously determined set of types to perform typesafe resolution. In this, both impls perform the same (correct) job as far as I can tell. |
@manovotn agree we differ in 1st and behave the same in 2nd but CDI spec requires 1 and 2 to be aligned so 2 is the behavior and types you must (as must in the spec) see in types. |
Type resolution says nothing about what types you should see there, it only defines matching rules that work on predetermined types. |
(part 2.1)
part 2.1.2 and if you follow the part 2.4.2 there is no magic there enabling weld behavior. Concretely type resolution defines what must be in types. |
More like nothing disabling weld behavior?
So yes, we arrive at different types in this one particular case due to the JDK bug. Which is exactly why 2.4.2 works for Weld just as well as for owb 🤷 |
@manovotn what you state is right but you don't put things together. Indeed you can get a raw type but you have to use the types present in the bean to resolve the beans at runtime, weld obviously does something more there since it does not have the parameterized type but resolves it properly. Doing this additional step is againsrt the spec (once again the rational being to present the same view to extensions and runtime). |
I'd just like to point out that I believe there is nothing ambiguous in the CDI specification itself. We're "just" hitting an ambiguity in the JLS. My understanding of that JLS ambiguity is that it allows raw The comments above that |
@Ladicek thing is there is nothing ambiguous on the resolution - and it works well for all impl - and spec states resolution and types are aligned so agree there is nothing ambiguous but proves test is wrong. |
I don't know how else to say that, there is no extra step. We identify set of types and we use them and nothing else to perform typesafe resolution.
What parameterized type? |
Ok, got it, my error was that weld resolves MyBean and not MyRawBean, mea culpa. Fine to stick on the generic resolution definition, if it is recursive (OWB) or absolute (Weld) at spec level. Thanks for that last rephrasing @manovotn . |
Per the current TCK Process documentation, please find bellow a challenge related to CDI TCK. Can someone please add the
challenge
label which is required by the TCK Process but not available to everyone to add.The relevant specification version and section number(s)
https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0.html#bean_types
https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0.html#managed_bean_types
The coordinates of the challenged test(s)
Only following test in the class are related to the challenge
The exact TCK version
CDI TCK version 4.0.7
The implementation being tested, including name and company
Apache OpenWebBeans 4.0.0-SNAPSHOT from the Apache Software Foundation
A full description of why the test is invalid and what the correct behavior is believed to be
The specification defines the following
And then provides a list of valid and invalid type. But I'm facing a case where it's ambiguous, at least I can find different behaviors so it's worth either updating a bit the TCK or improving the specification.
The test may fail with the assert on MySuperInterface.
MySuperInterface
can be seen from a specification point of view as an interface or as a parametrized type. The 2 are valid per https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0.html#managed_bean_types unless my reading is wrong.Any supporting material; debug logs, test output, test logs, run scripts, etc.
Here are 2 screenshots to show 2 different behaviors for the same specification
One option
Second option
The text was updated successfully, but these errors were encountered: