JDK-8080645 : Inference failure with multiple nested invocations
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8u45
  • Priority: P3
  • Status: Resolved
  • Resolution: Duplicate
  • OS: linux
  • CPU: x86_64
  • Submitted: 2015-05-04
  • Updated: 2015-05-23
  • Resolved: 2015-05-23
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
Return type is not inferred from target type when there is more than one return statement in the lambda expression, the compiler instead tries to compare different return values to infer the return type.

Take as example the following code ( taken from http://stackoverflow.com/questions/29956936/is-this-esoteric-generics-error-a-compiler-bug-or-a-new-restriction-inferred-t?nah=29957168#29957168 ) this code fails to compile:

import java.sql.Connection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class CompilerIssue
{
    public Set<String> test(int value)
    {
        return perform(connection -> {
            if (value % 2 == 0)
            {
                return Collections.<String>emptySet();
            }
            else
            {
                return new HashSet<>(10);
            }
        });
    }

    <V> V perform(BusinessLogic<V> logic)
    {
        return null;
    }

    interface BusinessLogic<V>
    {
        V execute(Connection connection) throws Exception;
    }
}

The problem is in the line "return new HashSet<>(10);", it doesn't infer the type String but the type Object. However, if the other return statement is commented out and replaced with a "return null;" then teh code compiles fine.


REGRESSION.  Last worked in version 8u5

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Copy the following code in a file "CompileIssue.java" and compile it ( javac CompileIssue.java )

------ File start ------

import java.sql.Connection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class CompilerIssue
{
    public Set<String> test(int value)
    {
        return perform(connection -> {
            if (value % 2 == 0)
            {
                return Collections.<String>emptySet();
            }
            else
            {
                return new HashSet<>(10);
            }
        });
    }

    <V> V perform(BusinessLogic<V> logic)
    {
        return null;
    }

    interface BusinessLogic<V>
    {
        V execute(Connection connection) throws Exception;
    }
}

------ file end ------

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Code should compile without errors, the same way as in jdk1.8.0_05
ACTUAL -
Compiler generates the following error:

Error:(12, 23) java: incompatible types: inferred type does not conform to upper bound(s)
    inferred: java.util.Set<? extends java.lang.Object>
    upper bound(s): java.util.Set<java.lang.String>,java.lang.Object

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.sql.Connection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class CompilerIssue
{
    public Set<String> test(int value)
    {
        return perform(connection -> {
            if (value % 2 == 0)
            {
                return Collections.<String>emptySet();
            }
            else
            {
                return new HashSet<>(10);
            }
        });
    }

    <V> V perform(BusinessLogic<V> logic)
    {
        return null;
    }

    interface BusinessLogic<V>
    {
        V execute(Connection connection) throws Exception;
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Specifying explicitly the type ("return new HashSet<String>(10);" instead of "return new HashSet<>(10);")


Comments
Tracked fix in jdk9 to JDK-8055963. A backport, if any, should be handled there.
23-05-2015

Compiles without error in JDK 9. Might be possible to isolate a fix.
21-05-2015

This appears to be an inference bug. Simplified test: static class C<T> {} static class D<U> extends C<U> {} <V> V pick(V v1, V v2) { return v1; } C<String> c = pick(new C<>(), new D<>()); This translates into these constraints for overload resolution: C<t> <: v D<u> <: v Which should be tentatively resolved as t=Object, u=Object, v=C<Object> Then we get, from the target type: v <: C<String> Which should trigger: C<t> <: C<String> D<u> <: C<String> Resulting in t=String, u=String, v=C<String>
21-05-2015