Forum Stats

  • 3,874,148 Users
  • 2,266,674 Discussions
  • 7,911,741 Comments

Discussions

Scoping error in prototype?

843793
843793 Member Posts: 41,732 Green Ribbon
edited Mar 27, 2003 9:35PM in Generics
I believe there are some scoping bugs in the 1.3 prototype compiler.

First, note that this is illegal:
class A { interface I {} }
class B extends A implements I {}
Although B inherits I, I is not in scope for the header of B.

Next, consider
class A { interface I {} }
class B extends A implements A.I {}
The compiler now accepts this example, since A is in scope, and A.I exists.

Now, what about
class A { interface I {} }
class B extends A implements B.I {}
This should also compile (for the same reason as the last example), but the prototype compiler rejects it saying I does not exist! This is the first bug.

We move on to this example:
class A { interface I {} }
class B<T extends I> extends A {}
The prototype compiler accepts this example. But remembering the first example, I is not yet in scope in the class header, so this should be rejected. Second bug.

To make matters more confusing:
class C<T> {
  class T {}
  T t;
}
What is the type of t? The prototype compiler accepts this, and gives it type Object (as the erasure of parameter T) rather than C.T.

On the other hand,
class C {
  <T> void m() {
    class T {}
    T t;
  }
}
Again, what is the type of t? The prototype compiler rejects this, stating that local class T conflicts with method type parameter T. Based on consistency, either the previous example or this example has a bug. I would prefer that the previous example be a compile error like this example is; if a type parameter and nested type share the same scope, they should not share the same name.

