8194743: Compiler implementation for Statements before super()

Reviewed-by: vromero, jwaters, mcimadamore
This commit is contained in:
Archie Cobbs 2023-11-27 17:26:52 +00:00 committed by Vicente Romero
parent 5e24aaf4f7
commit 12e983a72e
26 changed files with 1272 additions and 311 deletions

View File

@ -211,6 +211,7 @@ public class Preview {
return switch (feature) {
case STRING_TEMPLATES -> true;
case UNNAMED_CLASSES -> true;
case SUPER_INIT -> true;
//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
//When real preview features will be added, this method can be implemented to return 'true'
//for those selected features, and 'false' for all the others.

View File

@ -248,6 +248,7 @@ public enum Source {
UNNAMED_CLASSES(JDK21, Fragments.FeatureUnnamedClasses, DiagKind.PLURAL),
WARN_ON_ILLEGAL_UTF8(MIN, JDK21),
UNNAMED_VARIABLES(JDK22, Fragments.FeatureUnnamedVariables, DiagKind.PLURAL),
SUPER_INIT(JDK22, Fragments.FeatureSuperInit, DiagKind.NORMAL),
;
enum DiagKind {

View File

@ -937,6 +937,8 @@ public class Attr extends JCTree.Visitor {
Optional<ArgumentAttr.LocalCacheContext> localCacheContext =
Optional.ofNullable(env.info.attributionMode.isSpeculative ?
argumentAttr.withLocalCacheContext() : null);
boolean ctorProloguePrev = env.info.ctorPrologue;
env.info.ctorPrologue = false;
try {
// Local and anonymous classes have not been entered yet, so we need to
// do it now.
@ -959,12 +961,10 @@ public class Attr extends JCTree.Visitor {
// make sure class has been completed:
c.complete();
// If this class appears as an anonymous class
// in a superclass constructor call
// disable implicit outer instance from being passed.
// If this class appears as an anonymous class in a constructor
// prologue, disable implicit outer instance from being passed.
// (This would be an illegal access to "this before super").
if (env.info.isSelfCall &&
env.tree.hasTag(NEWCLASS)) {
if (ctorProloguePrev && env.tree.hasTag(NEWCLASS)) {
c.flags_field |= NOOUTERTHIS;
}
attribClass(tree.pos(), c);
@ -972,6 +972,7 @@ public class Attr extends JCTree.Visitor {
}
} finally {
localCacheContext.ifPresent(LocalCacheContext::leave);
env.info.ctorPrologue = ctorProloguePrev;
}
}
@ -981,6 +982,8 @@ public class Attr extends JCTree.Visitor {
Lint lint = env.info.lint.augment(m);
Lint prevLint = chk.setLint(lint);
boolean ctorProloguePrev = env.info.ctorPrologue;
env.info.ctorPrologue = false;
MethodSymbol prevMethod = chk.setMethod(m);
try {
deferredLintHandler.flush(tree.pos());
@ -1044,6 +1047,9 @@ public class Attr extends JCTree.Visitor {
chk.validate(tree.recvparam, newEnv);
}
// Is this method a constructor?
boolean isConstructor = TreeInfo.isConstructor(tree);
if (env.enclClass.sym.isRecord() && tree.sym.owner.kind == TYP) {
// lets find if this method is an accessor
Optional<? extends RecordComponent> recordComponent = env.enclClass.sym.getRecordComponents().stream()
@ -1071,14 +1077,11 @@ public class Attr extends JCTree.Visitor {
}
}
if (tree.name == names.init) {
if (isConstructor) {
// if this a constructor other than the canonical one
if ((tree.sym.flags_field & RECORD) == 0) {
JCMethodInvocation app = TreeInfo.firstConstructorCall(tree);
if (app == null ||
TreeInfo.name(app.meth) != names._this ||
!checkFirstConstructorStat(app, tree, false)) {
log.error(tree, Errors.FirstStatementMustBeCallToAnotherConstructor(env.enclClass.sym));
if (!TreeInfo.hasConstructorCall(tree, names._this)) {
log.error(tree, Errors.NonCanonicalConstructorInvokeAnotherConstructor(env.enclClass.sym));
}
} else {
// but if it is the canonical:
@ -1104,11 +1107,7 @@ public class Attr extends JCTree.Visitor {
);
}
JCMethodInvocation app = TreeInfo.firstConstructorCall(tree);
if (app != null &&
(TreeInfo.name(app.meth) == names._this ||
TreeInfo.name(app.meth) == names._super) &&
checkFirstConstructorStat(app, tree, false)) {
if (TreeInfo.hasAnyConstructorCall(tree)) {
log.error(tree, Errors.InvalidCanonicalConstructorInRecord(
Fragments.Canonical, env.enclClass.sym.name,
Fragments.CanonicalMustNotContainExplicitConstructorInvocation));
@ -1186,16 +1185,14 @@ public class Attr extends JCTree.Visitor {
// Add an implicit super() call unless an explicit call to
// super(...) or this(...) is given
// or we are compiling class java.lang.Object.
if (tree.name == names.init && owner.type != syms.objectType) {
JCBlock body = tree.body;
if (body.stats.isEmpty() ||
TreeInfo.getConstructorInvocationName(body.stats, names) == names.empty) {
JCStatement supCall = make.at(body.pos).Exec(make.Apply(List.nil(),
if (isConstructor && owner.type != syms.objectType) {
if (!TreeInfo.hasAnyConstructorCall(tree)) {
JCStatement supCall = make.at(tree.body.pos).Exec(make.Apply(List.nil(),
make.Ident(names._super), make.Idents(List.nil())));
body.stats = body.stats.prepend(supCall);
tree.body.stats = tree.body.stats.prepend(supCall);
} else if ((env.enclClass.sym.flags() & ENUM) != 0 &&
(tree.mods.flags & GENERATEDCONSTR) == 0 &&
TreeInfo.isSuperCall(body.stats.head)) {
TreeInfo.hasConstructorCall(tree, names._super)) {
// enum constructors are not allowed to call super
// directly, so make sure there aren't any super calls
// in enum constructors, except in the compiler
@ -1225,6 +1222,9 @@ public class Attr extends JCTree.Visitor {
annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m, null);
annotate.flush();
// Start of constructor prologue
localEnv.info.ctorPrologue = isConstructor;
// Attribute method body.
attribStat(tree.body, localEnv);
}
@ -1234,6 +1234,7 @@ public class Attr extends JCTree.Visitor {
} finally {
chk.setLint(prevLint);
chk.setMethod(prevMethod);
env.info.ctorPrologue = ctorProloguePrev;
}
}
@ -2518,21 +2519,15 @@ public class Attr extends JCTree.Visitor {
ListBuffer<Type> argtypesBuf = new ListBuffer<>();
if (isConstructorCall) {
// We are seeing a ...this(...) or ...super(...) call.
// Check that this is the first statement in a constructor.
checkFirstConstructorStat(tree, env.enclMethod, true);
// Record the fact
// that this is a constructor call (using isSelfCall).
localEnv.info.isSelfCall = true;
// Attribute arguments, yielding list of argument types.
localEnv.info.constructorArgs = true;
KindSelector kind = attribArgs(KindSelector.MTH, tree.args, localEnv, argtypesBuf);
localEnv.info.constructorArgs = false;
argtypes = argtypesBuf.toList();
typeargtypes = attribTypes(tree.typeargs, localEnv);
// Done with this()/super() parameters. End of constructor prologue.
env.info.ctorPrologue = false;
// Variable `site' points to the class in which the called
// constructor is defined.
Type site = env.enclClass.sym.type;
@ -2661,26 +2656,6 @@ public class Attr extends JCTree.Visitor {
}
}
/** Check that given application node appears as first statement
* in a constructor call.
* @param tree The application node
* @param enclMethod The enclosing method of the application.
* @param error Should an error be issued?
*/
boolean checkFirstConstructorStat(JCMethodInvocation tree, JCMethodDecl enclMethod, boolean error) {
if (enclMethod != null && enclMethod.name == names.init) {
JCBlock body = enclMethod.body;
if (body.stats.head.hasTag(EXEC) &&
((JCExpressionStatement) body.stats.head).expr == tree)
return true;
}
if (error) {
log.error(tree.pos(),
Errors.CallMustBeFirstStmtInCtor(TreeInfo.name(tree.meth)));
}
return false;
}
/** Obtain a method type with given argument types.
*/
Type newMethodTemplate(Type restype, List<Type> argtypes, List<Type> typeargtypes) {
@ -4353,16 +4328,6 @@ public class Attr extends JCTree.Visitor {
checkAssignable(tree.pos(), v, null, env);
}
// In a constructor body,
// if symbol is a field or instance method, check that it is
// not accessed before the supertype constructor is called.
if (symEnv.info.isSelfCall &&
sym.kind.matches(KindSelector.VAL_MTH) &&
sym.owner.kind == TYP &&
(sym.flags() & STATIC) == 0) {
chk.earlyRefError(tree.pos(), sym.kind == VAR ?
sym : thisSym(tree.pos(), env));
}
Env<AttrContext> env1 = env;
if (sym.kind != ERR && sym.kind != TYP &&
sym.owner != null && sym.owner != env1.enclClass.sym) {
@ -4474,18 +4439,7 @@ public class Attr extends JCTree.Visitor {
}
if (isType(sitesym)) {
if (sym.name == names._this || sym.name == names._super) {
// If `C' is the currently compiled class, check that
// `C.this' does not appear in an explicit call to a constructor
// also make sure that `super` is not used in constructor invocations
if (env.info.isSelfCall &&
((sym.name == names._this &&
site.tsym == env.enclClass.sym) ||
sym.name == names._super && env.info.constructorArgs &&
(sitesym.isInterface() || site.tsym == env.enclClass.sym))) {
chk.earlyRefError(tree.pos(), sym);
}
} else {
if (sym.name != names._this && sym.name != names._super) {
// Check if type-qualified fields or methods are static (JLS)
if ((sym.flags() & STATIC) == 0 &&
sym.name != names._super &&
@ -5674,6 +5628,9 @@ public class Attr extends JCTree.Visitor {
}
}
// Check for proper placement of super()/this() calls.
chk.checkSuperInitCalls(tree);
// Check for cycles among non-initial constructors.
chk.checkCyclicConstructors(tree);

View File

@ -49,13 +49,9 @@ public class AttrContext {
*/
int staticLevel = 0;
/** Is this an environment for a this(...) or super(...) call?
/** Are we in the 'prologue' part of a constructor, prior to an explicit this()/super()?
*/
boolean isSelfCall = false;
/** are we analyzing the arguments for a constructor invocation?
*/
boolean constructorArgs = false;
boolean ctorPrologue = false;
/** Are we evaluating the selector of a `super' or type name?
*/
@ -136,8 +132,7 @@ public class AttrContext {
AttrContext info = new AttrContext();
info.scope = scope;
info.staticLevel = staticLevel;
info.isSelfCall = isSelfCall;
info.constructorArgs = constructorArgs;
info.ctorPrologue = ctorPrologue;
info.selectSuper = selectSuper;
info.pendingResolutionPhase = pendingResolutionPhase;
info.lint = lint;

View File

@ -359,15 +359,6 @@ public class Check {
return types.createErrorType(found instanceof Type type ? type : syms.errType);
}
/** Report an error that symbol cannot be referenced before super
* has been called.
* @param pos Position to be used for error reporting.
* @param sym The referenced symbol.
*/
void earlyRefError(DiagnosticPosition pos, Symbol sym) {
log.error(pos, Errors.CantRefBeforeCtorCalled(sym));
}
/** Report duplicate declaration error.
*/
void duplicateError(DiagnosticPosition pos, Symbol sym) {
@ -3934,10 +3925,11 @@ public class Check {
// 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 (!TreeInfo.isConstructor(l.head))
continue;
JCMethodDecl meth = (JCMethodDecl)l.head;
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;
@ -3970,6 +3962,128 @@ public class Check {
}
}
/* *************************************************************************
* 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 {
// Match this scan stack: 1=JCMethodDecl, 2=JCExpressionStatement, 3=JCMethodInvocation
private static final int MATCH_SCAN_DEPTH = 3;
private boolean constructor; // is this method a constructor?
private boolean firstStatement; // at the first statement in method?
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 = TreeInfo.isConstructor(tree);
try {
// Scan method body
if (tree.body != null) {
firstStatement = true;
for (List<JCStatement> l = tree.body.stats; l.nonEmpty(); l = l.tail) {
scan(l.head);
firstStatement = false;
}
}
// Verify no 'return' seen prior to an explicit super()/this() call
if (constructor && earlyReturn != null && initCall != null)
log.error(earlyReturn.pos(), Errors.ReturnBeforeSuperclassInitialized);
} finally {
firstStatement = false;
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);
break;
}
// super()/this() calls must be a top level statement
if (scanDepth != MATCH_SCAN_DEPTH) {
log.error(apply.pos(), Errors.CtorCallsNotAllowedHere);
break;
}
// super()/this() calls must not appear more than once
if (initCall != null) {
log.error(apply.pos(), Errors.RedundantSuperclassInit);
break;
}
// If super()/this() isn't first, require "statements before super()" feature
if (!firstStatement)
preview.checkSourceLevel(apply.pos(), Feature.SUPER_INIT);
// 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) {
// don't descend any further
}
}
/* *************************************************************************
* Miscellaneous
**************************************************************************/

View File

@ -206,7 +206,6 @@ public class Enter extends JCTree.Visitor {
env.dup(tree, env.info.dup(WriteableScope.create(tree.sym)));
localEnv.enclClass = tree;
localEnv.outer = env;
localEnv.info.isSelfCall = false;
localEnv.info.lint = null; // leave this to be filled in by Attr,
// when annotations have been processed
localEnv.info.isAnonymousDiamond = TreeInfo.isDiamond(env.tree);
@ -259,7 +258,6 @@ public class Enter extends JCTree.Visitor {
env.dup(tree, env.info.dup(WriteableScope.create(tree.sym)));
localEnv.enclClass = predefClassDef;
localEnv.outer = env;
localEnv.info.isSelfCall = false;
localEnv.info.lint = null; // leave this to be filled in by Attr,
// when annotations have been processed
return localEnv;

View File

@ -32,6 +32,7 @@ import java.util.Map.Entry;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.LambdaExpressionTree.BodyKind;
@ -386,6 +387,13 @@ public class Flow {
*/
ListBuffer<PendingExit> pendingExits;
/** A class whose initializers we are scanning. Because initializer
* scans can be triggered out of sequence when visiting certain nodes
* (e.g., super()), we protect against infinite loops that could be
* triggered by incorrect code (e.g., super() inside initializer).
*/
JCClassDecl initScanClass;
/** A pending exit. These are the statements return, break, and
* continue. In addition, exception-throwing expressions or
* statements are put here when not known to be caught. This
@ -471,6 +479,24 @@ public class Flow {
scan(brk);
}
}
// Do something with all static or non-static field initializers and initialization blocks.
// Note: This method also sends nested class definitions to the handler.
protected void forEachInitializer(JCClassDecl classDef, boolean isStatic, Consumer<? super JCTree> handler) {
if (classDef == initScanClass) // avoid infinite loops
return;
JCClassDecl initScanClassPrev = initScanClass;
initScanClass = classDef;
try {
for (List<JCTree> defs = classDef.defs; defs.nonEmpty(); defs = defs.tail) {
JCTree def = defs.head;
if (!def.hasTag(METHODDEF) && ((TreeInfo.flags(def) & STATIC) != 0) == isStatic)
handler.accept(def);
}
} finally {
initScanClass = initScanClassPrev;
}
}
}
/**
@ -536,22 +562,16 @@ public class Flow {
try {
// process all the static initializers
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (!l.head.hasTag(METHODDEF) &&
(TreeInfo.flags(l.head) & STATIC) != 0) {
scanDef(l.head);
clearPendingExits(false);
}
}
forEachInitializer(tree, true, def -> {
scanDef(def);
clearPendingExits(false);
});
// process all the instance initializers
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (!l.head.hasTag(METHODDEF) &&
(TreeInfo.flags(l.head) & STATIC) == 0) {
scanDef(l.head);
clearPendingExits(false);
}
}
forEachInitializer(tree, false, def -> {
scanDef(def);
clearPendingExits(false);
});
// process all the methods
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
@ -1362,40 +1382,10 @@ public class Flow {
try {
// process all the static initializers
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (!l.head.hasTag(METHODDEF) &&
(TreeInfo.flags(l.head) & STATIC) != 0) {
scan(l.head);
errorUncaught();
}
}
// add intersection of all throws clauses of initial constructors
// to set of caught exceptions, unless class is anonymous.
if (!anonymousClass) {
boolean firstConstructor = true;
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (TreeInfo.isInitialConstructor(l.head)) {
List<Type> mthrown =
((JCMethodDecl) l.head).sym.type.getThrownTypes();
if (firstConstructor) {
caught = mthrown;
firstConstructor = false;
} else {
caught = chk.intersect(mthrown, caught);
}
}
}
}
// process all the instance initializers
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (!l.head.hasTag(METHODDEF) &&
(TreeInfo.flags(l.head) & STATIC) == 0) {
scan(l.head);
errorUncaught();
}
}
forEachInitializer(tree, true, def -> {
scan(def);
errorUncaught();
});
// in an anonymous class, add the set of thrown exceptions to
// the throws clause of the synthetic constructor and propagate
@ -1450,7 +1440,7 @@ public class Flow {
JCVariableDecl def = l.head;
scan(def);
}
if (TreeInfo.isInitialConstructor(tree))
if (TreeInfo.hasConstructorCall(tree, names._super))
caught = chk.union(caught, mthrown);
else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK)
caught = mthrown;
@ -1751,8 +1741,18 @@ public class Flow {
public void visitApply(JCMethodInvocation tree) {
scan(tree.meth);
scan(tree.args);
// Mark as thrown the exceptions thrown by the method being invoked
for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail)
markThrown(tree, l.head);
// After super(), scan initializers to uncover any exceptions they throw
if (TreeInfo.name(tree.meth) == names._super) {
forEachInitializer(classDef, false, def -> {
scan(def);
errorUncaught();
});
}
}
public void visitNewClass(JCNewClass tree) {
@ -2095,11 +2095,11 @@ public class Flow {
uninitsWhenFalse = new Bits(true);
}
private boolean isInitialConstructor = false;
private boolean isConstructor;
@Override
protected void markDead() {
if (!isInitialConstructor) {
if (!isConstructor) {
inits.inclRange(returnadr, nextadr);
} else {
for (int address = returnadr; address < nextadr; address++) {
@ -2346,13 +2346,10 @@ public class Flow {
}
// process all the static initializers
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (!l.head.hasTag(METHODDEF) &&
(TreeInfo.flags(l.head) & STATIC) != 0) {
scan(l.head);
clearPendingExits(false);
}
}
forEachInitializer(tree, true, def -> {
scan(def);
clearPendingExits(false);
});
// verify all static final fields got initailized
for (int i = firstadr; i < nextadr; i++) {
@ -2376,15 +2373,6 @@ public class Flow {
}
}
// process all the instance initializers
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (!l.head.hasTag(METHODDEF) &&
(TreeInfo.flags(l.head) & STATIC) == 0) {
scan(l.head);
clearPendingExits(false);
}
}
// process all the methods
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (l.head.hasTag(METHODDEF)) {
@ -2423,13 +2411,16 @@ public class Flow {
int returnadrPrev = returnadr;
Assert.check(pendingExits.isEmpty());
boolean lastInitialConstructor = isInitialConstructor;
boolean isConstructorPrev = isConstructor;
try {
isInitialConstructor = TreeInfo.isInitialConstructor(tree);
isConstructor = TreeInfo.isConstructor(tree);
if (!isInitialConstructor) {
// We only track field initialization inside constructors
if (!isConstructor) {
firstadr = nextadr;
}
// Mark all method parameters as DA
for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
JCVariableDecl def = l.head;
scan(def);
@ -2445,7 +2436,7 @@ public class Flow {
boolean isCompactOrGeneratedRecordConstructor = (tree.sym.flags() & Flags.COMPACT_RECORD_CONSTRUCTOR) != 0 ||
(tree.sym.flags() & (GENERATEDCONSTR | RECORD)) == (GENERATEDCONSTR | RECORD);
if (isInitialConstructor) {
if (isConstructor) {
boolean isSynthesized = (tree.sym.flags() &
GENERATEDCONSTR) != 0;
for (int i = firstadr; i < nextadr; i++) {
@ -2487,7 +2478,7 @@ public class Flow {
nextadr = nextadrPrev;
firstadr = firstadrPrev;
returnadr = returnadrPrev;
isInitialConstructor = lastInitialConstructor;
isConstructor = isConstructorPrev;
}
} finally {
lint = lintPrev;
@ -2503,7 +2494,7 @@ public class Flow {
Assert.check((inMethod && exit.tree.hasTag(RETURN)) ||
log.hasErrorOn(exit.tree.pos()),
exit.tree);
if (inMethod && isInitialConstructor) {
if (inMethod && isConstructor) {
Assert.check(exit instanceof AssignPendingExit);
inits.assign(((AssignPendingExit) exit).exit_inits);
for (int i = firstadr; i < nextadr; i++) {
@ -2959,6 +2950,28 @@ public class Flow {
public void visitApply(JCMethodInvocation tree) {
scanExpr(tree.meth);
scanExprs(tree.args);
// Handle superclass constructor invocations
if (isConstructor) {
// If super(): at this point all initialization blocks will execute
Name name = TreeInfo.name(tree.meth);
if (name == names._super) {
forEachInitializer(classDef, false, def -> {
scan(def);
clearPendingExits(false);
});
}
// If this(): at this point all final uninitialized fields will get initialized
else if (name == names._this) {
for (int address = firstadr; address < nextadr; address++) {
VarSymbol sym = vardecls[address].sym;
if (isFinalUninitializedField(sym) && !sym.isStatic())
letInit(tree.pos(), sym);
}
}
}
}
public void visitNewClass(JCNewClass tree) {

View File

@ -1859,7 +1859,7 @@ public class Lower extends TreeTranslator {
ot = ots.head;
} while (ot.owner != otc);
if (otc.owner.kind != PCK && !otc.hasOuterInstance()) {
chk.earlyRefError(pos, c);
log.error(pos, Errors.NoEnclInstanceOfTypeInScope(c));
Assert.error(); // should have been caught in Attr
return makeNull();
}
@ -1899,7 +1899,6 @@ public class Lower extends TreeTranslator {
List<VarSymbol> ots = outerThisStack;
if (ots.isEmpty()) {
log.error(pos, Errors.NoEnclInstanceOfTypeInScope(c));
Assert.error();
return makeNull();
}
VarSymbol ot = ots.head;
@ -1911,7 +1910,6 @@ public class Lower extends TreeTranslator {
ots = ots.tail;
if (ots.isEmpty()) {
log.error(pos, Errors.NoEnclInstanceOfTypeInScope(c));
Assert.error();
return tree;
}
ot = ots.head;
@ -2351,17 +2349,19 @@ public class Lower extends TreeTranslator {
tree.defs = tree.defs.prepend(l.head);
enterSynthetic(tree.pos(), l.head.sym, currentClass.members());
}
// If this$n was accessed, add the field definition and
// update initial constructors to initialize it
// If this$n was accessed, add the field definition and prepend
// initializer code to any super() invocation to initialize it
if (currentClass.hasOuterInstance() && shouldEmitOuterThis(currentClass)) {
tree.defs = tree.defs.prepend(otdef);
enterSynthetic(tree.pos(), otdef.sym, currentClass.members());
for (JCTree def : tree.defs) {
if (TreeInfo.isInitialConstructor(def)) {
JCMethodDecl mdef = (JCMethodDecl) def;
mdef.body.stats = mdef.body.stats.prepend(
initOuterThis(mdef.body.pos, mdef.params.head.sym));
for (JCTree def : tree.defs) {
if (TreeInfo.isConstructor(def)) {
JCMethodDecl mdef = (JCMethodDecl)def;
if (TreeInfo.hasConstructorCall(mdef, names._super)) {
List<JCStatement> initializer = List.of(initOuterThis(mdef.body.pos, mdef.params.head.sym));
TreeInfo.mapSuperCalls(mdef.body, supercall -> make.Block(0, initializer.append(supercall)));
}
}
}
}
@ -2826,19 +2826,18 @@ public class Lower extends TreeTranslator {
tree.params = tree.params.prepend(otdef);
}
// If this is an initial constructor, i.e., it does not start with
// this(...), insert initializers for this$n and proxies
// before (pre-1.4, after) the call to superclass constructor.
JCStatement selfCall = translate(tree.body.stats.head);
// Determine whether this constructor has a super() invocation
boolean invokesSuper = TreeInfo.hasConstructorCall(tree, names._super);
List<JCStatement> added = List.nil();
// Create initializers for this$n and proxies
ListBuffer<JCStatement> added = new ListBuffer<>();
if (fvs.nonEmpty()) {
List<Type> addedargtypes = List.nil();
for (List<VarSymbol> l = fvs; l.nonEmpty(); l = l.tail) {
m.capturedLocals =
m.capturedLocals.prepend((VarSymbol)
(proxies.get(l.head)));
if (TreeInfo.isInitialConstructor(tree)) {
if (invokesSuper) {
added = added.prepend(
initField(tree.body.pos, proxies.get(l.head), prevProxies.get(l.head)));
}
@ -2852,13 +2851,18 @@ public class Lower extends TreeTranslator {
syms.methodClass);
}
// Recursively translate existing local statements
tree.body.stats = translate(tree.body.stats);
// Prepend initializers in front of super() call
if (added.nonEmpty()) {
List<JCStatement> initializers = added.toList();
TreeInfo.mapSuperCalls(tree.body, supercall -> make.Block(0, initializers.append(supercall)));
}
// pop local variables from proxy stack
proxies = prevProxies;
// recursively translate following local statements and
// combine with this- or super-call
List<JCStatement> stats = translate(tree.body.stats.tail);
tree.body.stats = stats.prepend(selfCall).prependList(added);
outerThisStack = prevOuterThisStack;
} else {
Map<Symbol, Symbol> prevLambdaTranslationMap =

View File

@ -1503,13 +1503,15 @@ public class Resolve {
sym = findField(env1, env1.enclClass.sym.type, name, env1.enclClass.sym);
}
if (sym.exists()) {
if (staticOnly &&
sym.kind == VAR &&
if (sym.kind == VAR &&
sym.owner.kind == TYP &&
(sym.flags() & STATIC) == 0)
return new StaticError(sym);
else
return sym;
(sym.flags() & STATIC) == 0) {
if (staticOnly)
return new StaticError(sym);
if (env1.info.ctorPrologue && (sym.flags_field & SYNTHETIC) == 0)
return new RefBeforeCtorCalledError(sym);
}
return sym;
} else {
bestSoFar = bestOf(bestSoFar, sym);
}
@ -2006,11 +2008,15 @@ public class Resolve {
env1, env1.enclClass.sym.type, name, argtypes, typeargtypes,
allowBoxing, useVarargs);
if (sym.exists()) {
if (staticOnly &&
sym.kind == MTH &&
sym.owner.kind == TYP &&
(sym.flags() & STATIC) == 0) return new StaticError(sym);
else return sym;
if (sym.kind == MTH &&
sym.owner.kind == TYP &&
(sym.flags() & STATIC) == 0) {
if (staticOnly)
return new StaticError(sym);
if (env1.info.ctorPrologue && env1 == env)
return new RefBeforeCtorCalledError(sym);
}
return sym;
} else {
bestSoFar = bestOf(bestSoFar, sym);
}
@ -3767,7 +3773,10 @@ public class Resolve {
if (env1.enclClass.sym == c) {
Symbol sym = env1.info.scope.findFirst(name);
if (sym != null) {
if (staticOnly) sym = new StaticError(sym);
if (staticOnly)
sym = new StaticError(sym);
else if (env1.info.ctorPrologue)
sym = new RefBeforeCtorCalledError(sym);
return accessBase(sym, pos, env.enclClass.sym.type,
name, true);
}
@ -3781,6 +3790,8 @@ public class Resolve {
//this might be a default super call if one of the superinterfaces is 'c'
for (Type t : pruneInterfaces(env.enclClass.type)) {
if (t.tsym == c) {
if (env.info.ctorPrologue)
log.error(pos, Errors.CantRefBeforeCtorCalled(name));
env.info.defaultSuperCallSite = t;
return new VarSymbol(0, names._super,
types.asSuper(env.enclClass.type, c), env.enclClass.sym);
@ -3882,8 +3893,8 @@ public class Resolve {
Type thisType = (t.tsym.owner.kind.matches(KindSelector.VAL_MTH)
? resolveSelf(pos, env, t.getEnclosingType().tsym, names._this)
: resolveSelfContaining(pos, env, t.tsym, isSuperCall)).type;
if (env.info.isSelfCall && thisType.tsym == env.enclClass.sym) {
log.error(pos, Errors.CantRefBeforeCtorCalled("this"));
if (env.info.ctorPrologue && thisType.tsym == env.enclClass.sym) {
log.error(pos, Errors.CantRefBeforeCtorCalled(names._this));
}
return thisType;
}
@ -4588,7 +4599,11 @@ public class Resolve {
class StaticError extends InvalidSymbolError {
StaticError(Symbol sym) {
super(STATICERR, sym, "static error");
this(sym, "static error");
}
StaticError(Symbol sym, String debugName) {
super(STATICERR, sym, debugName);
}
@Override
@ -4607,6 +4622,32 @@ public class Resolve {
}
}
/**
* Specialization of {@link InvalidSymbolError} for illegal
* early accesses within a constructor prologue.
*/
class RefBeforeCtorCalledError extends StaticError {
RefBeforeCtorCalledError(Symbol sym) {
super(sym, "prologue error");
}
@Override
JCDiagnostic getDiagnostic(JCDiagnostic.DiagnosticType dkind,
DiagnosticPosition pos,
Symbol location,
Type site,
Name name,
List<Type> argtypes,
List<Type> typeargtypes) {
Symbol errSym = ((sym.kind == TYP && sym.type.hasTag(CLASS))
? types.erasure(sym.type).tsym
: sym);
return diags.create(dkind, log.currentSource(), pos,
"cant.ref.before.ctor.called", errSym);
}
}
/**
* InvalidSymbolError error class indicating that a pair of symbols
* (either methods, constructors or operands) are ambiguous
@ -4720,7 +4761,7 @@ public class Resolve {
boolean unboundLookup;
public BadMethodReferenceError(Symbol sym, boolean unboundLookup) {
super(sym);
super(sym, "bad method ref error");
this.unboundLookup = unboundLookup;
}

View File

@ -570,7 +570,6 @@ public class TypeEnter implements Completer {
Env<AttrContext> localEnv = outer.dup(tree, outer.info.dup(baseScope));
localEnv.baseClause = true;
localEnv.outer = outer;
localEnv.info.isSelfCall = false;
return localEnv;
}

View File

@ -541,44 +541,18 @@ public class Gen extends JCTree.Visitor {
nerrs++;
}
/** Insert instance initializer code into initial constructor.
/** Insert instance initializer code into constructors prior to the super() call.
* @param md The tree potentially representing a
* constructor's definition.
* @param initCode The list of instance initializer statements.
* @param initTAs Type annotations from the initializer expression.
*/
void normalizeMethod(JCMethodDecl md, List<JCStatement> initCode, List<TypeCompound> initTAs) {
if (md.name == names.init && TreeInfo.isInitialConstructor(md)) {
// We are seeing a constructor that does not call another
// constructor of the same class.
List<JCStatement> stats = md.body.stats;
ListBuffer<JCStatement> newstats = new ListBuffer<>();
if (TreeInfo.isConstructor(md) && TreeInfo.hasConstructorCall(md, names._super)) {
// We are seeing a constructor that has a super() call.
// Find the super() invocation and append the given initializer code.
TreeInfo.mapSuperCalls(md.body, supercall -> make.Block(0, initCode.prepend(supercall)));
if (stats.nonEmpty()) {
// Copy initializers of synthetic variables generated in
// the translation of inner classes.
while (TreeInfo.isSyntheticInit(stats.head)) {
newstats.append(stats.head);
stats = stats.tail;
}
// Copy superclass constructor call
newstats.append(stats.head);
stats = stats.tail;
// Copy remaining synthetic initializers.
while (stats.nonEmpty() &&
TreeInfo.isSyntheticInit(stats.head)) {
newstats.append(stats.head);
stats = stats.tail;
}
// Now insert the initializer code.
newstats.appendList(initCode);
// And copy all remaining statements.
while (stats.nonEmpty()) {
newstats.append(stats.head);
stats = stats.tail;
}
}
md.body.stats = newstats.toList();
if (md.body.endpos == Position.NOPOS)
md.body.endpos = TreeInfo.endPos(md.body.stats.last());

View File

@ -233,9 +233,17 @@ compiler.err.switch.expression.empty=\
compiler.err.switch.expression.no.result.expressions=\
switch expression does not have any result expressions
# 0: name
compiler.err.call.must.be.first.stmt.in.ctor=\
call to {0} must be first statement in constructor
compiler.err.call.must.only.appear.in.ctor=\
explicit constructor invocation may only appear within a constructor body
compiler.err.redundant.superclass.init=\
redundant explicit constructor invocation
compiler.err.ctor.calls.not.allowed.here=\
explicit constructor invocation not allowed here
compiler.err.return.before.superclass.initialized=\
''return'' not allowed before explicit constructor invocation
# 0: symbol kind, 1: name, 2: symbol kind, 3: type, 4: message segment
compiler.err.cant.apply.symbol.noargs=\
@ -387,7 +395,7 @@ compiler.err.annotation.decl.not.allowed.here=\
compiler.err.cant.inherit.from.final=\
cannot inherit from final {0}
# 0: symbol or string
# 0: symbol or name
compiler.err.cant.ref.before.ctor.called=\
cannot reference {0} before supertype constructor has been called
@ -3193,6 +3201,9 @@ compiler.misc.feature.unconditional.patterns.in.instanceof=\
compiler.misc.feature.unnamed.classes=\
unnamed classes
compiler.misc.feature.super.init=\
statements before super()
compiler.warn.underscore.as.identifier=\
as of release 9, ''_'' is a keyword, and may not be used as an identifier
@ -3895,8 +3906,8 @@ compiler.err.invalid.supertype.record=\
classes cannot directly extend {0}
# 0: symbol
compiler.err.first.statement.must.be.call.to.another.constructor=\
constructor is not canonical, so its first statement must invoke another constructor of class {0}
compiler.err.non.canonical.constructor.invoke.another.constructor=\
constructor is not canonical, so it must invoke another constructor of class {0}
compiler.err.instance.initializer.not.allowed.in.records=\
instance initializers not allowed in records

View File

@ -50,6 +50,7 @@ import static com.sun.tools.javac.tree.JCTree.Tag.SYNCHRONIZED;
import javax.lang.model.element.ElementKind;
import javax.tools.JavaFileObject;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
@ -113,25 +114,6 @@ public class TreeInfo {
return false;
}
/** Is there a constructor invocation in the given list of trees?
*/
public static Name getConstructorInvocationName(List<? extends JCTree> trees, Names names) {
for (JCTree tree : trees) {
if (tree.hasTag(EXEC)) {
JCExpressionStatement stat = (JCExpressionStatement)tree;
if (stat.expr.hasTag(APPLY)) {
JCMethodInvocation apply = (JCMethodInvocation)stat.expr;
Name methName = TreeInfo.name(apply.meth);
if (methName == names._this ||
methName == names._super) {
return methName;
}
}
}
}
return names.empty;
}
public static boolean isMultiCatch(JCCatch catchClause) {
return catchClause.param.vartype.hasTag(TYPEUNION);
}
@ -170,18 +152,6 @@ public class TreeInfo {
return null;
}
/** Is this a call to this or super?
*/
public static boolean isSelfCall(JCTree tree) {
Name name = calledMethodName(tree);
if (name != null) {
Names names = name.table.names;
return name==names._this || name==names._super;
} else {
return false;
}
}
/** Is this tree a 'this' identifier?
*/
public static boolean isThisQualifier(JCTree tree) {
@ -238,32 +208,103 @@ public class TreeInfo {
.collect(List.collector());
}
/** Is this a constructor whose first (non-synthetic) statement is not
* of the form this(...)?
*/
public static boolean isInitialConstructor(JCTree tree) {
JCMethodInvocation app = firstConstructorCall(tree);
if (app == null) return false;
Name meth = name(app.meth);
return meth == null || meth != meth.table.names._this;
/** Is the given method a constructor containing a super() or this() call?
*/
public static boolean hasAnyConstructorCall(JCMethodDecl tree) {
return hasConstructorCall(tree, null);
}
/** Return the first call in a constructor definition. */
public static JCMethodInvocation firstConstructorCall(JCTree tree) {
if (!tree.hasTag(METHODDEF)) return null;
JCMethodDecl md = (JCMethodDecl) tree;
Names names = md.name.table.names;
if (md.name != names.init) return null;
if (md.body == null) return null;
List<JCStatement> stats = md.body.stats;
// Synthetic initializations can appear before the super call.
while (stats.nonEmpty() && isSyntheticInit(stats.head))
stats = stats.tail;
if (stats.isEmpty()) return null;
if (!stats.head.hasTag(EXEC)) return null;
JCExpressionStatement exec = (JCExpressionStatement) stats.head;
if (!exec.expr.hasTag(APPLY)) return null;
return (JCMethodInvocation)exec.expr;
/** Is the given method a constructor containing a super() and/or this() call?
* The "target" is either names._this, names._super, or null for either/both.
*/
public static boolean hasConstructorCall(JCMethodDecl tree, Name target) {
JCMethodInvocation app = findConstructorCall(tree);
return app != null && (target == null || target == name(app.meth));
}
/** Find the first super() or init() call in the given constructor.
*/
public static JCMethodInvocation findConstructorCall(JCMethodDecl md) {
if (!TreeInfo.isConstructor(md) || md.body == null)
return null;
return new ConstructorCallFinder(md.name.table.names).find(md).head;
}
/** Finds all calls to this() and/or super() in a given constructor.
* We can't assume they will be "top level" statements, because
* some synthetic calls to super() are added inside { } blocks.
* So we must recurse through the method's entire syntax tree.
*/
private static class ConstructorCallFinder extends TreeScanner {
final ListBuffer<JCMethodInvocation> calls = new ListBuffer<>();
final Names names;
ConstructorCallFinder(Names names) {
this.names = names;
}
List<JCMethodInvocation> find(JCMethodDecl meth) {
scan(meth);
return calls.toList();
}
@Override
public void visitApply(JCMethodInvocation invoke) {
Name name = TreeInfo.name(invoke.meth);
if ((name == names._this || name == names._super))
calls.append(invoke);
super.visitApply(invoke);
}
@Override
public void visitClassDef(JCClassDecl tree) {
// don't descend any further
}
@Override
public void visitLambda(JCLambda tree) {
// don't descend any further
}
}
/** Finds super() invocations and translates them using the given mapping.
*/
public static void mapSuperCalls(JCBlock block, Function<? super JCExpressionStatement, ? extends JCStatement> mapper) {
block.stats = block.stats.map(new TreeInfo.SuperCallTranslator(mapper)::translate);
}
/** Finds all super() invocations and translates them somehow.
*/
private static class SuperCallTranslator extends TreeTranslator {
final Function<? super JCExpressionStatement, ? extends JCStatement> translator;
/** Constructor.
*
* @param translator translates super() invocations, returning replacement statement or null for no change
*/
SuperCallTranslator(Function<? super JCExpressionStatement, ? extends JCStatement> translator) {
this.translator = translator;
}
// Because it returns void, anywhere super() can legally appear must be a location where a JCStatement
// could also appear, so it's OK that we are replacing a JCExpressionStatement with a JCStatement here.
@Override
public void visitExec(JCExpressionStatement stat) {
if (!TreeInfo.isSuperCall(stat) || (result = this.translator.apply(stat)) == null)
super.visitExec(stat);
}
@Override
public void visitClassDef(JCClassDecl tree) {
// don't descend any further
}
@Override
public void visitLambda(JCLambda tree) {
// don't descend any further
}
}
/** Return true if a tree represents a diamond new expr. */

View File

@ -56,6 +56,7 @@ import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
@ -428,7 +429,9 @@ class ExpressionToTypeInfo {
MethodInvocationTree superCall =
clazz.getMembers()
.stream()
.map(TreeInfo::firstConstructorCall)
.filter(JCMethodDecl.class::isInstance)
.map(JCMethodDecl.class::cast)
.map(TreeInfo::findConstructorCall)
.findAny()
.get();
TreePath superCallPath

View File

@ -1,2 +1,2 @@
AnonymousInSuperCallNegTest.java:23:49: compiler.err.cant.ref.before.ctor.called: x
AnonymousInSuperCallNegTest.java:23:49: compiler.err.no.encl.instance.of.type.in.scope: AnonymousInSuperCallNegTest.JavacBug
1 error

View File

@ -0,0 +1,171 @@
/*
* @test /nodynamiccopyright/
* @bug 8194743
* @summary Permit additional statements before this/super in constructors
* @compile/fail/ref=SuperInitFails.out -XDrawDiagnostics SuperInitFails.java
* @enablePreview
*/
import java.util.concurrent.atomic.AtomicReference;
public class SuperInitFails extends AtomicReference<Object> implements Iterable<Object> {
private int x;
/// GOOD EXAMPLES
public SuperInitFails() { // this should be OK
}
public SuperInitFails(Object x) {
this.x = x.hashCode(); // this should be OK
}
public SuperInitFails(byte x) {
super(); // this should be OK
}
public SuperInitFails(char x) {
this((int)x); // this should be OK
}
/// FAIL EXAMPLES
{
this(1); // this should FAIL
}
{
super(); // this should FAIL
}
void normalMethod1() {
super(); // this should FAIL
}
void normalMethod2() {
this(); // this should FAIL
}
void normalMethod3() {
Runnable r = () -> super(); // this should FAIL
}
void normalMethod4() {
Runnable r = () -> this(); // this should FAIL
}
public SuperInitFails(short x) {
hashCode(); // this should FAIL
super();
}
public SuperInitFails(float x) {
this.hashCode(); // this should FAIL
super();
}
public SuperInitFails(int x) {
super.hashCode(); // this should FAIL
super();
}
public SuperInitFails(long x) {
SuperInitFails.this.hashCode(); // this should FAIL
super();
}
public SuperInitFails(double x) {
SuperInitFails.super.hashCode(); // this should FAIL
super();
}
public SuperInitFails(byte[] x) {
{
super(); // this should FAIL
}
}
public SuperInitFails(char[] x) {
if (x.length == 0)
return; // this should FAIL
super();
}
public SuperInitFails(short[] x) {
this.x = x.length; // this should FAIL
super();
}
public SuperInitFails(float[] x) {
System.identityHashCode(this); // this should FAIL
super();
}
public SuperInitFails(int[] x) {
this(this); // this should FAIL
}
public SuperInitFails(long[] x) {
this(Object.this); // this should FAIL
}
public SuperInitFails(double[] x) {
Iterable.super.spliterator(); // this should FAIL
super();
}
public SuperInitFails(byte[][] x) {
super(new Object() {
{
super(); // this should FAIL
}
});
}
public SuperInitFails(char[][] x) {
new Inner1(); // this should FAIL
super();
}
class Inner1 {
}
record Record1(int value) {
Record1(float x) { // this should FAIL
}
}
record Record2(int value) {
Record2(float x) { // this should FAIL
super();
}
}
@Override
public java.util.Iterator<Object> iterator() {
return null;
}
public SuperInitFails(short[][] x) {
class Foo {
Foo() {
SuperInitFails.this.hashCode();
}
};
new Foo(); // this should FAIL
super();
}
public SuperInitFails(float[][] x) {
Runnable r = () -> {
super(); // this should FAIL
};
}
public SuperInitFails(int[][] z) {
super((Runnable)() -> x); // this should FAIL
}
public SuperInitFails(long[][] z) {
super(new Inner1()); // this should FAIL
}
}

View File

@ -0,0 +1,29 @@
SuperInitFails.java:57:9: compiler.err.cant.ref.before.ctor.called: hashCode()
SuperInitFails.java:62:9: compiler.err.cant.ref.before.ctor.called: this
SuperInitFails.java:67:9: compiler.err.cant.ref.before.ctor.called: super
SuperInitFails.java:72:23: compiler.err.cant.ref.before.ctor.called: this
SuperInitFails.java:77:23: compiler.err.cant.ref.before.ctor.called: super
SuperInitFails.java:94:9: compiler.err.cant.ref.before.ctor.called: this
SuperInitFails.java:99:33: compiler.err.cant.ref.before.ctor.called: this
SuperInitFails.java:104:14: compiler.err.cant.ref.before.ctor.called: this
SuperInitFails.java:108:20: compiler.err.not.encl.class: java.lang.Object
SuperInitFails.java:112:17: compiler.err.cant.ref.before.ctor.called: super
SuperInitFails.java:119:22: compiler.err.call.must.only.appear.in.ctor
SuperInitFails.java:125:9: compiler.err.cant.ref.before.ctor.called: this
SuperInitFails.java:133:9: compiler.err.non.canonical.constructor.invoke.another.constructor: SuperInitFails.Record1
SuperInitFails.java:138:9: compiler.err.non.canonical.constructor.invoke.another.constructor: SuperInitFails.Record2
SuperInitFails.java:154:9: compiler.err.cant.ref.before.ctor.called: this
SuperInitFails.java:165:31: compiler.err.cant.ref.before.ctor.called: x
SuperInitFails.java:169:15: compiler.err.cant.ref.before.ctor.called: this
SuperInitFails.java:33:13: compiler.err.call.must.only.appear.in.ctor
SuperInitFails.java:37:14: compiler.err.call.must.only.appear.in.ctor
SuperInitFails.java:41:14: compiler.err.call.must.only.appear.in.ctor
SuperInitFails.java:45:13: compiler.err.call.must.only.appear.in.ctor
SuperInitFails.java:49:33: compiler.err.call.must.only.appear.in.ctor
SuperInitFails.java:53:32: compiler.err.call.must.only.appear.in.ctor
SuperInitFails.java:83:18: compiler.err.ctor.calls.not.allowed.here
SuperInitFails.java:89:13: compiler.err.return.before.superclass.initialized
SuperInitFails.java:160:18: compiler.err.ctor.calls.not.allowed.here
- compiler.note.preview.filename: SuperInitFails.java, DEFAULT
- compiler.note.preview.recompile
26 errors

View File

@ -0,0 +1,480 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8194743
* @summary Test valid placements of super()/this() in constructors
* @enablePreview
*/
import java.util.concurrent.atomic.AtomicReference;
public class SuperInitGood {
SuperInitGood(Object obj) {
}
SuperInitGood(int x) {
}
// Default constructor provided by compiler
static class Test0 {
}
// No explicit calls to this()/super()
static class Test1 {
Test1() {
}
Test1(int a) {
this.hashCode();
}
}
// Explicit calls to this()/super()
static class Test2<T> {
static int i;
Test2() {
this(0);
}
Test2(int i) {
Test2.i = i;
super();
}
Test2(T obj) {
this(java.util.Objects.hashCode(obj));
}
public T get() {
return null;
}
}
// Explicit this()/super() with stuff in front
static class Test3 {
int x;
final int y;
final int z;
Test3() {
new Object().hashCode();
new Object().hashCode();
super();
this.x = new Object().hashCode();
this.y = new Object().hashCode() % 17;
this.z = this.x + this.y;
}
}
// Reference within constructor to outer class that's also my superclass
class Test5 extends SuperInitGood {
Test5(Object obj) {
if (obj == null)
throw new IllegalArgumentException();
super(SuperInitGood.this); // NOT a 'this' reference
}
}
// Initialization blocks
class Test6 {
final long startTime;
final int x;
{
this.x = 12;
}
Test6() {
long now = System.nanoTime();
long then = now + 1000000L;
while (System.nanoTime() < then) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
super();
this.startTime = now;
}
}
// Mix up inner classes, proxies, and super() calls
// Copied mostly from UnverifiableInitForNestedLocalClassTest.java
public static void test7(final String arg) {
final String inlined = " inlined ";
class LocalClass {
String m() {
return "LocalClass " + arg + inlined;
}
class SubClass extends LocalClass {
@Override
String m() {
return "SubClass " + arg + inlined;
}
}
class SubSubClass extends SubClass {
@Override
String m() {
return "SubSubClass " + arg + inlined;
}
}
class AnotherLocal {
class AnotherSub extends LocalClass {
AnotherSub() {
}
AnotherSub(int x) {
this((char)x);
}
AnotherSub(char y) {
super();
}
@Override
String m() {
return "AnotherSub " + arg + inlined;
}
}
}
}
}
// Anonymous inner class
public static void test8() {
new Test2<Byte>(null) {
@Override
public Byte get() {
return (byte)-1;
}
};
}
// Qualified super() invocation
public static class Test9 extends Test5 {
public Test9(SuperInitGood implicit, Object obj) {
obj.hashCode();
implicit.super(obj);
}
}
// Copied from WhichImplicitThis6
public static class Test10 {
private int i;
public Test10(int i) {}
public class Sub extends Test10 {
public Sub() {
super(i); // i is not inherited, so it is the enclosing i
}
}
}
// Two constructors where only one invokes super()
public static class Test11 {
public Test11() {
}
public Test11(int x) {
super();
}
}
// Nested version of the previous test
public static class Test12 {
Test12() {
class Sub {
public Sub() {
}
public Sub(int j) {
super();
}
}
}
}
// Nested super()'s requiring initialization code appended
public static class Test13 extends SuperInitGood {
final int x = new Object().hashCode();
Test13() {
super(new Object() {
public void foo() {
class Bar {
final int y = new Object().hashCode();
Bar() {
super();
}
Bar(int ignored) {
}
}
}
});
}
}
// Initializer in initializer block
public static class Test14 {
final int x; // initialized in constructor
final int y; // initialized in initialization block
final int z = 13; // initialized with intializer value
public Test14() {
this(0);
}
public Test14(boolean z) {
this.x = z ? 1 : 0;
}
public Test14(int x) {
super();
this.x = x;
}
{
this.y = -1;
}
}
// Qualified super() invocation with superclass instance
public static class Test15 {
final String name;
public Test15(String name) {
this.name = name;
}
public class Test15b extends Test15 {
public Test15b(String name) {
super(name);
}
public String getName() {
return Test15.this.name;
}
}
}
public static class Test15c extends Test15.Test15b {
public Test15c(Test15 a, String name) {
a.super(name);
}
}
// Mixing up outer instances, proxies, and initializers
public static class Test16 {
final String x = String.valueOf(new Object().hashCode());
public void run() {
final String y = String.valueOf(new Object().hashCode());
class Sub {
final String z;
Sub(String z, int ignored) {
this(z, (float)ignored);
}
Sub(String z, float ignored) {
this.z = z;
}
Sub(String z, byte ignored) {
super();
this.z = z;
}
Sub(String z, char ignored) {
this(z, (int)ignored);
}
String x() {
return x;
}
String y() {
return y;
}
String z() {
return z;
}
}
final String z = String.valueOf(new Object().hashCode());
final Sub[] subs = new Sub[] {
new Sub(z, 1),
new Sub(z, -1),
new Sub(z, (float)0),
new Sub(z, (byte)0),
new Sub(z, (char)0)
};
for (int i = 0; i < subs.length; i++) {
//System.err.println("i = " + i);
final Sub sub = subs[i];
final String subx = sub.x();
final String suby = sub.y();
final String subz = sub.z();
if (!x.equals(subx))
throw new RuntimeException("x=" + x + " but sub[" + i + "].x()=" + subx);
if (!y.equals(suby))
throw new RuntimeException("y=" + y + " but sub[" + i + "].y()=" + suby);
if (!z.equals(subz))
throw new RuntimeException("z=" + z + " but sub[" + i + "].z()=" + subz);
}
}
}
// Records
public class Test17 {
record Rectangle(float length, float width) { }
record StringHolder(String string) {
StringHolder {
java.util.Objects.requireNonNull(string);
}
}
record ValueHolder(int value) {
ValueHolder(float x) {
if (Float.isNaN(x))
throw new IllegalArgumentException();
this((int)x);
}
}
}
// Exceptions thrown by initializer block
public static class Test18 extends AtomicReference<Object> {
{
if ((this.get().hashCode() % 3) == 0)
throw new MyException();
}
public Test18(Object obj) throws MyException {
super(obj);
}
public Test18(boolean fail) throws MyException {
Object obj;
for (obj = new Object(); true; obj = new Object()) {
if (((obj.hashCode() % 3) == 0) != fail)
continue;
break;
}
this(obj);
}
public static class MyException extends Exception {
}
}
// super()/this() within outer try block but inside inner class
public static class Test19 {
public Test19(int x) {
try {
new Test1(x) {
@Override
public int hashCode() {
return x ^ super.hashCode();
}
};
} catch (StackOverflowError e) {
// ignore
}
}
}
// local class declared before super(), but not used until after super()
public static class Test20 {
public Test20() {
class Foo {
Foo() {
Test20.this.hashCode();
}
}
super();
new Foo();
}
}
// local class inside super() parameter list
public static class Test21 extends AtomicReference<Object> {
private int x;
public Test21() {
super(switch ("foo".hashCode()) {
default -> {
class Nested {{ System.out.println(x); }} // class is NOT instantiated - OK
yield "bar";
}
});
}
}
public static void main(String[] args) {
new Test0();
new Test1();
new Test1(7);
new Test2<Byte>();
new Test2<>(args);
new Test3();
new SuperInitGood(3).new Test5(3);
new SuperInitGood(3).new Test6();
SuperInitGood.test7("foo");
SuperInitGood.test8();
new Test9(new SuperInitGood(5), "abc");
new Test10(7);
new Test11(9);
new Test12();
new Test13();
Test14 t14 = new Test14();
assert t14.x == 0 && t14.y == -1 && t14.z == 13;
t14 = new Test14(7);
assert t14.x == 7 && t14.y == -1 && t14.z == 13;
new Test15c(new Test15("foo"), "bar");
new Test16().run();
new Test17.StringHolder("foo");
try {
new Test17.StringHolder(null);
throw new Error();
} catch (NullPointerException e) {
// expected
}
try {
new Test18(true);
assert false : "expected exception";
} catch (Test18.MyException e) {
// expected
}
try {
new Test18(false);
} catch (Test18.MyException e) {
assert false : "unexpected exception: " + e;
}
new Test19(123);
new Test20();
new Test21();
}
}

View File

@ -141,7 +141,6 @@ compiler.warn.invalid.path # this warning is genera
compiler.err.invalid.path # this error is generated only in Windows systems
compiler.note.multiple.elements # needs user code
compiler.err.preview.feature.disabled.classfile # preview feature support: needs compilation against classfile
compiler.warn.preview.feature.use # preview feature support: not generated currently
compiler.warn.preview.feature.use.classfile # preview feature support: needs compilation against classfile
compiler.note.preview.plural.additional # preview feature support: diag test causes intermittent failures (see JDK-8201498)
compiler.misc.bad.intersection.target.for.functional.expr # currently not generated, should be removed?

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -21,11 +21,10 @@
* questions.
*/
// key: compiler.err.call.must.be.first.stmt.in.ctor
// key: compiler.err.call.must.only.appear.in.ctor
class CallMustBeFirst {
CallMustBeFirst() {
int i = 0;
class CallOnlyInConstructor {
void foo() {
super();
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// key: compiler.err.ctor.calls.not.allowed.here
class CallsNotAllowedHere {
public CallsNotAllowedHere() {
{
super();
}
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// key: compiler.misc.feature.super.init
// key: compiler.warn.preview.feature.use
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class FeatureStatementsBeforeSuper {
FeatureStatementsBeforeSuper() {
System.out.println();
super();
}
}

View File

@ -21,7 +21,7 @@
* questions.
*/
// key: compiler.err.first.statement.must.be.call.to.another.constructor
// key: compiler.err.non.canonical.constructor.invoke.another.constructor
record R(int x) {
public R(int x, int y) { this.x = x; }

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// key: compiler.err.redundant.superclass.init
class RedundantSuperclassInit {
RedundantSuperclassInit() {
super();
super();
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// key: compiler.err.return.before.superclass.initialized
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source ${jdk.version}
class ReturnBeforeSuperclassInit {
ReturnBeforeSuperclassInit(boolean maybe) {
if (maybe)
return;
super();
}
}

View File

@ -403,11 +403,11 @@ class RecordCompilationTests extends CompilationTestCase {
assertFail("compiler.err.invalid.canonical.constructor.in.record",
"record R(int x, int y) { public R(int y, int x) { this.x = this.y = 0; }}");
// first invocation should be one to the canonical
assertFail("compiler.err.first.statement.must.be.call.to.another.constructor",
// constructor is not canonical, so it must only invoke another constructor
assertFail("compiler.err.non.canonical.constructor.invoke.another.constructor",
"record R(int x, int y) { public R(int y, int x, int z) { this.x = this.y = 0; } }");
assertFail("compiler.err.first.statement.must.be.call.to.another.constructor",
assertFail("compiler.err.non.canonical.constructor.invoke.another.constructor",
"record R(int x, int y) { public R(int y, int x, int z) { super(); this.x = this.y = 0; } }");
assertOK("record R(int x, int y) { " +