Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
9fe8e82
8194743: Permit additional statements before this/super in constructors
archiecobbs Feb 1, 2023
51a27c4
Merge branch 'master' into SuperInit
archiecobbs Feb 9, 2023
4aae83b
Merge branch 'master' into SuperInit
archiecobbs Mar 20, 2023
61c0eac
Use for() loop instead of stream for efficiency.
archiecobbs Mar 20, 2023
805b781
Merge branch 'master' into SuperInit
archiecobbs Apr 17, 2023
50d17ca
Merge branch 'master' into SuperInit
archiecobbs Apr 18, 2023
95140ee
Merge branch 'master' into SuperInit
archiecobbs Apr 19, 2023
53d19a0
Merge branch 'master' into SuperInit
archiecobbs Apr 20, 2023
dcf6fa6
Merge branch 'master' into SuperInit
archiecobbs Apr 24, 2023
9deaa03
Merge branch 'master' into SuperInit
archiecobbs Apr 25, 2023
ceb41f8
Fix Javadoc comment.
archiecobbs Apr 25, 2023
a1f1efc
Merge branch 'master' into SuperInit
archiecobbs Apr 25, 2023
7c874eb
Fix typo in comment.
archiecobbs Apr 25, 2023
0eac8b6
Use for() loop instead of stream for efficiency.
archiecobbs Apr 25, 2023
0b8a8c7
Fix typo in comment.
archiecobbs Apr 26, 2023
0e638da
Small refactoring to avoid redundant test.
archiecobbs Apr 26, 2023
3c9322b
Make "statements before super()" support a preview feature.
archiecobbs May 16, 2023
73bb327
Use @enablePreview in tests in preference to explicit command line fl…
archiecobbs May 17, 2023
80ba6be
Merge branch 'master' into SuperInit
archiecobbs May 23, 2023
eb1eb5a
Add unit tests with local class decl's prior to super().
archiecobbs May 23, 2023
9a2cb45
Update unit test after merged-in commit eaa80ad08.
archiecobbs May 23, 2023
543a596
Rename unit test to be consistent with other feature exampless.
archiecobbs May 23, 2023
a352a58
Merge branch 'master' into SuperInit
archiecobbs May 26, 2023
276e449
Fix mistake in previous merge commit 80ba6be4.
archiecobbs May 26, 2023
a5f8cc5
Merge branch 'master' into SuperInit
archiecobbs Jun 13, 2023
c6cf80f
Use TreeInfo.isConstructor() for detecting constructors.
archiecobbs Jul 7, 2023
b0b13b2
Add unit test verifying super() can't appear inside a lambda.
archiecobbs Jul 7, 2023
599d761
Create and cache a single instance of the oft-used SuperThisChecker.
archiecobbs Jul 7, 2023
e2f8813
Merge branch 'master' into SuperInit
archiecobbs Jul 8, 2023
5cced39
Eliminate "isSelfCall" flag, which is now made obsolete by "ctorProlo…
archiecobbs Sep 7, 2023
4ff4c5c
Merge branch 'master' into SuperInit
archiecobbs Sep 7, 2023
a5510b7
Change the error message to match the applicable spec rule.
archiecobbs Sep 16, 2023
599cf0e
Remove obsolete flag "constructorArgs".
archiecobbs Sep 22, 2023
150d794
Change variable name "statik" -> "isStatic" per review recommendation.
archiecobbs Sep 25, 2023
3e75e19
Have RefBeforeCtorCalledError extend StaticError and customize debug …
archiecobbs Sep 25, 2023
355edfb
Address review comments relating to error messages.
archiecobbs Sep 25, 2023
2356ac6
After further review, revert 599d761 to avoid mutable state lying aro…
archiecobbs Sep 25, 2023
738c10a
Reword new errors to use the JLS term for "explicit constructor invoc…
archiecobbs Sep 28, 2023
99a277e
Merge branch 'master' into SuperInit
archiecobbs Oct 20, 2023
bbdb814
Merge branch 'master' into SuperInit
archiecobbs Nov 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ public boolean isPreview(Feature feature) {
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ public enum Feature {
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 {
Expand Down
105 changes: 31 additions & 74 deletions src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,8 @@ public void visitClassDef(JCClassDecl tree) {
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.
Expand All @@ -959,19 +961,18 @@ public void visitClassDef(JCClassDecl tree) {
// 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);
result = tree.type = c.type;
}
} finally {
localCacheContext.ifPresent(LocalCacheContext::leave);
env.info.ctorPrologue = ctorProloguePrev;
}
}

Expand All @@ -981,6 +982,8 @@ public void visitMethodDef(JCMethodDecl tree) {

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());
Expand Down Expand Up @@ -1044,6 +1047,9 @@ public void visitMethodDef(JCMethodDecl tree) {
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()
Expand Down Expand Up @@ -1071,14 +1077,11 @@ public void visitMethodDef(JCMethodDecl tree) {
}
}

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:
Expand All @@ -1104,11 +1107,7 @@ public void visitMethodDef(JCMethodDecl tree) {
);
}

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));
Expand Down Expand Up @@ -1186,16 +1185,14 @@ public void visitMethodDef(JCMethodDecl tree) {
// 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
Expand Down Expand Up @@ -1225,6 +1222,9 @@ public void visitMethodDef(JCMethodDecl tree) {
annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m, null);
annotate.flush();

// Start of constructor prologue
localEnv.info.ctorPrologue = isConstructor;

// Attribute method body.
attribStat(tree.body, localEnv);
}
Expand All @@ -1234,6 +1234,7 @@ public void visitMethodDef(JCMethodDecl tree) {
} finally {
chk.setLint(prevLint);
chk.setMethod(prevMethod);
env.info.ctorPrologue = ctorProloguePrev;
}
}

Expand Down Expand Up @@ -2518,21 +2519,15 @@ public void visitApply(JCMethodInvocation tree) {

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;
Expand Down Expand Up @@ -2661,26 +2656,6 @@ Type adjustMethodReturnType(Symbol msym, Type qualifierType, Name methodName, Li
}
}

/** 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) {
Expand Down Expand Up @@ -4353,16 +4328,6 @@ public void visitIdent(JCIdent tree) {
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) {
Expand Down Expand Up @@ -4474,18 +4439,7 @@ public void visitSelect(JCFieldAccess tree) {
}

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 &&
Expand Down Expand Up @@ -5669,6 +5623,9 @@ private void attribClassBody(Env<AttrContext> env, ClassSymbol c) {
}
}

// Check for proper placement of super()/this() calls.
chk.checkSuperInitCalls(tree);

// Check for cycles among non-initial constructors.
chk.checkCyclicConstructors(tree);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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?
*/
Expand Down Expand Up @@ -136,8 +132,7 @@ AttrContext dup(WriteableScope scope) {
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;
Expand Down
Loading