Incoherent invocation type inference?

B. Blaser bsrbnd at gmail.com
Sun Jan 15 10:34:47 UTC 2017


Hi,

2017-01-14 14:12 GMT+01:00 B. Blaser <bsrbnd at gmail.com>:
> Hi,
>
> 2017-01-14 1:42 GMT+01:00 Maurizio Cimadamore <maurizio.cimadamore at oracle.com>:
>>
>>
>> On 13/01/17 21:58, B. Blaser wrote:
>>>
>>> Hi,
>>>
>>> 2017-01-13 15:12 GMT+01:00 Maurizio Cimadamore
>>> <maurizio.cimadamore at oracle.com>:
>>>>
>>>> I think that's correct behavior. When you say something like:
>>>>
>>>> li = get(null);
>>>
>>> i = get(null); // li = ... is OK ;-)
>>>
>>>> T will get two constraints:
>>>>
>>>> * an upper bound (from declared bound) : T <: Iterable<String>
>>>> * an upper bound (from return type compatibility) : T <: Integer
>>>
>>> Integer being final, a correct return type constraint would be <T =
>>> Integer>, leading to incompatible bounds {T <: Iterable<String>, <T =
>>> Integer>} and then javac should fail...?
>>
>> Well, if the spec would ever add some special treatment for finality in
>> inference constraints (for instance cast conversion special cases final
>> classes to give better results), that woul dbe a way yes - but I'll leave it
>> to our spec experts.
> In that case, I suggest something like below (line 899 in a more
> recent version).
>
> http://hg.openjdk.java.net/jdk9/dev/langtools/file/b6960e2da008/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java#l899
>
>> In your examples though, what's really troublesome, is that you have a
>> generic method whose type-arguments are only instantiated from the return
>> type constraint. When you have a situation like that, and you mix unchecked
>> cast, things are going to blow up almost always - final or not.

I don't agree with that. Let "Iterable -> List -> ArrayList" be an
unique hierarchy. In our example, "li = get()" is meaningful and won't
ever fail, but "i = get()" will always blow up!

>> I'd be more
>> supportive for an optional Lint warning for cases where inference doesn't
>> get any constraints from the actual arguments
> As unchecked cast produces a warning, javac should probably also emit
> at least a warning in our situation; but I'm not sure this would be
> really satisfying?

Could we express a rule for a "lint" warning (this is not
straightforward, I think)? Speaking of rules and inference, I was
wondering if it could be imaginable to express facts about known types
and bounds with formal logic rules; and then infer type variables
using built-in systems like Prolog?

> diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
> b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
> --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
> +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
> @@ -966,9 +966,11 @@
>                      //if 's' is 'null' there's no instantiated type U for which
>                      //U <: s (but 'null' itself, which is not a valid type)
>                      return false;
> +                } else if ((s.tsym.flags() & Flags.FINAL) != 0) {
> +                    t.addBound(InferenceBound.EQ, s, Types.this);
> +                } else {
> +                    t.addBound(InferenceBound.UPPER, s, Types.this);
>                  }
> -
> -                t.addBound(InferenceBound.UPPER, s, Types.this);
>                  return true;
>              }
>
>
>> - just for a quick comparison,
>> C# treats these cases as hard errors; that's not the way Java inference has
>> been designed from the start, but that doesn't mean that we couldn't help
>> programmers steer away from dubious  generic methods (like your 0-ary get()
>> method below).

I think so, too...

>> Maurizio

Bernard

>>>> The language will then infer T = Integer & Iterable<String>, an
>>>> intersection
>>>> type.
>>>>
>>>> Normally this would be fine - but the issue here is that Integer is a
>>>> final
>>>> class, so that intersection type has no witnesses - e.g. there's no way
>>>> for
>>>> get() to construct a type that is both an Integer and an Iterable<String>
>>>> -
>>>> so that will almost always fail - unless get() returns null.
>>>>
>>>> That said, I'm not aware of any check in the spec for preventing an
>>>> intersection type to mention a final class as one of its components so,
>>>> while this does look weird, I think the compiler is doing what the
>>>> language
>>>> spec says.
>>>>
>>>> Maurizio
>>>>
>>>>
>>>>
>>>> On 13/01/17 13:01, B. Blaser wrote:
>>>>>
>>>>> mport java.util.*;
>>>>>
>>>>> public class Issue  {
>>>>>       <T extends Iterable<String>> T get() { return (T)new
>>>>> ArrayList<String>(); }
>>>>>       <T extends Iterable<String>> T get(T t) { return (T)new
>>>>> ArrayList<String>(); }
>>>>>
>>>>>       void run() {
>>>>>           List<String> li = null;
>>>>>           LinkedList<String> ll = null;
>>>>>           Integer i = null;
>>>>>
>>>>>           li = get(null);
>>>>>           i = get(null); // Shouldn't compile? and will fail at runtime.
>>>>> //        i = get(li); // Fails as expected
>>>>>
>>>>>           li = get();
>>>>>           ll = get(); // OK, but will fail at runtime due to unchecked
>>>>> conversion in get().
>>>>>           i = get(); // Shouldn't compile? and will fail at runtime.
>>>>>       }
>>>>>
>>>>>       public static void main(String... args) {
>>>>>           new Issue().run();
>>>>>       }
>>>>> }
>>>>
>>>>
>>


More information about the compiler-dev mailing list