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

Trees.getScope() fails for TreePath in constructor with super() or this() call

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Cannot Reproduce
    • Icon: P4 P4
    • None
    • 7u11
    • tools
    • None

      In developing an annotation processor that's doing additional constraint checking, I noticed in Java 6 that I would receive error messages saying:

      :23: error: call to super must be first statement in constructor
              super(p_commandID);
                   ^

      These didn't affect the operation of my annotation processor. Now, we're in the process of upgrading to Java 7, and we discovered both that these error messages are fully formed, containing the entire java file, and that they prevent the annotation processor from completing successfully.

      These files compile correctly via javac, and upon further inspection of the files, the calls to super() are indeed the first statement in each of the constructors, so this error message is itself erroneous.


      In digging into the Javac source from the source tarball in OpenJDK 7u6, I see that the JavacTrees.getScope() method calls JavacTrees.getAttrContext(TreePath) . getAttrContext() iterates through the JCTree's in the TreePath and uses a switch on the Kind to visit each one.

      In the BLOCK case of that switch statement, there is the following code:

                          if (method != null)
                              env = memberEnter.getMethodEnv(method, env);
                          JCTree body = copier.copy((JCTree)tree, (JCTree) path.getLeaf());
                          env = attribStatToTree(body, env, copier.leafCopy);
                          return env;

      I believe the problem might be that 'env' is passed to attribStatToTree(), but 'env' contains a reference to the original Tree, while 'body' and 'copier.leafCopy' have a distinct copy of the tree.

      Later, deeper in the call stack, Attr.checkFirstConstructorStat() has the following code. The portion of the expression that fails is:

      ((JCExpressionStatement) body.stats.head).expr == tree

      Since 'body' was obtained from 'enclMethod' and 'enclMethod' was obtained from 'env', and since 'env' doesn't refer to the same JCTree as 'tree', the identity comparison fails.

      boolean checkFirstConstructorStat(JCMethodInvocation tree, Env<AttrContext> env) {
                  JCMethodDecl enclMethod = env.enclMethod;
                  if (enclMethod != null && enclMethod.name == names.init) {
                      JCBlock body = enclMethod.body;
                      if (body.stats.head.getTag() == JCTree.EXEC &&
                          ((JCExpressionStatement) body.stats.head).expr == tree)
                          return true;
                  }
                  log.error(tree.pos(),"call.must.be.first.stmt.in.ctor",
                            TreeInfo.name(tree.meth));
                  return false;
              }

      I'm not at all familiar with the Javac internals, so this is all speculation, but after inspecting, this makes the most sense to me. Hope it helps.

            jjg Jonathan Gibbons
            jjg Jonathan Gibbons
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: