Inference Bug related to parametric polymorphism unification

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon Jun 17 00:01:59 UTC 2019


On 15/06/2019 22:55, John Napier wrote:
> I definitely think that your suggestions (or “musings" if suggestions 
> is too strong a word) for choosing the most deeply nested expression 
> as the unification reference would be an improvement over the current 
> situation from a user's perspective, provided it doesn’t lead to a 
> dramatically slower compilation time.
Hi John,

I think that, in this case performances should not be a problem, as it's 
mostly matter to sort constraints by position/depth, which should be 
doable.

What I'm afraid of, is that for each example such an analysis might help 
in getting 'right', there can possibly another where such an analysis 
would 'block' types from being propagated from the LHS and lead to dual 
inference failure modes.

I'll leave this to Dan - the overall feeling I get on this topic is that 
the dependency analysis we have in JLS is great in terms of deciding 
which variables to solve first when we know we have to solve _all_ 
variables in a dependency graph. The problem is that we try to 'reuse' 
this same dependency info, decorate it with extra info about 
directionality of expressions/constraints (e.g. a lambda that takes a X 
and produces a R, means that there's a new dependency in that R depends 
on X). While this work in most cases, the fact that the underlying 
dependency analysis is brittle (e.g. in X <: List<Y> we only conclude 
that X depends on Y, but not viceversa), means that we can 'miss' 
dependencies between expressions when multiple functional expressions 
are 'stuck'.

This can be summarized with this simple example (if I didn't do any odd 
mistakes):

import java.util.function.*;
import java.util.*;

class Test {
   static <X, Y extends List<R>, R, W>  void m(Function<X, Y> a1, 
Function<R, W> a2) {
   }

   static <X, Y extends List<R>, R, W>  void m_inv(Function<R, W> a2, 
Function<X, Y> a1) {
   }


   static void test() {
      m(x -> (List<String>)null, s -> s.length());
      m_inv(s -> s.length(), x -> (List<String>)null);
   }
}


The two invocations are clearly identical (well, symmetric) - m_inv 
declared formals in opposite order than those of m. Yet, testing on JDK 
13, the first invocation passes, while the second fails, with a similar 
error as the one in your example.

In a way, I think that the fact that Y depends on R (as per JLS 
dependency analysis) should be treated as 'less important' compared to 
the fact that one of the two lambdas affects the input variables of the 
other, indirectly, via Y. But I don't think there's an easy way to tweak 
the spec, w/o coming up with a brand new dependency analysis for 
handling stuck expressions. Which might push the cost. vs. benefit ratio 
out of the acceptable limit.

Cheers
Maurizio




More information about the compiler-dev mailing list