Skip to content

Commit 4e1fe9a

Browse files
authored
Merge pull request #5363 from smowton/smowton/feature/generic-lambda-boxing
Java frontend: support lambda autoboxing involving generics
2 parents 60ba633 + 040542b commit 4e1fe9a

File tree

11 files changed

+197
-46
lines changed

11 files changed

+197
-46
lines changed
189 Bytes
Binary file not shown.
191 Bytes
Binary file not shown.
299 Bytes
Binary file not shown.
174 Bytes
Binary file not shown.
266 Bytes
Binary file not shown.
2.84 KB
Binary file not shown.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
public class Test {
2+
3+
interface TakesT<T> {
4+
public Object take(T t);
5+
}
6+
7+
interface YieldsT<T> {
8+
public T yield();
9+
}
10+
11+
interface TakesInt {
12+
public Object take(int i);
13+
}
14+
15+
interface TakesLong {
16+
public Object take(long l);
17+
}
18+
19+
interface YieldsInt {
20+
public int yield();
21+
}
22+
23+
public static Object takesLong(long l) { return l; }
24+
25+
public static short yieldsShort() { return (short)1; }
26+
27+
public static void test() {
28+
29+
// Implement a generic interface using a primitive-typed function
30+
// that requires us to unbox the Short then widen it:
31+
TakesT<Short> takesShort = Test::takesLong;
32+
assert takesShort.take((short)1).equals(1L);
33+
34+
// Surprisingly, the following case doesn't compile despite
35+
// being type-safe and symmetrical to the one above:
36+
// YieldsT<Long> yieldsLong = Test::yieldsShort;
37+
38+
// So instead, here's a simpler case, that just requires us
39+
// to box the return value, not widen it then box:
40+
YieldsT<Short> yieldsShort = Test::yieldsShort;
41+
assert yieldsShort.yield() == (short)1;
42+
43+
// Now test the reverse: using an instantiated generic to
44+
// implement a primitive-typed interface:
45+
46+
TakesT<Long> takesLong = x -> x + 1;
47+
// Again this doesn't compile:
48+
// TakesInt takesInt = takesLong::take;
49+
// So here's a simpler example that only boxes to implement the
50+
// primitive interface, rather than boxes then widens:
51+
TakesLong takesPrimitiveLong = takesLong::take;
52+
assert takesPrimitiveLong.take(1L).equals(2L);
53+
54+
// And finally, using an instantiated generic to satisfy a primitive
55+
// return value. This one does work with the conversion requiring both
56+
// to unbox the value and to widen it.
57+
YieldsT<Short> yieldsShortGeneric = () -> (short)1;
58+
YieldsInt yieldsInt = yieldsShortGeneric::yield;
59+
assert yieldsInt.yield() == 1;
60+
61+
// Check we got to the end:
62+
assert false;
63+
64+
}
65+
66+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
Test
3+
--function Test.test --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar`
4+
\[java::Test\.test:\(\)V\.assertion\.1\] line 32 assertion at file Test\.java line 32 function java::Test\.test:\(\)V bytecode-index 15: SUCCESS
5+
\[java::Test\.test:\(\)V\.assertion\.2\] line 41 assertion at file Test\.java line 41 function java::Test\.test:\(\)V bytecode-index 29: SUCCESS
6+
\[java::Test\.test:\(\)V\.assertion\.3\] line 52 assertion at file Test\.java line 52 function java::Test\.test:\(\)V bytecode-index 50: SUCCESS
7+
\[java::Test\.test:\(\)V\.assertion\.4\] line 59 assertion at file Test\.java line 59 function java::Test\.test:\(\)V bytecode-index 68: SUCCESS
8+
\[java::Test\.test:\(\)V\.assertion\.5\] line 62 assertion at file Test\.java line 62 function java::Test\.test:\(\)V bytecode-index 74: FAILURE
9+
^EXIT=10$
10+
^SIGNAL=0$

jbmc/src/java_bytecode/java_utils.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,33 +72,41 @@ get_java_primitive_type_info(const typet &maybe_primitive_type)
7272
type_info_by_primitive_type = {
7373
{java_boolean_type(),
7474
{"java::java.lang.Boolean",
75-
"java::java.lang.Boolean.valueOf:(Z)Ljava/lang/Boolean;"}},
75+
"java::java.lang.Boolean.valueOf:(Z)Ljava/lang/Boolean;",
76+
"java::java.lang.Boolean.booleanValue:()Z"}},
7677
{java_byte_type(),
7778
{"java::java.lang.Byte",
78-
"java::java.lang.Byte.valueOf:(B)Ljava/lang/Byte;"}},
79+
"java::java.lang.Byte.valueOf:(B)Ljava/lang/Byte;",
80+
"java::java.lang.Number.byteValue:()B"}},
7981
{java_char_type(),
8082
{"java::java.lang.Character",
8183
"java::java.lang.Character.valueOf:(C)"
82-
"Ljava/lang/Character;"}},
84+
"Ljava/lang/Character;",
85+
"java::java.lang.Character.charValue:()C"}},
8386
{java_double_type(),
8487
{"java::java.lang.Double",
8588
"java::java.lang.Double.valueOf:(D)"
86-
"Ljava/lang/Double;"}},
89+
"Ljava/lang/Double;",
90+
"java::java.lang.Number.doubleValue:()D"}},
8791
{java_float_type(),
8892
{"java::java.lang.Float",
8993
"java::java.lang.Float.valueOf:(F)"
90-
"Ljava/lang/Float;"}},
94+
"Ljava/lang/Float;",
95+
"java::java.lang.Number.floatValue:()F"}},
9196
{java_int_type(),
9297
{"java::java.lang.Integer",
9398
"java::java.lang.Integer.valueOf:(I)"
94-
"Ljava/lang/Integer;"}},
99+
"Ljava/lang/Integer;",
100+
"java::java.lang.Number.intValue:()I"}},
95101
{java_long_type(),
96102
{"java::java.lang.Long",
97-
"java::java.lang.Long.valueOf:(J)Ljava/lang/Long;"}},
103+
"java::java.lang.Long.valueOf:(J)Ljava/lang/Long;",
104+
"java::java.lang.Number.longValue:()J"}},
98105
{java_short_type(),
99106
{"java::java.lang.Short",
100107
"java::java.lang.Short.valueOf:(S)"
101-
"Ljava/lang/Short;"}}};
108+
"Ljava/lang/Short;",
109+
"java::java.lang.Number.shortValue:()S"}}};
102110

103111
auto found = type_info_by_primitive_type.find(maybe_primitive_type);
104112
return found == type_info_by_primitive_type.end() ? nullptr : &found->second;

jbmc/src/java_bytecode/java_utils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ struct java_primitive_type_infot
4040
/// Full identifier of the boxed type's factory method that takes the
4141
/// corresponding primitive as its sole argument
4242
const irep_idt boxed_type_factory_method;
43+
/// Full identifier of the most general boxed-type method that yields this
44+
/// type. For most primitives that means the corresponding method on
45+
/// java.lang.Number; for Character and Boolean it means the method on their
46+
/// specific boxed type.
47+
const irep_idt unboxing_function_name;
4348
};
4449

4550
/// If \p primitive_type is a Java primitive type, return information about it,

0 commit comments

Comments
 (0)