Unboxing conversion of generic types with box as a bound
Tagir Valeev
amaembo at gmail.com
Wed Dec 17 10:30:45 UTC 2025
On Tue, Dec 16, 2025 at 4:37 PM Kevin Bourrillion <
kevin.bourrillion at oracle.com> wrote:
> Interesting. That also represents an unexpected inconsistency between x++
> and x+=1.
>
Wow! That's a crazy puzzler!
>
>
>
> * Confidential- Oracle Internal From: *Maurizio Cimadamore <
> maurizio.cimadamore at oracle.com>
> *Date: *Tuesday, December 16, 2025 at 2:07 AM
> *To: *Tagir Valeev <amaembo at gmail.com>, Kevin Bourrillion <
> kevin.bourrillion at oracle.com>
> *Cc: *compiler-dev at openjdk.org <compiler-dev at openjdk.org>
> *Subject: *Re: Unboxing conversion of generic types with box as a bound
>
> Hi Tagir,
> I agree with all Kevin said. I'm also with you re. things being
> inconsistent.
>
> The reason as to why javac allows what it allows is that it implements a
> more lax version of "unboxing" (mostly because of old issues that were
> "fixed" in Java 6, see end of this email). E.g. anything whose supertype is
> a boxed type can be unboxed, and so type variables get caught in the mix.
>
> But since String doesn't use unboxing conversion, there's no special
> behavior there.
>
> There's also other oddities and anomalies -- for instance:
>
> ```
> <X extends Integer> void m(Integer i, X x) {
> x++; // ok
> x = (Integer)x; // error
> }
> ```
>
> The first statement being ok means that basically we know that X can only
> be instantiated with Integer (as Integer is final). But then why does the
> cast fail?
>
> I think more work needs to be done on both sides (spec _and_ compiler) to
> flesh out this story in full.
>
> Maurizio
>
> [1] - https://bugs.openjdk.org/browse/JDK-5043021
> [2] - https://bugs.openjdk.org/browse/JDK-6240565
> On 16/12/2025 08:04, Tagir Valeev wrote:
>
> Hello, Kevin!
>
> Indeed, this is helpful, thank you. It looks like, we should support this
> case in IntelliJ IDEA as well. Here's another sample that might be related:
>
> private static <X extends String> void concat(X x) {
> System.out.println(x+1);
> }
>
> void main() {
> concat("hello");
> }
>
> This code is not compilable, we have a compilation error:
>
> java: bad operand types for binary operator '+'
> first type: X
> second type: int
>
> It looks inconsistent to me that 'X extends Integer' is being treated as
> Integer, but 'X extends String' is not being treated as String. But
> probably it's a different case, as unboxing is not involved here.
>
> With best regards,
> Tagir Valeev.
>
> On Tue, Dec 16, 2025 at 12:01 AM Kevin Bourrillion <
> kevin.bourrillion at oracle.com> wrote:
>
> Hey Tagir, we were discussing this internally a couple months ago, and I
> felt there was a general consensus that this is indeed a spec bug. Javac's
> permissiveness seems totally reasonable here.
>
> We either need to insert more WRCs before unbox conversions (like the one
> you found), or (a seemingly-simpler approach that I hope will work) just
> tweak the specification of unboxing conversion itself so it applies to any
> *subtype* of Integer-etc., instead of only the exact type. (Of course
> this also affects intersection types and capture types as well as the case
> in your example.)
>
> We didn't jump on fixing it yet but it's slated to happen sooner or later
> as part of a series of type-conversion cleanups we're working on. Hopefully
> just getting this answer unblocks you for now?
>
>
>
>
>
> Confidential- Oracle Internal
> *From: *compiler-dev <compiler-dev-retn at openjdk.org> on behalf of Tagir
> Valeev <amaembo at gmail.com>
> *Date: *Monday, December 15, 2025 at 9:21 AM
> *To: *compiler-dev at openjdk.org <compiler-dev at openjdk.org>
> *Subject: *Unboxing conversion of generic types with box as a bound
>
> Hello!
>
> I noticed that the following program is compilable without errors using
> OpenJDK 25 javac:
>
> private static <X extends Integer> void add(X x) {
> System.out.println(+x);
> }
>
> void main() {
> add(10);
> }
>
> I wonder if it's correct. The JLS 15.15.3 says [1]:
>
> The type of the operand expression of the unary + operator must be a type
> that is convertible (§5.1.8) to a primitive numeric type, or a compile-time
> error occurs.
>
> The JLS 5.1.8, in turn, says [2]:
>
> Unboxing conversion treats expressions of a reference type as expressions
> of a corresponding primitive type. Specifically, the following eight
> conversions are called the unboxing conversions:
> ...
> From type Integer to type int
> ...
> At run time, unboxing conversion proceeds as follows:
> ...
> If r is a reference of type Integer, then unboxing conversion converts r
> into r.intValue()
>
> In my case, the type of reference is X, rather than Integer. So my
> question, should I read 'type Integer' here as 'type Integer, or any
> generic type whose upper bound is type Integer'? In other words, should we
> assume that the X type declared as `X extends Integer` is convertible to a
> primitive numeric type? It looks like, here a widening reference conversion
> (5.1.5) happens before an unboxing conversion, but 15.15.3 does not say
> about widening reference conversion. I've also checked 5.6 "Numeric
> contexts", but it also does not mention that before unboxing conversion, a
> widening reference conversion might occur.
>
> Could anybody please clarify whether the compiler is wrong or I read the
> specification incorrectly? Thank you in advance!
>
> With best regards,
> Tagir Valeev
>
> [1]
> https://docs.oracle.com/javase/specs/jls/se25/html/jls-15.html#jls-15.15.3
> [2]
> https://docs.oracle.com/javase/specs/jls/se25/html/jls-5.html#jls-5.1.8
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/compiler-dev/attachments/20251217/a2738bb3/attachment-0001.htm>
More information about the compiler-dev
mailing list