Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4028226

in JDK 1.1 beta3 the correction of compiler bug 4023931 is not correct!

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P4 P4
    • 1.2.0
    • 1.1, 1.1.3
    • tools
    • None
    • 1.2fcs
    • x86, sparc
    • solaris_2.5.1, windows_95
    • Not verified



      Name: mc57594 Date: 01/27/97


      The correction of bug 4023931 is not correct because it
      implements a tricky exception to avoid that a package-private method can be
      overriden by methods defined in subclasses belonging to other packages. But
      the very reason of the problem is still there: javac, in violation of the
      Java specifications (8.2 and 6.4.2), considers that package-private class
      members are well inheritable by subclasses of other packages, while they are
      not visible within these subclasses.

      Bug 4023931 was a logical consequence of this violation of the rule
      forbiding the inheritance of package-private members outside the package
      where they are defined: it resulted from the application of the usual
      overriding mechanism for inherited methods in this case. The present
      correction of the bug is dangerous: to correct the bug without correcting
      its real cause, javac allows an exception to the overriding mechanism, and
      this leads to inconsistencies and several other violations of the
      specifications.

      Example (the 4 classes are defined in 4 different source files):

      package motherpack;
              public class Mother {
                      public Mother(){}
                      String meth() {return "mother meth";}}

      package daughterpack;
      import motherpack.Mother;
              public class Daughter extends Mother {
                      public Daughter(){}}


      package motherpack;
      import daughterpack.Daughter;
              public class GrandDaughter extends Daughter {
                      public GrandDaughter(){}}

      import java.io.*;
      import daughterpack.Daughter;

              public class Main {
                      public static void main(String[] args) {
      GrandDaughter obj = new GrandDaughter
                             System.out.println(obj.meth());
                             System.out.println(((Daughter)obj).meth());
                             System.out.println(((Mother)obj).meth());}}

      This program is successfully compiled by javac and prints:
              mother meth
              mother meth
              mother meth

      According to Java specifications:
              - 6.4.2: "The only members of a class are those inherited from its
      direct superclasse and its direct superinterfaces and those defined in its
      body."
              - 8.2: "Only members of a class that are declared protected or
      public are inherited by subclasses declared in a package other than the one
      in which the class is declared."
              
      In the example program above, in the member access expressions obj.meth()
      and ((Daughter)obj).meth(), the type of the variable is respectively
      GrandDaughter and Daughter. From 6.4.2 and 8.2, it comes that meth() is not
      a member of these reference types. So these method access expressions are
      illegal and should be rejected by the compiler: only the
      expression((Mother)obj).meth() should be accepted. If the compiler accepts
      the (illegal) expressions obj.meth() and ((Daughter)obj).meth(), it is
      because it considers (in violation of 8.2) the package-private members of
      Mother as inherited by Daughter, while accessible only from the package of
      Mother (and not even within the body of the class Daughter) and then
      inherited from Daughter by GrandDaughter. This violation leads to cumbersome
      confusions, as illustrated below.

      Now, suppose that we rewrite the class Daughter like this:

      package daughterpack;
      import motherpack.Mother;
              public class Daughter extends Mother {
                      public Daughter(){}
                      String meth(){return "Daughter meth" ;}}

      The result of our main program does not change (the new method is invisible
      outside the daughterpack package). But this leads to a first problem: the
      access expression ((Daughter)obj).meth() then does not denote the same
      method according to the context where it occurs: within package motherpack
      it returns the value "mother meth" while within daughterpack it returns
      "daughterpack meth! This is a fitrst violation of the specifications.
        
      Now, suppose that instead we rewrite the class Daughter like this:

      package daughterpack;
      import motherpack.Mother;
              public class Daughter extends Mother {
                      public Daughter(){}
                      public int meth(){return 666 ;}}

      Now in JDK 1.1 beta 3 this is allowed. But there happens an indetermination:
      on a variable obj of type GrandDaughter, what will denote the expression
      obj.meth() within package motherpack: the String method indirectly (and in
      violation of specs) inherited from Mother or the int one inherited from
      Daughter? There is an ambiguity, which is not directly seen by javac: here,
      in practice, it apparently gives priority to the method inherited from
      Daughter, and runing main gives:
              666
              666
              mother meth

      But suppose that one wants also then to rewrite the class GrandDaughter as
      follows:

      package motherpack;
      import daughterpack.Daughter;
              public class GrandDaughter extends Daughter {
                      public GrandDaughter(){}
                      public int meth() {return 777;}}

      In this case the method would override the one it inherits from Daughter.
      But, there javac detects a problem and reject this definition because it
      finds the return type of GrandDaughter.meth() (int) incompatible with the
      return type of Mother.meth() (String)!!!!!!

      If then one tries alternatively:

      package motherpack;
      import daughterpack.Daughter;
              public class GrandDaughter extends Daughter {
                      public GrandDaughter(){}
                      public String meth() {return "granddaughter meth";}}

      javac now rejects also this definition because it finds the return type of
      GrandDaughter.meth() (now String) incompatible with the return type of
      Daughter.meth() (int) in this case!!!!

      Now suppose we redefine Daughter and GrandDaughter as follows:

      package daughterpack;
      import motherpack.Mother;
              public class Daughter extends Mother {
                      public Daughter(){}
                      public String meth(){return "daughter meth" ;}}

      and

      package motherpack;
      import daughterpack.Daughter;
              public class GrandDaughter extends Daughter {
                      public GrandDaughter(){}
                      public String meth() {return "granddaughter meth";}}

      javac then finds no problem. But running our main program leads to find an
      inconsistent behaviour of java, as it prints:
              granddaughter meth
              daughter meth
              granddaughter meth

      Clearly the method of GrandDaughter has overriden the one of Mother but not
      the one of Daughter!!!! This is a bug.

      All those troubles and bugs would have been avoided if specs 6.4.2 and 8.2
      had been respected...

      Note also that the problem is not specific to package-private methods (and
      also attributes). It is also possible to show the same kind of violation
      with class-private methods (and attributes). Javac considers them also as
      inherited by subclasses (while visible on these subclasses only with the
      body of the class where they are defined).

      In conclusion, instead of respecting inheritance exceptions clearly
      documented in Java official specifications (6.4.2 and 8.2), javac considers
      that all methods and attributes are inherited, which leads to manage tricky
      exceptions to the usual overriding/hiding rules when these inherited members
      are not visible from the inheriting classes, tricky exceptions that are hard
      to understand and implement in a consistent way, and makes Java implemented
      semantics dangerously different in practice from the one officially
      specified by JavaSoft.
      ======================================================================

      ken.arnold@East 1997-03-21

      I've been playing around with an independent report of this same bug, and it seems as if we are hacking around a VM deficiency, so cannot fix the real problem. When I invoke a package-accessible method, the invoke_virtual has no way of knowing that I intend to invoke only a package-accessible one. The critical information has been lost. I think we need a new VM instruction, something like invoke_package_virtual. You cannot intuit this from the execution environment. We are giving partial inheritiance if inaccessible methods which violates JLS.

      ken.arnold@East 1997-03-21

            tturnidgsunw Todd Turnidge (Inactive)
            mchamnessunw Mark Chamness (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: