-
Sub-task
-
Resolution: Delivered
-
P4
-
26
Whenever a reference to an inner type of the kind A.B is found, javac needs to normalize the qualifier type A, so that any reference to potentially parameterized types are made explicit. This normalization occurs regardless of whether this type is explicitly provided in the source code, or inferred from the enclosing context. Some cases were found where no normalization was applied to the type qualifier -- e.g. if the qualifier is a type-variable type. The lack of normalization led to spurious errors, as the type reference was incorrectly classified as a raw type. This change adds the missing normalization step. While this change is generally beneficial, as spurious errors that would cause compilation to fail are no longer generated, there might be cases where the additional normalization steps might result in a change in overload resolution.
For example, assume the following snippet in which the result of the getter.get() method call is passed to an overloaded method with two candidates: m(T) and M(Object). Without the patch the following code prints Object, erroneously; with the patch the following code prints T, correctly. An erroneous classification of the qualified type means that the type G.Getter was treated as a raw type and the result of the method call was Object; the correct classification is to treat it as T <: Number and the return type of the method call to be Number. That very classification selects a different overload m before and after the patch:
static class Usage<T extends Number, G extends Getters<T>> {
public void test(G.Getter getter) {
m(getter.get()); // javac selects one of the overloads for m
}
void m(T t) { System.out.println("T"); }
void m(Object s) { System.out.println("Object"); }
}
static abstract class Getters<T> {
abstract class Getter {
abstract T get();
}
}
public static void main(String[] args) {
new Usage<Integer, Getters<Integer>>().test(new Getters<Integer>() {}.new Getter() { Integer get() { return 42; } });
}
For example, assume the following snippet in which the result of the getter.get() method call is passed to an overloaded method with two candidates: m(T) and M(Object). Without the patch the following code prints Object, erroneously; with the patch the following code prints T, correctly. An erroneous classification of the qualified type means that the type G.Getter was treated as a raw type and the result of the method call was Object; the correct classification is to treat it as T <: Number and the return type of the method call to be Number. That very classification selects a different overload m before and after the patch:
static class Usage<T extends Number, G extends Getters<T>> {
public void test(G.Getter getter) {
m(getter.get()); // javac selects one of the overloads for m
}
void m(T t) { System.out.println("T"); }
void m(Object s) { System.out.println("Object"); }
}
static abstract class Getters<T> {
abstract class Getter {
abstract T get();
}
}
public static void main(String[] args) {
new Usage<Integer, Getters<Integer>>().test(new Getters<Integer>() {}.new Getter() { Integer get() { return 42; } });
}