8194743: Compiler implementation for Statements before super()
Reviewed-by: vromero, jwaters, mcimadamore
This commit is contained in:
parent
5e24aaf4f7
commit
12e983a72e
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
**************************************************************************/
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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 =
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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
|
||||
|
@ -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. */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
171
test/langtools/tools/javac/SuperInit/SuperInitFails.java
Normal file
171
test/langtools/tools/javac/SuperInit/SuperInitFails.java
Normal 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
|
||||
}
|
||||
}
|
29
test/langtools/tools/javac/SuperInit/SuperInitFails.out
Normal file
29
test/langtools/tools/javac/SuperInit/SuperInitFails.out
Normal 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
|
480
test/langtools/tools/javac/SuperInit/SuperInitGood.java
Normal file
480
test/langtools/tools/javac/SuperInit/SuperInitGood.java
Normal 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();
|
||||
}
|
||||
}
|
@ -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?
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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; }
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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) { " +
|
||||
|
Loading…
Reference in New Issue
Block a user