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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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)