Skip to content

Java frontend: support lambda autoboxing involving generics #5363

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

Merged

Conversation

smowton
Copy link
Contributor

@smowton smowton commented May 28, 2020

Based on #5359, extending that implementation to also account for generics

smowton added 7 commits May 27, 2020 16:33
Turns out the usual lambda metafactory can apply boxing/unboxing, so you might see a situation like

int myMethod(int i) { ... }

interface MyLambdaType { Integer f(Integer i); }

MyLambdaType mlt = SomeType::myMethod;

And contrary to my prior expectations, the compiler doesn't generate a method stub to do the type conversion,
it just directly requests an invokestatic lambda pointed at myMethod, regardless of the int vs. Integer clash.
The lambda metafactory (which re-implement in JBMC) is then responsible for adding the necessary conversions.
This broadens our support for lambdas with implicit boxing/unboxing conversions
to include those that use a method-reference to a method with type Object -> x
to satisfy a functional interface of type primitive -> x (requiring boxing the
parameter and then upcasting to Object), or a method with type x -> primitive
satisfying an interface of type x -> Object (requiring the same conversion on
the return value).
…type widening

For example, an int -> int method is a valid instantiation of a functional interface
that expects a byte -> int, or one that expects an int -> long. In both cases it's down
to the metafactory / our code that emulates its behaviour to add the widening cast.
…from a symbol-table id

These users start with an irep_idt, so it's cheaper to look up by id than to create a derived
string and then look up by that.
It's possible to use a primitive-typed method-reference to implement an appropriately
specialised generic functional interface (e.g. something that takes an integer can satisfy
Consumer<Byte> by direct method reference without needing to introduce a stub method to do
the conversion).

Here we implement that by unboxing using the java.lang.Number interface where necessary, thereby
catching all valid widening numeric casts at the same time (Short.intValue for example will
do the widening and deliver the required primitive type). When boxing we always box to the
primitive type's corresponding boxed type, as this is all the compiler seems to allow (i.e. it
won't allow satisfying Producer<Long> using a function that returns an int, even though that is
type-safe).

Note this solution uses virtual dispatch a little more often than is needed: in some simple cases
the class file contains enough information to know that the Object we're unboxing must in practice
be an Integer (for example) and so no dispatch is necessary. That doesn't account for all cases
where a generic interface is implemented however, so I did not pursue this possibility.
@codecov
Copy link

codecov bot commented May 28, 2020

Codecov Report

Merging #5363 into develop will increase coverage by 0.01%.
The diff coverage is 100.00%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #5363      +/-   ##
===========================================
+ Coverage    68.17%   68.19%   +0.01%     
===========================================
  Files         1170     1171       +1     
  Lines        96540    96605      +65     
===========================================
+ Hits         65819    65881      +62     
- Misses       30721    30724       +3     
Flag Coverage Δ
#cproversmt2 42.47% <ø> (ø)
#regression 65.40% <100.00%> (+0.02%) ⬆️
#unit 31.82% <43.66%> (+<0.01%) ⬆️
Impacted Files Coverage Δ
jbmc/src/java_bytecode/assignments_from_json.cpp 97.99% <100.00%> (-0.01%) ⬇️
jbmc/src/java_bytecode/java_utils.cpp 91.07% <100.00%> (-0.60%) ⬇️
jbmc/src/java_bytecode/java_utils.h 100.00% <100.00%> (ø)
jbmc/src/java_bytecode/lambda_synthesis.cpp 97.96% <100.00%> (+0.39%) ⬆️
jbmc/src/java_bytecode/java_types.h 98.59% <0.00%> (+<0.01%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d58ebd8...040542b. Read the comment docs.

Copy link
Contributor

@thk123 thk123 left a comment

Choose a reason for hiding this comment

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

Review of final commit only
lgtm - one question, but non-blocking

@smowton smowton merged commit 4e1fe9a into diffblue:develop May 28, 2020
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.

2 participants