Comments

  • 843793
    843793 Member Posts: 41,732 Green Ribbon
    > class A { interface I {} }
    <div class="jive-quote">class B extends A implements B.I {}</div>This should also compile (for the same reason as the
    last example), but the prototype compiler rejects it
    saying I does not exist! This is the first bug.
    I see what you mean, but it isn't a great loss. It's not that I isn't in scope, it's that B doesn't exist yet. I can live with this one.

    Remember, the actual class file for the interface would be A$I.class, not B$I.class - so asking for B.I requires a lookup.

    Of course, B.I might be a different interface entirely...

    We move on to this example:
    > class A { interface I {} }
    <div class="jive-quote">class B<T extends I> extends A {}</div>The prototype compiler accepts this example.  But
    remembering the first example, I is not yet in scope
    in the class header, so this should be rejected.
    Second bug.
    Again, you have a point but I can live with it. Generics are all checked at compile time, so there are no messy run-time dependencies hanging around. Basically, the compiler is free to do what it damn well likes at compile time, whereas at runtime it has rules to follow.
    To make matters more confusing:
    > class C<T> {
    <div class="jive-quote">class T {}<br />
    T t;<br />
    }</div>What is the type of t? The prototype compiler accepts
    this, and gives it type Object (as the erasure of
    parameter T) rather than C.T.
    
    On the other hand,
    > class C {
    <div class="jive-quote"><T> void m() {<br />
    class T {}<br />
    T t;<br />
    }<br />
    }</div>Again, what is the type of t? The prototype compiler
    rejects this, stating that local class T conflicts
    with method type parameter T.  Based on consistency,
    either the previous example or this example has a bug.
    I would prefer that the previous example be a compile
    error like this example is; if a type parameter and
    nested type share the same scope, they should not
    share the same name.
    Yup, those ones do look pretty dubious. I think you're dealing with a side-effect of the whole compile-time/run-time division here - effectively, the namespace for generic parameters is separate, because it gets erased. Anyone from Sun care to comment?

    Since people from Sun never do comment here, I suggest you keep experimenting, find as much as you can, and then file a real bug report.

    http://developer.java.sun.com/developer/bugParade/
  • 843793
    843793 Member Posts: 41,732 Green Ribbon
    Since people from Sun never do comment here, I suggest you keep experimenting, find as much as you can, and then file a real bug report.
    I have been experimenting with nested generic types quite a bit and the result is frustrating. Since the spec has barely anything to say about nested generic types, it is close to impossible to judge whether any dubious behavior is a bug or intended behavior. How do you want to file a bug report if there is no spec to compare against?

    I'm refering to the Participant Draft Specification of April 27, 2001 that comes with the early access release that is available for download. Is there a final spec that is more complete than the draft? I would be thankful if someone could point me to that document.



    For instance, the draft spec says: "The erasure of a nested type T.C is |T|.C."

    Aha! And what is the erasure of a parameterized type inside a parameterized type? From the draft spec I cannot tell whether the erasure of T<T1,...,Tn>.C<S1,...,Sn> is T.C<S1,...,Sn> or T.C. According to the wording of the spec is must be the former (since C is not required to be a non-parameterized type), but intuitively I would think it is the latter. However, the spec does not say that translation of types is performed recursively. It does not say anything.


    Also, if you file a bug report, the bugs are occasionally rejected as "not a bug" refering to statements in the spec that I cannot find in the spec. See for instance http://developer.java.sun.com/developer/bugParade/bugs/4482981.html
    It reports problems with nested interfaces inside a generic class. Instead of Outer<T>.InnerInterface, which would be the intuitive name for an interface inside a parameterized class, you have to use the raw outer type, namely Outer.InnerInterface.

    The reasoning was: "The spec says that static members need to be qualified with unparameterized types. Since interfaces are static, they fall under that rule." Fair enough, but the draft spec does not state anything like that.

    Here is another riddle:

    public final class BridgeMethods {
    private static class X<T> {
    public class Y<T> {}
    }

    private static
    <T extends X<T>.Y<T>> T f(T arg) {return arg;}

    public static void main(String[] args) {
    X<String> x = new X<String>();
    X<String>.Y<String> y = f(x.new Y<String>());
    }

    }

    The error message is to the call of method f: f<T extends generics.BridgeMethods.X<T>.Y<T>>(T) in generics.BridgeMethods cannot be applied to (generics.BridgeMethods.X<java.lang.String>.Y<java.lang.String>)

    That does not make any sense to me. But frankly said, I have no idea whether this is my misconception or a compiler bug. I cannot find any justification for this error message in the draft spec. Does anybody have an idea why this compiler message would make sense?
  • brucechapman
    brucechapman Member Posts: 466
    >
    Also, if you file a bug report, the bugs are
    occasionally rejected as "not a bug" refering to
    statements in the spec that I cannot find in the spec.
    See for instance
    http://developer.java.sun.com/developer/bugParade/bugs
    4482981.html
    It reports problems with nested interfaces inside a
    generic class. Instead of Outer<T>.InnerInterface,
    which would be the intuitive name for an interface
    inside a parameterized class, you have to use the raw
    outer type, namely Outer.InnerInterface.

    The reasoning was: "The spec says that static members
    need to be qualified with unparameterized types. Since
    interfaces are static, they fall under that rule."
    Fair enough, but the draft spec does not state
    anything like that.
    OK, I own up, that bug report was in response to some feedback I made on the prototype. (I just checked and I still have the original code).

    Actually the draft spec does say (or maybe imply) that, in a different sort of way, thus..

    "The scope of a type parameter is all of the declared class, except any static members or initializers, but including the type parameter section itself."

    Therefore the reasoning is that the type parameter does not apply to an inner interface because it is static.

    Whether the reasoning behind the scope exclusion for statics is actually relevant to interfaces (as opposed to methods and fields), is something I am yet to be convinced on.

    regards

    Bruce
  • 843793
    843793 Member Posts: 41,732 Green Ribbon
    Actually the draft spec does say (or maybe imply)
    that, in a different sort of way, thus..

    "The scope of a type parameter is all of the
    declared class, except any static members or
    initializers, but including the type parameter section
    itself."


    Therefore the reasoning is that the type parameter
    does not apply to an inner interface because it is
    static.
    Uumm, this is reading a lot into the innocent statement. :-)

    To my mind the quoted statement just specifies where the type variables of a generic class are valid, i.e. static methods, static initializers, static nested classes and interfaces do not have access to the type variables of the enclosing class and I cannot use the type variables for declaring static fields. Perfectly reasonable.

    But the statement does not says how static members of a generic class are qualified or that the name of the scope of a static member is the raw type only, while the name of the scope of a non-static member can be the raw type or a parameterized type. The spec does not say anything about the scope names. The compiler could equally well accept something like OuterClass<T>.InnerStaticClass or OuterClass<T>.InnerInterface and simply ignore the type variable T instead of flagging it as an error.

    Angelika
  • brucechapman
    brucechapman Member Posts: 466
    Fair enough comment.


    please tell them at

    mailto:[email protected]?subject=JSR%2014%20Comments
    Bruce
This discussion has been closed.