Bug 80800 - getErasure() of ITypeBinding and IMethodBinding don't match the JLS
Summary: getErasure() of ITypeBinding and IMethodBinding don't match the JLS
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.1   Edit
Hardware: PC Windows XP
: P3 enhancement (vote)
Target Milestone: 3.1 M5   Edit
Assignee: Jerome Lanneluc CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 83100
  Show dependency tree
 
Reported: 2004-12-13 06:58 EST by Markus Keller CLA
Modified: 2005-02-15 09:35 EST (History)
5 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 2004-12-13 06:58:17 EST
I20041208-1200

I found out why the Javadoc of getErasure() of ITypeBinding and IMethodBinding
contained:
	 * Note: the erasure link is not a general back-link between the members
	 * of a generic type instance and the corresponding members of the
	 * generic type.
(removed on my request in bug 79136).

The upcoming JLS3 defines erasure in '4.6 Type Erasure':
[..]
- The erasure of a parameterized type (§4.5) T<T1, ... ,Tn> is |T|.
[..]
- The erasure of a method signature s is a signature consisting of the same name
as s, and the erasures of all the formal parameter types given in s.

Note that the erasure of T<T1, ... ,Tn> does not contain any type parameters or
arguments. Section '4.8 Raw Types' explicitly says: "To facilitate interfacing
with non-generic legacy code, it is also possible to use as a type the erasure
(§4.6) of a parameterized type (§4.5). Such a type is called a raw type.[..]".

From this perspective, the erasure of a type/method is indeed not the generic
version, but the raw version.

In order to conform to the specs, we should consider
a) renaming the current getErasure() into getGenericType/Method(), and
b) introducing new methods getErasure(), which return the raw binding of the
receiver (see also bugs 77360, 40096).
Comment 1 Jim des Rivieres CLA 2005-01-04 14:00:31 EST
I agree that getErasure() is bad choice of name given the way "erasure" is 
defined in JLS3. I've done part (a):
- renamed ITypeBinding.getErasure() to getGenericType()
- deprecated ITypeBinding.getErasure()
- renamed IMethodBinding.getErasure() to getGenericMethod()
- deprecated IMethodBinding.getErasure()
- updated ASTConverter15Test

Both deprecated methods are part of J2SE5 effort, and should be deleted before 
3.1M5 once clients are no longer using them.

re: part (b). As discussed in bug 40096, the binding API does not provide 
operations that would require creating new bindings. Olivier, is
there a binding for the raw type at the compiler level? I.e., would it be
possible to provide a method that returned the corresponding raw type?

Comment 2 Dirk Baeumer CLA 2005-01-05 05:09:31 EST
Markus, can you please adjust the callers of getErasure() in JDT/UI accordingly.
Comment 3 Olivier Thomann CLA 2005-01-05 11:30:53 EST
I guess it is possible to get the corresponding raw type. So we need to decide
if we change the behavior of the method getErasure() or if we add a new method.
Comment 4 Markus Keller CLA 2005-01-05 13:14:26 EST
Contrary to my initial suggestion, I would not call the method that returns the
raw type getErasure().

getRawType() would be more in-line with getGenericType() and the checks
isRawType() and isGenericType().
Comment 5 Jim des Rivieres CLA 2005-01-05 13:31:34 EST
I'd like to do neither. A raw type binding is a binding for a particular kind 
of *reference* to a generic type. 

Markus, Do you have a particular usecase for providing a navigation from a 
generic type to the erasure type?
Comment 6 Martin Aeschlimann CLA 2005-01-06 03:04:31 EST
What about 'getErasure()' on type variables? 

For <A extends Vector> isn't 'Vector' the _erasure_? 'getGenericType()' doesn't
seem to apply here.
Comment 7 Olivier Thomann CLA 2005-01-12 16:10:06 EST
I didn't find a way to get a raw type from any parameterized type binding on the
compiler side.
Jim, what action do you want to do?

