diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java index cdbd57047ff..ad859bb4507 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java @@ -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. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java index 017400adcfc..0290f8bc752 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java @@ -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 { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 33a751aac7e..b69c2156e1d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -937,6 +937,8 @@ public class Attr extends JCTree.Visitor { Optional 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 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 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 argtypes, List 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 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); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java index 2a96b11316c..b8afce009a5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java @@ -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; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index d05d5e2f510..ead1ecf3c63 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -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 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 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 **************************************************************************/ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java index e09ed62d62b..90d48243841 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java @@ -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; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index 4a23bda0ea8..d07b3a41ccb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -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 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 handler) { + if (classDef == initScanClass) // avoid infinite loops + return; + JCClassDecl initScanClassPrev = initScanClass; + initScanClass = classDef; + try { + for (List 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 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 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 l = tree.defs; l.nonEmpty(); l = l.tail) { @@ -1362,40 +1382,10 @@ public class Flow { try { // process all the static initializers - for (List 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 l = tree.defs; l.nonEmpty(); l = l.tail) { - if (TreeInfo.isInitialConstructor(l.head)) { - List 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 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 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 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 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 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 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) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index c51b1cb2a7e..39807aeaa5e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -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 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 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 added = List.nil(); + // Create initializers for this$n and proxies + ListBuffer added = new ListBuffer<>(); if (fvs.nonEmpty()) { List addedargtypes = List.nil(); for (List 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 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 stats = translate(tree.body.stats.tail); - tree.body.stats = stats.prepend(selfCall).prependList(added); outerThisStack = prevOuterThisStack; } else { Map prevLambdaTranslationMap = diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java index ab84d372bd7..3980f03713d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java @@ -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 argtypes, + List 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; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java index 313ee5c16d1..ef74ccc9877 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java @@ -570,7 +570,6 @@ public class TypeEnter implements Completer { Env localEnv = outer.dup(tree, outer.info.dup(baseScope)); localEnv.baseClause = true; localEnv.outer = outer; - localEnv.info.isSelfCall = false; return localEnv; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index caa5249bb83..4e5c4e49436 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -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 initCode, List 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 stats = md.body.stats; - ListBuffer 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()); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index b485370da27..57e31441632 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -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 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index 13932560434..ee4477135c6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -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 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 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 calls = new ListBuffer<>(); + final Names names; + + ConstructorCallFinder(Names names) { + this.names = names; + } + + List 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 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 translator; + + /** Constructor. + * + * @param translator translates super() invocations, returning replacement statement or null for no change + */ + SuperCallTranslator(Function 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. */ diff --git a/src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java b/src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java index 414d58b589d..e9eaccea321 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java @@ -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 diff --git a/test/langtools/tools/javac/AnonymousClass/AnonymousInSuperCallNegTest.out b/test/langtools/tools/javac/AnonymousClass/AnonymousInSuperCallNegTest.out index c04b45bce1e..140521689a0 100644 --- a/test/langtools/tools/javac/AnonymousClass/AnonymousInSuperCallNegTest.out +++ b/test/langtools/tools/javac/AnonymousClass/AnonymousInSuperCallNegTest.out @@ -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 diff --git a/test/langtools/tools/javac/SuperInit/SuperInitFails.java b/test/langtools/tools/javac/SuperInit/SuperInitFails.java new file mode 100644 index 00000000000..cd0fc3fd903 --- /dev/null +++ b/test/langtools/tools/javac/SuperInit/SuperInitFails.java @@ -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 implements Iterable { + + 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 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 + } +} diff --git a/test/langtools/tools/javac/SuperInit/SuperInitFails.out b/test/langtools/tools/javac/SuperInit/SuperInitFails.out new file mode 100644 index 00000000000..60bbd83dcc0 --- /dev/null +++ b/test/langtools/tools/javac/SuperInit/SuperInitFails.out @@ -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 diff --git a/test/langtools/tools/javac/SuperInit/SuperInitGood.java b/test/langtools/tools/javac/SuperInit/SuperInitGood.java new file mode 100644 index 00000000000..cbcbee8fef0 --- /dev/null +++ b/test/langtools/tools/javac/SuperInit/SuperInitGood.java @@ -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 { + 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(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 { + + { + 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 { + 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(); + 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(); + } +} diff --git a/test/langtools/tools/javac/diags/examples.not-yet.txt b/test/langtools/tools/javac/diags/examples.not-yet.txt index 06b7cea1afe..9d7d79eca16 100644 --- a/test/langtools/tools/javac/diags/examples.not-yet.txt +++ b/test/langtools/tools/javac/diags/examples.not-yet.txt @@ -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? diff --git a/test/langtools/tools/javac/diags/examples/CallMustBeFirst.java b/test/langtools/tools/javac/diags/examples/CallOnlyInConstructor.java similarity index 84% rename from test/langtools/tools/javac/diags/examples/CallMustBeFirst.java rename to test/langtools/tools/javac/diags/examples/CallOnlyInConstructor.java index 3f098706245..f1018be46ce 100644 --- a/test/langtools/tools/javac/diags/examples/CallMustBeFirst.java +++ b/test/langtools/tools/javac/diags/examples/CallOnlyInConstructor.java @@ -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(); } } diff --git a/test/langtools/tools/javac/diags/examples/CallsNotAllowedHere.java b/test/langtools/tools/javac/diags/examples/CallsNotAllowedHere.java new file mode 100644 index 00000000000..2f9239f09c0 --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/CallsNotAllowedHere.java @@ -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(); + } + } +} diff --git a/test/langtools/tools/javac/diags/examples/FeatureStatementsBeforeSuper.java b/test/langtools/tools/javac/diags/examples/FeatureStatementsBeforeSuper.java new file mode 100644 index 00000000000..f0e44836850 --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/FeatureStatementsBeforeSuper.java @@ -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(); + } +} diff --git a/test/langtools/tools/javac/diags/examples/FirstInvocationMustBeAnotherConstructor.java b/test/langtools/tools/javac/diags/examples/FirstInvocationMustBeAnotherConstructor.java index e2478244cc9..da7b69ba16c 100644 --- a/test/langtools/tools/javac/diags/examples/FirstInvocationMustBeAnotherConstructor.java +++ b/test/langtools/tools/javac/diags/examples/FirstInvocationMustBeAnotherConstructor.java @@ -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; } diff --git a/test/langtools/tools/javac/diags/examples/RedundantSuperclassInit.java b/test/langtools/tools/javac/diags/examples/RedundantSuperclassInit.java new file mode 100644 index 00000000000..82c5faf1d04 --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/RedundantSuperclassInit.java @@ -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(); + } +} diff --git a/test/langtools/tools/javac/diags/examples/ReturnBeforeSuperclassInit.java b/test/langtools/tools/javac/diags/examples/ReturnBeforeSuperclassInit.java new file mode 100644 index 00000000000..dc883b59b8f --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/ReturnBeforeSuperclassInit.java @@ -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(); + } +} diff --git a/test/langtools/tools/javac/records/RecordCompilationTests.java b/test/langtools/tools/javac/records/RecordCompilationTests.java index 5f204598324..5a51496ff19 100644 --- a/test/langtools/tools/javac/records/RecordCompilationTests.java +++ b/test/langtools/tools/javac/records/RecordCompilationTests.java @@ -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) { " +