Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 218645 - [dom] Implicit type bounds not available from capture binding
Summary: [dom] Implicit type bounds not available from capture binding
Status: RESOLVED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.4   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: BETA J8   Edit
Assignee: Markus Keller CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 217984
  Show dependency tree
 
Reported: 2008-02-12 11:32 EST by Markus Keller CLA
Modified: 2014-02-28 16:23 EST (History)
7 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Markus Keller CLA 2008-02-12 11:32:23 EST
I20080207-1530

import java.util.ArrayList;
public class NumberThing<T extends Number> extends ArrayList<T> {
        void test() {
                NumberThing<?> questionmarkThing = new NumberThing<Double>();
                questionmarkThing.get(0);
        }
}

Look at the resolved type binding of the method invocation 'questionmarkThing.get(0)'. This is a capture binding with type bounds {java.lang.Number} (the 'Number' comes from the declaration of type variable T).

ITypeBinding#isCapture() says that these bounds should be available from getTypeBounds(), but that method's Javadoc contradicts this by talking about the 'declared' or 'explicit' bounds.

The current implementation of getTypeBounds() does not return the first bound, but it does return additional bounds (e.g. with <T extends Number & Runnable & Cloneable>).

I don't think it makes sense to keep up the current implementation of getTypeBounds() for capture types, since capture types are always generated and never have 'explicit' type bounds. Clients dealing with capture types need all bounds, so I propose to change getTypeBounds() to this:
/**
 * Returns the upper type bounds of this type variable or capture. If the
 * variable or the capture had no bound, then it returns an empty list. [..]

We need this to fix bug 217984.
Comment 1 Markus Keller CLA 2008-10-01 02:22:05 EDT
Any news here?
Comment 2 Markus Keller CLA 2014-02-26 13:54:42 EST
Fixed with http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=06cedbd8bd184e3f34c939385e2cbddd9e3aedca

ITypeBinding#getTypeBounds() now also works for wildcard bindings. For wildcards, #getErasure() already included the bounds of the wildcard's type variable, and it's convenient for clients if the full bounds are exposed as well.
Comment 3 Srikanth Sankaran CLA 2014-02-26 19:31:53 EST
Stephan, does CaptureBinding18 escape inference and make it into the bindings
seen after inference ? I somehow had the impression that it was a purely
transient abstraction, but looking at https://bugs.eclipse.org/bugs/show_bug.cgi?id=428968, it looks like it may not. Could you confirm ? 

For the present bug, should there be anything on the DOM/AST side for 
CaptureBinding18 ?
Comment 4 Stephan Herrmann CLA 2014-02-27 05:28:59 EST
(In reply to Srikanth Sankaran from comment #3)
> Stephan, does CaptureBinding18 escape inference and make it into the bindings
> seen after inference ? I somehow had the impression that it was a purely
> transient abstraction, but looking at
> https://bugs.eclipse.org/bugs/show_bug.cgi?id=428968, it looks like it may
> not. Could you confirm ? 

I introduced two new kinds of type variables, with these properties:

*InferenceVariable*
- created when type inference starts
- must not be mentioned in any inference solution 

*CaptureBinding18*
- created during the last phase of type inference (resolution), when no exact solution for one inference variable could be found
- survives inference
- has no wildcards
- has lower and/or upper bounds (yes, may have both!)

BTW, TypeBinding.isProperType() can be used to discriminate inference variables from any "real" types. CaptureBinding18 are consider proper types - with one tiny exception: Scope.greaterLowerBound() has a heuristic to prevent creation of a contradictory glb. This heuristic doesn't work for CaptureBinding18, which is why we have the boolean parameter 'admitCapture18'. This being an implementation detail, the general rule is: yes, CaptureBinding18 are proper types, and must be handled by everybody downstream (see also bug 425183 :) )
 
> For the present bug, should there be anything on the DOM/AST side for 
> CaptureBinding18 ?

Good question.
 What does DOM do, when converting a WildcardBinding or a CaptureBinding? Looking at DOM's TypeBinding.getBound() isn't this already incomplete for those? multiple bounds? boundKind? If this incompleteness doesn't hurt, maybe CaptureBinding18 can just play by the same rules ...

Just CaptureBindig18 don't have anything like a sourceName, we can't even say capture-of ..., so capture#0 may be the best we can do.
Comment 5 Markus Keller CLA 2014-02-27 07:13:52 EST
(In reply to Stephan Herrmann from comment #4)
> Looking at DOM's TypeBinding.getBound() isn't this already incomplete for
> those? multiple bounds? boundKind?

ITypeBinding#getBound() is only defined for wildcards, not for captures. The Javadoc already says
	 * @see #isUpperbound()
, which reveals whether #getBound() returns an upper or lower bound.

#getTypeBounds() now returns the combined upper bounds for captures, wildcards, and type variables.

To get the lower bound of a capture, use capture.getWildcard().getBound() and !capture.getWildcard().isUpperbound().

I think from an API POV, we're fine now.

Example, where the various combinations can be seen in the 'get(0)' MethodInvocations:

class Gen<E extends List<String> & RandomAccess> extends ArrayList<E> {
    void foo() {
        Gen<?> g = new Gen<>();
        g.get(0);
        Gen<? extends Cloneable> ge = new Gen<>();
        ge.get(0);
        Gen<? super Vector<String>> gs = new Gen<>();
        gs.get(0);
    }
}
Comment 6 Stephan Herrmann CLA 2014-02-27 07:20:57 EST
(In reply to Markus Keller from comment #5)
> To get the lower bound of a capture, use capture.getWildcard().getBound()
> and !capture.getWildcard().isUpperbound().

Except that a CaptureBinding18 doesn't have a wildcard ... is that a problem in this context? Mind you: these bindings can have any non-empty combination of lower / upper bounds.
Comment 7 Markus Keller CLA 2014-02-28 07:50:01 EST
(In reply to Stephan Herrmann from comment #4)
> Looking at DOM's TypeBinding.getBound() isn't this already incomplete for
> those? multiple bounds? boundKind?

Clarified Javadoc: http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=482e34c9a574ed0f9bfa7d2de4b50af5b3e68c79


(In reply to Stephan Herrmann from comment #6)
> Except that a CaptureBinding18 doesn't have a wildcard ... is that a problem
> in this context? Mind you: these bindings can have any non-empty combination
> of lower / upper bounds.

That would be a breaking change of the ITypeBinding#getWildcard() API. Can you give me an example where such a capture binding shows up?
Comment 8 Stephan Herrmann CLA 2014-02-28 08:17:08 EST
(In reply to Markus Keller from comment #7)
> (In reply to Stephan Herrmann from comment #6)
> > Except that a CaptureBinding18 doesn't have a wildcard ... is that a problem
> > in this context? Mind you: these bindings can have any non-empty combination
> > of lower / upper bounds.
> 
> That would be a breaking change of the ITypeBinding#getWildcard() API. Can
> you give me an example where such a capture binding shows up?

The patch in attachment 240378 [details] on behalf of bug 425183 contains a test case that demonstrates the basic situation. The message send naturalOrder() resolves to a type containing a CaptureBinding18.
It doesn't, however, demonstrate multiple upper & lower bounds. I'd have to dig deeper into GenericsRegressionTest_1_8 to pull out an example with multiple bounds.
Comment 9 Stephan Herrmann CLA 2014-02-28 16:23:39 EST
Here's a funny observation: I instrumented the compiler to yell "BINGO" when it sees a CaptureBinding18 escape inference AND where that binding has a non-null lowerBound. I compiled JRE8 with this compiler and all of our RunAllJava8Test and heard the compiler yell not once.

I've seen CaptureBinding18 with both upper and lower bounds, and I've seen CaptureBinding18 escape inference, but both properties together: never.

I have no indications that this is by construction or intended.

So, while we "must" support that situation, it's awfully difficult to test (to say the least),


Reading the javadoc of ITypeBinding#getWildcard() I see that already the concept of wildcard-less capture is a problem. THAT we must (no quotes) handle as the examples around bug 425183 demonstrate.

OTOH, seeing that #isCapture() explicitly mentions JLS 5.1.10, it might be fair to let the DOM bindings for CaptureBinding18 not answer yes. That would mean we need to invent a new name for these, right? Like a new #isFlumChak(), right? So what *are* they? Here's their birth certificate (from 18.4):
"Let Z1, ..., Zn be fresh type variables ..."
Sounds exactly like what #isTypeVariable() explains: "Note that type variables are distinct from capture bindings (even though capture bindings are often depicted as synthetic type variables);" 

Does this info help solve the API issues?