[lvti] Non-denotable types

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Fri Jul 14 10:03:18 UTC 2017

On 14/07/17 06:15, Tagir Valeev wrote:
> Hello!
> When thinking about local variable type inference, I noticed that lambda
> parameter type inference in Java 8 is sometimes similar. For example, we
> can declare the following method:
>      static <T> void var(T t, Consumer<? super T> cons) {
>          cons.accept(t);
>      }
> Then Java 8 code like this:
>     var(theValue, theVariable -> {
>        codeBlock;
>     });
> Is equivalent to LVTI code like this:
>    var theVariable = theValue;
>    codeBlock;
Yep - this is how lambda implicit parameters work in JDK 8/9.
> Apparently this does not work for non-denotable types. I checked the
> non-denotable types section in [1]: seems that lambda parameter inference
> infers to non-denotable type as it described in this section, which already
> produces "surprising" results:
>      void test(List<?> l1, List<?> l2) {
>          var(l1, l3 -> {
>              l3 = l2; // error
>              l3.add(l3.get(0)); // error
>          });
>      }
> Shouldn't we expect a consistency here? There's already a kind of variables
> in Java (lambda parameters) for which non-denotable types are inferred. It
> looks strange to me that two kinds of variable inference work in different
> manner.
We had many discussions on this very topic. In general we agree that 
it's bad having two different inference schemes for doing similar 
things. That said, there are distinctions to be made, as implicit lambda 
parameter inference is _not_ an inference process that can be applied in 
isolation, by adjusting each parameter. The way this works is that you 
infer a _target_ (not a parameter type) and then you derive the 
parameter type from the target, under the constraint that the parameter 
type must be whatever is needed to make the lambda 'override equivalent' 
with the functional descriptor induced by the target.

In LVTI the inference process is more straightforward: you start from an 
expression type (the initializer) and you derive a type from it (the 
type for the 'var'), which will need to be some upper bound of the type 
you started with.

Being consistent here could mean two things: either allow non denotable 
types in 'var', or disallow non-denotable types in implicit lambdas. The 
first path seems 'easier' - although it might expose a 'feature' that 
many people would probably not even have realized it's there. The latter 
would be safer from a language/type-system design perspective, but it 
comes with a source compatibility price. We are currently discussing 
this and running experiments to see which approach would work best - so 
expect to hear something on this topic soon.

> With best regards,
> Tagir Valeev
> [1] http://openjdk.java.net/jeps/286

More information about the amber-dev mailing list