JDK-8039222 : 5.1.10: Define glb for non-class types
  • Type: Bug
  • Component: specification
  • Sub-Component: language
  • Affected Version: 5.0,7,8
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2014-04-03
  • Updated: 2018-05-03
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Related Reports
Blocks :  
Blocks :  
Blocks :  
Blocks :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
JLS 5.1.10 ensures that intersection types produced by glb have only one "best" class:

"glb(V1, ..., Vm) is defined as V1 & ... & Vm."

"It is a compile-time error if, for any two classes (not interfaces) Vi and Vj, Vi is not a subclass of Vj or vice versa."

The well-formedness rule is important: if two unrelated classes were part of an intersection, we would not know how to determine the intersection's members (this is an unsupported form of multiple inheritance).

However, this rule doesn't consider other types:
- Array types should be treated just like class types
- Intersection types should be decomposed when creating the list V1, ..., Vm
- Treatment of type variables is unclear...

A related problem with glb is that it does not claim to eliminate redundant elements, and it is unclear whether glb of a single type is that type or the (singleton) "intersection of" that type.  Different behavior of compilers might result in different membership (depending also on how membership of an intersection is defined).

Some points about type variables:

- An unbounded or Object-bounded type variable might be treated like an interface, since it only extends Object.  Or perhaps not, since i) there are subtle differences between the members of an empty class that extends Object and an empty interface, and ii) the instantiation of the type variable might be a class (does this matter?)

- A class-bounded type variable must be treated like a class.

- An interface-bounded type variable could probably be treated like an interface; but see above about unbounded type variables -- (i) does not apply, but (ii) does.

- A type variable with some other upper bound needs to have this test somehow performed on its upper bound, recursively.

- A capture variable with a lower bound presents an interesting problem: two lower-bounded capture variables might have the same lower bound.  In that case, a naive glb would consider this an error, but a smarter glb would use the lower bound as the glb of the two types!  (Just like the lub of two unrelated type variables is the lub of their upper bounds.)  (JDK-8033718 implements this glb logic.)

<T> void doGLB(List<? super T> l1, List<? super T> l2) {}
void test(List<? super String> l) { doGLB(l, l); }

- Note in all of this that 5.1.10 may ask to glb capture variables that _don't have bounds yet_.  This makes things tricky when we start trying to look at type variable bounds.
Not mentioned above: we also need to handle wildcard-parameterized types. Often, for example, two interface types contain information that can be combined to form one useful type: - ArrayList<?> & Iterable<String> implies ArrayList<String> - List<? extends Runnable> & Queue<? extends Cloneable> implies List<? extends Runnable & Cloneable> & Queue<? extends Runnable & Cloneable> - Class<? extends Number> & Class<? super Integer> implies Class<? extends Number super Integer> (assuming that type can be expressed...)

JDK-8144066 is a use case for glb of lower-bounded type variables (and glb of array types).

Two notes: - In some cases, glb should reject combinations of types as malformed, and it's the responsibility of the "caller" to recover - The "caller" may be capture (5.1.10), but it may also be lub (4.10.4), non-wildcard parameterization (9.9), or inference resolution (18.4)

Another way to view this bug, if we prefer to leave glb as a simple intersection constructor: how do I find the members of an intersection of types other than classes and interfaces? 4.9 has some answers to this question, but is incomplete.

Another approach for arrays might be to complain if the array type is not a subtype of every interface -- after all, there are no objects with type Foo[] & MyCustomInterface.

Here's a concrete example I encountered in java.util.stream.Collectors of glb performed on two different lower-bounded capture variables. public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) { return groupingBy(classifier, HashMap::new, downstream); } public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream) { ... } The two '? super T' instances in the first declaration become two capture variables when the second declaration is invoked. They are both upper bounds of the second declaration's T, so we need their glb.