Philippe, any comment?
Comment 8 Olivier Thomann CLA 2005-01-12 16:29:15 EST
Jim, I reassign to you as long as the API side is not complete.
Comment 9 Dirk Baeumer CLA 2005-01-17 10:59:26 EST
Jeem,

we had a long discussion about this here in ZRH and with Philippe. Our outcome
was as follows:

- we need a method ITypeBinding#getErasureType() to implement signature
  comparison. We agreed that this method might return the generic type instead
  of the raw type, when the compiler doesn't have the raw type. The behaviour
  should be consistent though.

- we need a method to navigate from a parameterized type to a generic type. This
  can either be getGenericType or a method getTypeDeclaration. The advantage of
  the second one is that it can return something for all type bindings.

- IMethodBinding#getErasureMethod should be removed. Instead a method getGeneric
  Method or getMethodDeclaration should be added.

Jeem, what do you think ?
Comment 10 Jim des Rivieres CLA 2005-01-17 18:22:07 EST
The changes I made in comment #1 address the last 2 bullets in comment #9:
- ITypeBinding.getGenericType() navigates from a parameterized type to a 
generic type. It's spec'd to return something for all type bindings.
- IMethodBinding.getGenericMethod() replaced IMethodBinding.getErasureMethod().

For the first bullet, it would be weird for a method to return the raw type in 
some cases and the generic type if the raw type happened to be unavailable.
How about this instead: getRawTypeIfAvailable() returns the raw type is it 
happens to be available, otherwise null. A client could use 
getRawTypeIfAvailable() != null ? getRawTypeIfAvailable() : getGenericType()
to implement signature comparison.
Comment 11 Dirk Baeumer CLA 2005-01-18 04:32:43 EST
Jeem,

