Bug report
Adam Hawthorne
adamh at basis.com
Thu Jan 10 14:42:54 PST 2013
Per Jonathan, here are the relevant fields from the public bug form:
Synopsis:
Trees.getScope() fails for TreePath in constructor with super() or this()
call
Full OS version:
Linux hostname 3.2.0-36-generic #56-Ubuntu SMP Wed Jan 2 21:50:39 UTC 2013
x86_64 x86_64 x86_64 GNU/Linux
Development Kit or Runtime version:
java version "1.7.0_10"
Java(TM) SE Runtime Environment (build 1.7.0_10-b18)
Java HotSpot(TM) 64-Bit Server VM (build 23.6-b04, mixed mode)
Description:
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.
Steps to Reproduce:
Using the provided source code, first, build the annotation processor:
javac -classpath .:/path/to/tools.jar bug/*.java
Then run the annotation processor on the test file:
javac -classpath . -processor bug.TreesScopeBug bug/TreesScopeBugTest.java
Expected Result:
I would expect to see no output, and for the TreesScopeBugTest.class file
to be updated.
Actual Result:
I see the error message:
bug/TreesScopeBugTest.java:6: error: call to super must be first statement
in constructor
super();
^
1 error
Source code for an executable test case:
bug/TestAnnotation.java:
===========================
package bug;
public @interface TestAnnotation {
// empty
}
bug/TreesScopeBug.java:
===========================
package bug;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import com.sun.source.tree.BlockTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
import com.sun.source.util.Trees;
@SupportedAnnotationTypes("bug.TestAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class TreesScopeBug extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> p_annotations,
RoundEnvironment p_roundEnv) {
final Trees tr = Trees.instance(processingEnv);
if (! p_annotations.isEmpty()) {
// Assuming TestAnnotation here
TypeElement testAnnElem = p_annotations.iterator().next();
Set<? extends Element> ctorSet =
p_roundEnv.getElementsAnnotatedWith(testAnnElem);
// Assuming one elem here.
Element e = ctorSet.iterator().next();
TreePath path = tr.getPath(e);
new TreePathScanner<Void, Void>() {
@Override
public Void visitBlock(BlockTree node, Void p) {
// Bug occurs here, but diagnostics are delayed.
tr.getScope(getCurrentPath());
return null;
}
}.scan(path, null);
}
return true;
}
}
bug/TreeScopeBugTest.java:
============================
package bug;
public class TreesScopeBugTest {
@TestAnnotation
TreesScopeBugTest() {
super();
}
}
Workaround:
I think I can workaround by duplicating the operation of 'Scope' in my
particular use-case, but I don't believe a generic workaround is available.
On Thu, Jan 10, 2013 at 5:22 PM, Adam Hawthorne <adamh at basis.com> wrote:
> Sorry in advance for the OT post.
>
> I've been trying all day to submit a javac/Tree API bug on
> bugreport.sun.com, and it fails every time. Is there some other place I
> can/should send this? The bug is in Trees.getScope() .
>
> Feel free to reply privately to avoid OT list traffic.
>
> Thanks in advance.
>
> Adam
>
>
--
Adam Hawthorne
Software Architect
BASIS International Ltd.
www.basis.com
+1.505.938.6169 Phone
+1.505.750.4128 Direct
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20130110/eb3d6eb4/attachment.html
More information about the compiler-dev
mailing list