Incoherent invocation type inference?
B. Blaser
bsrbnd at gmail.com
Sat Jan 14 13:12:51 UTC 2017
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'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?
Bernard
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).
>
> 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