I introduced some confusion here, sorry. I didn't mean that getErasure sometimes
returns the raw and sometimes the generic type. It should always return the same
kind of type. All I tried to say was that we prefer getting the raw type (since
that's what the spec is saying) and if this isn't possible, the generic type.

Regarding getGenericType returning something all the time. This seems wired from
an API stand point since

'String'.getGenericType().isGenericType() == false.

IMO we should still offer a method getErasure() (even if it doesn't fully follow
the spec) since that's what clients want to do. Calling getGenericType() instead
of getErasure() makes code harder to understand.

So prefer the following methods:

ITypeBinding#getErasure(): return something for every kind of type. For
parameterized types and generic types this will be the generic type.

ITypeBinding#getTypeDeclaration(): although this method will return the same as
getErasure() (because we can return raw types here) I would prefer having this.
In client code it is a difference navigating to something or taking the erasure.

IMethodBinding#getTypeDeclaration(): same as for types.
Comment 12 Markus Keller CLA 2005-01-18 04:47:27 EST
Note: there should be a difference between getTypeDeclaration() and getErasure():

* ITypeBinding#getTypeDeclaration():
isGenericType       -> isGenericType (identical)
isParameterizedType -> isGenericType
isRawType           -> isGenericType
isWildcardType      -> isWildcardType (identical)
isTypeVariable      -> isTypeVariable (identical)
nongeneric          -> nongeneric (identical)

* ITypeBinding#getErasure():
isGenericType       -> isGenericType (identical)
isParameterizedType -> isGenericType
isRawType           -> isGenericType (identical)
isWildcardType      -> '? extends': bound; '?' and '? super': Object
isTypeVariable      -> erasure of leftmost bound
nongeneric          -> nongeneric (identical)


* IMethodBinding#getMethodDeclaration():
isGenericMethod       -> isGenericMethod (identical)
isParameterizedMethod -> isGenericMethod
isRawMethod           -> isGenericMethod
nongeneric            -> nongeneric (identical)

* IMethodBinding#getErasure():
Remove this method. The correct result for IMethodBinding#getErasure() would be
a method binding with all parameter and return types erased by
ITypeBinding#getErasure(). But I don't think it's possible for the compiler to
return such a method binding.
Comment 13 Jim des Rivieres CLA 2005-01-18 10:34:01 EST
Martin, Your table is very helpful. For ITypeBinding.getErasure(), I assume 
you meant to say:
isRawType           -> isRawType (identical)
Comment 14 Markus Keller CLA 2005-01-18 11:09:03 EST
[Martin -> Markus ;-]
Oops, that was a typo. I meant:

* ITypeBinding#getErasure():
isRawType           -> isGenericType

The erasure as defined by the table above will be used to "normalize" a type
binding to something that's written to the classfile. We will use getErasure()
to compare such normalized references in order to e.g. find out whether two
types will be erased to the same entity in the classfile.

To be consistent for all kinds of generic/parameterized/raw types, getErasure()
should always return the same kind of binding (which will be a *generic* type,
since comment 7 indicated that it is difficult to get the raw type in all cases).

Sidenote: The problem we were facing here (whether the return type of
getErasure() should be a raw or a generic type), is really a question of
different models. The erasure process is a transformation from a compiler-world
ITypeBinding to a classfile-world erased type.

Since we don't have entities for these classfile types, we try to get a useful
result in terms of compiler-world ITypeBindings. For this mapping back to the
compiler-world, we are actually free to choose between raw and generic types.
Both are +/- equally "right" or "wrong".
Comment 15 Jim des Rivieres CLA 2005-01-18 11:26:23 EST
Here are specs for ITypeBinding.getTypeDeclaration() and getErasure(). Let me 
know if these are ok.

	/**
	 * Returns the binding for the type declaration corresponding to this 
type
	 * binding. For parameterized types ({@link #isParameterizedType()})
	 * and raw types ({@link #isRawType()}), this method returns the 
binding
	 * for the corresponding generic type. For other type bindings, this
	 * returns the same binding.
	 * <p>
	 * Note: Support for new language features proposed for the upcoming 
1.5
	 * release of J2SE is tentative and subject to change.
	 * </p>
	 *
	 * @return the generic type binding
	 * @since 3.1
	 */
	public ITypeBinding getTypeDeclaration();

	/**
	 * Returns the erasure of this type binding.
	 * <ul>
	 * <li>For parameterized types ({@link #isParameterizedType()})
	 * - returns the binding for the corresponding generic type.</li>
	 * <li>For raw types ({@link #isRawType()})
	 * - returns the identical binding.</li>
	 * <li>For wildcard types ({@link #isWildcardType()})
	 * - returns the binding for the upper bound if it has one and
	 * java.lang.Object in other cases.
	 * </li>
	 * <li>For type variables ({@link #isTypeVariable()})
	 * - returns the binding for the erasure of the leftmost bound
	 * if it has bounds and java.lang.Object if it does not.</li>
	 * <li>For all other type bindings - returns the identical 
binding.</li>
	 * </ul>
	 * <p>
	 * Note: Support for new language features proposed for the upcoming 
1.5
	 * release of J2SE is tentative and subject to change.
	 * </p>
	 *
	 * @return the erasure type binding
	 * @since 3.0
	 */
	public ITypeBinding getErasure();

Comment 16 Martin Aeschlimann CLA 2005-01-18 11:33:41 EST
That's probably a typo:
	 * <li>For raw types ({@link #isRawType()})
	 * - returns the identical binding.</li>
Comment 17 Jim des Rivieres CLA 2005-01-18 11:53:13 EST
I've correct it to read:
	 * <li>For raw types ({@link #isRawType()})
	 * - returns the binding for the corresponding generic type.</li>
Comment 18 Jim des Rivieres CLA 2005-01-18 12:01:32 EST
Olivier, Back over to you.
Comment 19 Markus Keller CLA 2005-01-18 12:11:19 EST
I fully agree with the latest specs as committed in HEAD.
Comment 20 Jerome Lanneluc CLA 2005-01-21 07:03:14 EST
Added implementation that matches the released API.
Added tests ASTConvert15Test#test0110() to test0125().
Comment 21 David Audel CLA 2005-02-15 09:35:19 EST
Verified in I20050214-0927