Skip to content

Commit 12e983a

Browse files
archiecobbsVicente Romero
authored and
Vicente Romero
committedNov 27, 2023
8194743: Compiler implementation for Statements before super()
Reviewed-by: vromero, jwaters, mcimadamore
1 parent 5e24aaf commit 12e983a

26 files changed

+1274
-313
lines changed
 

‎src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java

+1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ public boolean isPreview(Feature feature) {
211211
return switch (feature) {
212212
case STRING_TEMPLATES -> true;
213213
case UNNAMED_CLASSES -> true;
214+
case SUPER_INIT -> true;
214215
//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
215216
//When real preview features will be added, this method can be implemented to return 'true'
216217
//for those selected features, and 'false' for all the others.

‎src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java

+1
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ public enum Feature {
248248
UNNAMED_CLASSES(JDK21, Fragments.FeatureUnnamedClasses, DiagKind.PLURAL),
249249
WARN_ON_ILLEGAL_UTF8(MIN, JDK21),
250250
UNNAMED_VARIABLES(JDK22, Fragments.FeatureUnnamedVariables, DiagKind.PLURAL),
251+
SUPER_INIT(JDK22, Fragments.FeatureSuperInit, DiagKind.NORMAL),
251252
;
252253

253254
enum DiagKind {

‎src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java

+31-74
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,8 @@ public void visitClassDef(JCClassDecl tree) {
937937
Optional<ArgumentAttr.LocalCacheContext> localCacheContext =
938938
Optional.ofNullable(env.info.attributionMode.isSpeculative ?
939939
argumentAttr.withLocalCacheContext() : null);
940+
boolean ctorProloguePrev = env.info.ctorPrologue;
941+
env.info.ctorPrologue = false;
940942
try {
941943
// Local and anonymous classes have not been entered yet, so we need to
942944
// do it now.
@@ -959,19 +961,18 @@ public void visitClassDef(JCClassDecl tree) {
959961
// make sure class has been completed:
960962
c.complete();
961963

962-
// If this class appears as an anonymous class
963-
// in a superclass constructor call
964-
// disable implicit outer instance from being passed.
964+
// If this class appears as an anonymous class in a constructor
965+
// prologue, disable implicit outer instance from being passed.
965966
// (This would be an illegal access to "this before super").
966-
if (env.info.isSelfCall &&
967-
env.tree.hasTag(NEWCLASS)) {
967+
if (ctorProloguePrev && env.tree.hasTag(NEWCLASS)) {
968968
c.flags_field |= NOOUTERTHIS;
969969
}
970970
attribClass(tree.pos(), c);
971971
result = tree.type = c.type;
972972
}
973973
} finally {
974974
localCacheContext.ifPresent(LocalCacheContext::leave);
975+
env.info.ctorPrologue = ctorProloguePrev;
975976
}
976977
}
977978

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

982983
Lint lint = env.info.lint.augment(m);
983984
Lint prevLint = chk.setLint(lint);
985+
boolean ctorProloguePrev = env.info.ctorPrologue;
986+
env.info.ctorPrologue = false;
984987
MethodSymbol prevMethod = chk.setMethod(m);
985988
try {
986989
deferredLintHandler.flush(tree.pos());
@@ -1044,6 +1047,9 @@ public void visitMethodDef(JCMethodDecl tree) {
10441047
chk.validate(tree.recvparam, newEnv);
10451048
}
10461049

1050+
// Is this method a constructor?
1051+
boolean isConstructor = TreeInfo.isConstructor(tree);
1052+
10471053
if (env.enclClass.sym.isRecord() && tree.sym.owner.kind == TYP) {
10481054
// lets find if this method is an accessor
10491055
Optional<? extends RecordComponent> recordComponent = env.enclClass.sym.getRecordComponents().stream()
@@ -1071,14 +1077,11 @@ public void visitMethodDef(JCMethodDecl tree) {
10711077
}
10721078
}
10731079

1074-
if (tree.name == names.init) {
1080+
if (isConstructor) {
10751081
// if this a constructor other than the canonical one
10761082
if ((tree.sym.flags_field & RECORD) == 0) {
1077-
JCMethodInvocation app = TreeInfo.firstConstructorCall(tree);
1078-
if (app == null ||
1079-
TreeInfo.name(app.meth) != names._this ||
1080-
!checkFirstConstructorStat(app, tree, false)) {
1081-
log.error(tree, Errors.FirstStatementMustBeCallToAnotherConstructor(env.enclClass.sym));
1083+
if (!TreeInfo.hasConstructorCall(tree, names._this)) {
1084+
log.error(tree, Errors.NonCanonicalConstructorInvokeAnotherConstructor(env.enclClass.sym));
10821085
}
10831086
} else {
10841087
// but if it is the canonical:
@@ -1104,11 +1107,7 @@ public void visitMethodDef(JCMethodDecl tree) {
11041107
);
11051108
}
11061109

1107-
JCMethodInvocation app = TreeInfo.firstConstructorCall(tree);
1108-
if (app != null &&
1109-
(TreeInfo.name(app.meth) == names._this ||
1110-
TreeInfo.name(app.meth) == names._super) &&
1111-
checkFirstConstructorStat(app, tree, false)) {
1110+
if (TreeInfo.hasAnyConstructorCall(tree)) {
11121111
log.error(tree, Errors.InvalidCanonicalConstructorInRecord(
11131112
Fragments.Canonical, env.enclClass.sym.name,
11141113
Fragments.CanonicalMustNotContainExplicitConstructorInvocation));
@@ -1186,16 +1185,14 @@ public void visitMethodDef(JCMethodDecl tree) {
11861185
// Add an implicit super() call unless an explicit call to
11871186
// super(...) or this(...) is given
11881187
// or we are compiling class java.lang.Object.
1189-
if (tree.name == names.init && owner.type != syms.objectType) {
1190-
JCBlock body = tree.body;
1191-
if (body.stats.isEmpty() ||
1192-
TreeInfo.getConstructorInvocationName(body.stats, names) == names.empty) {
1193-
JCStatement supCall = make.at(body.pos).Exec(make.Apply(List.nil(),
1188+
if (isConstructor && owner.type != syms.objectType) {
1189+
if (!TreeInfo.hasAnyConstructorCall(tree)) {
1190+
JCStatement supCall = make.at(tree.body.pos).Exec(make.Apply(List.nil(),
11941191
make.Ident(names._super), make.Idents(List.nil())));
1195-
body.stats = body.stats.prepend(supCall);
1192+
tree.body.stats = tree.body.stats.prepend(supCall);
11961193
} else if ((env.enclClass.sym.flags() & ENUM) != 0 &&
11971194
(tree.mods.flags & GENERATEDCONSTR) == 0 &&
1198-
TreeInfo.isSuperCall(body.stats.head)) {
1195+
TreeInfo.hasConstructorCall(tree, names._super)) {
11991196
// enum constructors are not allowed to call super
12001197
// directly, so make sure there aren't any super calls
12011198
// in enum constructors, except in the compiler
@@ -1225,6 +1222,9 @@ public void visitMethodDef(JCMethodDecl tree) {
12251222
annotate.queueScanTreeAndTypeAnnotate(tree.body, localEnv, m, null);
12261223
annotate.flush();
12271224

1225+
// Start of constructor prologue
1226+
localEnv.info.ctorPrologue = isConstructor;
1227+
12281228
// Attribute method body.
12291229
attribStat(tree.body, localEnv);
12301230
}
@@ -1234,6 +1234,7 @@ public void visitMethodDef(JCMethodDecl tree) {
12341234
} finally {
12351235
chk.setLint(prevLint);
12361236
chk.setMethod(prevMethod);
1237+
env.info.ctorPrologue = ctorProloguePrev;
12371238
}
12381239
}
12391240

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

25192520
ListBuffer<Type> argtypesBuf = new ListBuffer<>();
25202521
if (isConstructorCall) {
2521-
// We are seeing a ...this(...) or ...super(...) call.
2522-
// Check that this is the first statement in a constructor.
2523-
checkFirstConstructorStat(tree, env.enclMethod, true);
2524-
2525-
// Record the fact
2526-
// that this is a constructor call (using isSelfCall).
2527-
localEnv.info.isSelfCall = true;
25282522

25292523
// Attribute arguments, yielding list of argument types.
2530-
localEnv.info.constructorArgs = true;
25312524
KindSelector kind = attribArgs(KindSelector.MTH, tree.args, localEnv, argtypesBuf);
2532-
localEnv.info.constructorArgs = false;
25332525
argtypes = argtypesBuf.toList();
25342526
typeargtypes = attribTypes(tree.typeargs, localEnv);
25352527

2528+
// Done with this()/super() parameters. End of constructor prologue.
2529+
env.info.ctorPrologue = false;
2530+
25362531
// Variable `site' points to the class in which the called
25372532
// constructor is defined.
25382533
Type site = env.enclClass.sym.type;
@@ -2661,26 +2656,6 @@ Type adjustMethodReturnType(Symbol msym, Type qualifierType, Name methodName, Li
26612656
}
26622657
}
26632658

2664-
/** Check that given application node appears as first statement
2665-
* in a constructor call.
2666-
* @param tree The application node
2667-
* @param enclMethod The enclosing method of the application.
2668-
* @param error Should an error be issued?
2669-
*/
2670-
boolean checkFirstConstructorStat(JCMethodInvocation tree, JCMethodDecl enclMethod, boolean error) {
2671-
if (enclMethod != null && enclMethod.name == names.init) {
2672-
JCBlock body = enclMethod.body;
2673-
if (body.stats.head.hasTag(EXEC) &&
2674-
((JCExpressionStatement) body.stats.head).expr == tree)
2675-
return true;
2676-
}
2677-
if (error) {
2678-
log.error(tree.pos(),
2679-
Errors.CallMustBeFirstStmtInCtor(TreeInfo.name(tree.meth)));
2680-
}
2681-
return false;
2682-
}
2683-
26842659
/** Obtain a method type with given argument types.
26852660
*/
26862661
Type newMethodTemplate(Type restype, List<Type> argtypes, List<Type> typeargtypes) {
@@ -4353,16 +4328,6 @@ public void visitIdent(JCIdent tree) {
43534328
checkAssignable(tree.pos(), v, null, env);
43544329
}
43554330

4356-
// In a constructor body,
4357-
// if symbol is a field or instance method, check that it is
4358-
// not accessed before the supertype constructor is called.
4359-
if (symEnv.info.isSelfCall &&
4360-
sym.kind.matches(KindSelector.VAL_MTH) &&
4361-
sym.owner.kind == TYP &&
4362-
(sym.flags() & STATIC) == 0) {
4363-
chk.earlyRefError(tree.pos(), sym.kind == VAR ?
4364-
sym : thisSym(tree.pos(), env));
4365-
}
43664331
Env<AttrContext> env1 = env;
43674332
if (sym.kind != ERR && sym.kind != TYP &&
43684333
sym.owner != null && sym.owner != env1.enclClass.sym) {
@@ -4474,18 +4439,7 @@ public void visitSelect(JCFieldAccess tree) {
44744439
}
44754440

44764441
if (isType(sitesym)) {
4477-
if (sym.name == names._this || sym.name == names._super) {
4478-
// If `C' is the currently compiled class, check that
4479-
// `C.this' does not appear in an explicit call to a constructor
4480-
// also make sure that `super` is not used in constructor invocations
4481-
if (env.info.isSelfCall &&
4482-
((sym.name == names._this &&
4483-
site.tsym == env.enclClass.sym) ||
4484-
sym.name == names._super && env.info.constructorArgs &&
4485-
(sitesym.isInterface() || site.tsym == env.enclClass.sym))) {
4486-
chk.earlyRefError(tree.pos(), sym);
4487-
}
4488-
} else {
4442+
if (sym.name != names._this && sym.name != names._super) {
44894443
// Check if type-qualified fields or methods are static (JLS)
44904444
if ((sym.flags() & STATIC) == 0 &&
44914445
sym.name != names._super &&
@@ -5674,6 +5628,9 @@ private void attribClassBody(Env<AttrContext> env, ClassSymbol c) {
56745628
}
56755629
}
56765630

5631+
// Check for proper placement of super()/this() calls.
5632+
chk.checkSuperInitCalls(tree);
5633+
56775634
// Check for cycles among non-initial constructors.
56785635
chk.checkCyclicConstructors(tree);
56795636

‎src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java

+3-8
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,9 @@ public class AttrContext {
4949
*/
5050
int staticLevel = 0;
5151

52-
/** Is this an environment for a this(...) or super(...) call?
52+
/** Are we in the 'prologue' part of a constructor, prior to an explicit this()/super()?
5353
*/
54-
boolean isSelfCall = false;
55-
56-
/** are we analyzing the arguments for a constructor invocation?
57-
*/
58-
boolean constructorArgs = false;
54+
boolean ctorPrologue = false;
5955

6056
/** Are we evaluating the selector of a `super' or type name?
6157
*/
@@ -136,8 +132,7 @@ AttrContext dup(WriteableScope scope) {
136132
AttrContext info = new AttrContext();
137133
info.scope = scope;
138134
info.staticLevel = staticLevel;
139-
info.isSelfCall = isSelfCall;
140-
info.constructorArgs = constructorArgs;
135+
info.ctorPrologue = ctorPrologue;
141136
info.selectSuper = selectSuper;
142137
info.pendingResolutionPhase = pendingResolutionPhase;
143138
info.lint = lint;

1 commit comments

Comments
 (1)

openjdk-notifier[bot] commented on Nov 27, 2023

@openjdk-notifier[bot]
Please sign in to comment.