-
Notifications
You must be signed in to change notification settings - Fork 6.1k
8194743: Compiler implementation for Statements before super() #13656
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 14 commits
9fe8e82
51a27c4
4aae83b
61c0eac
805b781
50d17ca
95140ee
53d19a0
dcf6fa6
9deaa03
ceb41f8
a1f1efc
7c874eb
0eac8b6
0b8a8c7
0e638da
3c9322b
73bb327
80ba6be
eb1eb5a
9a2cb45
543a596
a352a58
276e449
a5f8cc5
c6cf80f
b0b13b2
599d761
e2f8813
5cced39
4ff4c5c
a5510b7
599cf0e
150d794
3e75e19
355edfb
2356ac6
738c10a
99a277e
bbdb814
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3885,10 +3885,13 @@ void checkCyclicConstructors(JCClassDecl tree) { | |
|
|
||
| // enter each constructor this-call into the map | ||
| for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { | ||
| JCMethodInvocation app = TreeInfo.firstConstructorCall(l.head); | ||
| if (app == null) continue; | ||
| JCMethodDecl meth = (JCMethodDecl) l.head; | ||
| if (TreeInfo.name(app.meth) == names._this) { | ||
| if (!l.head.hasTag(METHODDEF)) | ||
| continue; | ||
| JCMethodDecl meth = (JCMethodDecl)l.head; | ||
| if (meth.name != names.init) | ||
| continue; | ||
| JCMethodInvocation app = TreeInfo.findConstructorCall(meth); | ||
| if (app != null && TreeInfo.name(app.meth) == names._this) { | ||
| callMap.put(meth.sym, TreeInfo.symbol(app.meth)); | ||
| } else { | ||
| meth.sym.flags_field |= ACYCLIC; | ||
|
|
@@ -3921,6 +3924,117 @@ private void checkCyclicConstructor(JCClassDecl tree, Symbol ctor, | |
| } | ||
| } | ||
|
|
||
| /* ************************************************************************* | ||
| * Verify the proper placement of super()/this() calls. | ||
| * | ||
| * - super()/this() may only appear in constructors | ||
| * - There must be at most one super()/this() call per constructor | ||
| * - The super()/this() call, if any, must be a top-level statement in the | ||
| * constructor, i.e., not nested inside any other statement or block | ||
| * - There must be no return statements prior to the super()/this() call | ||
| **************************************************************************/ | ||
|
|
||
| void checkSuperInitCalls(JCClassDecl tree) { | ||
| new SuperThisChecker().check(tree); | ||
| } | ||
|
|
||
| private class SuperThisChecker extends TreeScanner { | ||
archiecobbs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // Match this scan stack: 1=JCMethodDecl, 2=JCExpressionStatement, 3=JCMethodInvocation | ||
| private static final int MATCH_SCAN_DEPTH = 3; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic seems a bit fragile in that it relies on the shape of the AST. Also, in principle you can create something that will overflow the int, and will cause the check to spuriously pass for a very very very large number of nested expressions :-) That said, I understand that, in order to make this code "tighter", you would need to override al visitors that can "nest" code inside other code, which is also not great.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree about this appearing fragile. The thinking was that, well, the JLS rule for this is simply:
So this tries to mirror exactly that in the most direct way. The fragility comes from the possibility of some unexpected change to the AST class model... but at least if that ever did happen, you would probably get a clear warning when a bunch of JDK code stopped compiling :) |
||
|
|
||
| private boolean constructor; // is this method a constructor? | ||
| private JCReturn earlyReturn; // first return prior to the super()/init(), if any | ||
| private Name initCall; // whichever of "super" or "init" we've seen already | ||
| private int scanDepth; // current scan recursion depth in method body | ||
|
|
||
| public void check(JCClassDecl classDef) { | ||
| scan(classDef.defs); | ||
| } | ||
|
|
||
| @Override | ||
| public void visitMethodDef(JCMethodDecl tree) { | ||
| Assert.check(!constructor); | ||
| Assert.check(earlyReturn == null); | ||
| Assert.check(initCall == null); | ||
| Assert.check(scanDepth == 1); | ||
|
|
||
| // Initialize state for this method | ||
| constructor = tree.name == names.init; | ||
| try { | ||
|
|
||
| // Scan method body | ||
| if (tree.body != null) | ||
| scan(tree.body.stats); | ||
|
|
||
| // Verify no 'return' seen prior to an explicit super()/this() call | ||
| if (constructor && earlyReturn != null && initCall != null) | ||
| log.error(earlyReturn.pos(), Errors.ReturnBeforeSuperclassInitialized(initCall)); | ||
| } finally { | ||
| constructor = false; | ||
| earlyReturn = null; | ||
| initCall = null; | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void scan(JCTree tree) { | ||
| scanDepth++; | ||
| try { | ||
| super.scan(tree); | ||
| } finally { | ||
| scanDepth--; | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void visitApply(JCMethodInvocation apply) { | ||
| do { | ||
|
|
||
| // Is this a super() or this() call? | ||
| Name methodName = TreeInfo.name(apply.meth); | ||
| if (methodName != names._super && methodName != names._this) | ||
| break; | ||
|
|
||
| // super()/this() calls must only appear in a constructor | ||
| if (!constructor) { | ||
| log.error(apply.pos(), Errors.CallMustOnlyAppearInCtor(methodName)); | ||
| break; | ||
| } | ||
|
|
||
| // super()/this() calls must be a top level statement | ||
| if (scanDepth != MATCH_SCAN_DEPTH) { | ||
| log.error(apply.pos(), Errors.CallsNotAllowedHere(methodName)); | ||
| break; | ||
| } | ||
|
|
||
| // super()/this() calls must not appear more than once | ||
| if (initCall != null) { | ||
| log.error(apply.pos(), Errors.RedundantSuperclassInit(methodName, initCall)); | ||
| break; | ||
| } | ||
|
|
||
| // We found a legitimate super()/this() call; remember it | ||
| initCall = methodName; | ||
| } while (false); | ||
|
|
||
| // Proceed | ||
| super.visitApply(apply); | ||
| } | ||
|
|
||
| @Override | ||
| public void visitReturn(JCReturn tree) { | ||
| if (constructor && initCall == null && earlyReturn == null) | ||
| earlyReturn = tree; // we have seen a return but not (yet) a super()/this() | ||
| super.visitReturn(tree); | ||
| } | ||
|
|
||
| @Override | ||
| public void visitClassDef(JCClassDecl tree) { | ||
archiecobbs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // don't descend any further | ||
| } | ||
| } | ||
|
|
||
| /* ************************************************************************* | ||
| * Miscellaneous | ||
| **************************************************************************/ | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.