Skip to content

Commit 3620108

Browse files
author
Vicente Romero
committed
8359370: [lworld] allow instance fields of identity classes to be readable in the prologue phase
Reviewed-by: mcimadamore
1 parent 4b93af7 commit 3620108

27 files changed

+442
-280
lines changed

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

Lines changed: 253 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ public class Attr extends JCTree.Visitor {
124124
final ArgumentAttr argumentAttr;
125125
final MatchBindingsComputer matchBindingsComputer;
126126
final AttrRecover attrRecover;
127+
final LocalProxyVarsGen localProxyVarsGen;
127128

128129
public static Attr instance(Context context) {
129130
Attr instance = context.get(attrKey);
@@ -163,6 +164,7 @@ protected Attr(Context context) {
163164
argumentAttr = ArgumentAttr.instance(context);
164165
matchBindingsComputer = MatchBindingsComputer.instance(context);
165166
attrRecover = AttrRecover.instance(context);
167+
localProxyVarsGen = LocalProxyVarsGen.instance(context);
166168

167169
Options options = Options.instance(context);
168170

@@ -301,9 +303,7 @@ boolean isAssignableAsBlankFinal(VarSymbol v, Env<AttrContext> env) {
301303
void checkAssignable(DiagnosticPosition pos, VarSymbol v, JCTree base, Env<AttrContext> env) {
302304
if (v.name == names._this) {
303305
log.error(pos, Errors.CantAssignValToThis);
304-
return;
305-
}
306-
if ((v.flags() & FINAL) != 0 &&
306+
} else if ((v.flags() & FINAL) != 0 &&
307307
((v.flags() & HASINIT) != 0
308308
||
309309
!((base == null ||
@@ -314,23 +314,6 @@ void checkAssignable(DiagnosticPosition pos, VarSymbol v, JCTree base, Env<AttrC
314314
} else {
315315
log.error(pos, Errors.CantAssignValToVar(Flags.toSource(v.flags() & (STATIC | FINAL)), v));
316316
}
317-
return;
318-
}
319-
320-
// Check instance field assignments that appear in constructor prologues
321-
if (rs.isEarlyReference(env, base, v)) {
322-
323-
// Field may not be inherited from a superclass
324-
if (v.owner != env.enclClass.sym) {
325-
log.error(pos, Errors.CantRefBeforeCtorCalled(v));
326-
return;
327-
}
328-
329-
// Field may not have an initializer
330-
if ((v.flags() & HASINIT) != 0) {
331-
log.error(pos, Errors.CantAssignInitializedBeforeCtorCalled(v));
332-
return;
333-
}
334317
}
335318
}
336319

@@ -1252,6 +1235,25 @@ public void visitMethodDef(JCMethodDecl tree) {
12521235

12531236
// Attribute method body.
12541237
attribStat(tree.body, localEnv);
1238+
if (isConstructor) {
1239+
ListBuffer<JCTree> prologueCode = new ListBuffer<>();
1240+
for (JCTree stat : tree.body.stats) {
1241+
prologueCode.add(stat);
1242+
/* gather all the stats in the body until a `super` or `this` constructor invocation is found,
1243+
* including the constructor invocation, that way we don't need to worry in the visitor below if
1244+
* if we are dealing or not with prologue code
1245+
*/
1246+
if (stat instanceof JCExpressionStatement expStmt &&
1247+
expStmt.expr instanceof JCMethodInvocation mi &&
1248+
TreeInfo.isConstructorCall(mi)) {
1249+
break;
1250+
}
1251+
}
1252+
if (!prologueCode.isEmpty()) {
1253+
CtorPrologueVisitor ctorPrologueVisitor = new CtorPrologueVisitor(localEnv);
1254+
ctorPrologueVisitor.scan(prologueCode.toList());
1255+
}
1256+
}
12551257
}
12561258

12571259
localEnv.info.scope.leave();
@@ -1263,6 +1265,232 @@ public void visitMethodDef(JCMethodDecl tree) {
12631265
}
12641266
}
12651267

1268+
class CtorPrologueVisitor extends TreeScanner {
1269+
Env<AttrContext> localEnv;
1270+
CtorPrologueVisitor(Env<AttrContext> localEnv) {
1271+
this.localEnv = localEnv;
1272+
}
1273+
1274+
boolean insideLambdaOrClassDef = false;
1275+
1276+
@Override
1277+
public void visitLambda(JCLambda lambda) {
1278+
boolean previousInsideLambdaOrClassDef = insideLambdaOrClassDef;
1279+
try {
1280+
insideLambdaOrClassDef = true;
1281+
super.visitLambda(lambda);
1282+
} finally {
1283+
insideLambdaOrClassDef = previousInsideLambdaOrClassDef;
1284+
}
1285+
}
1286+
1287+
@Override
1288+
public void visitClassDef(JCClassDecl classDecl) {
1289+
boolean previousInsideLambdaOrClassDef = insideLambdaOrClassDef;
1290+
try {
1291+
insideLambdaOrClassDef = true;
1292+
super.visitClassDef(classDecl);
1293+
} finally {
1294+
insideLambdaOrClassDef = previousInsideLambdaOrClassDef;
1295+
}
1296+
}
1297+
1298+
private void reportPrologueError(JCTree tree, Symbol sym) {
1299+
preview.checkSourceLevel(tree, Feature.FLEXIBLE_CONSTRUCTORS);
1300+
log.error(tree, Errors.CantRefBeforeCtorCalled(sym));
1301+
}
1302+
1303+
@Override
1304+
public void visitApply(JCMethodInvocation tree) {
1305+
super.visitApply(tree);
1306+
Name name = TreeInfo.name(tree.meth);
1307+
boolean isConstructorCall = name == names._this || name == names._super;
1308+
Symbol msym = TreeInfo.symbolFor(tree.meth);
1309+
// is this an instance method call or an illegal constructor invocation like: `this.super()`?
1310+
if (msym != null && // for erroneous invocations msym can be null, ignore those
1311+
(!isConstructorCall ||
1312+
isConstructorCall && tree.meth.hasTag(SELECT))) {
1313+
if (isEarlyReference(localEnv, tree.meth, msym))
1314+
reportPrologueError(tree.meth, msym);
1315+
}
1316+
}
1317+
1318+
@Override
1319+
public void visitIdent(JCIdent tree) {
1320+
analyzeSymbol(tree);
1321+
}
1322+
1323+
@Override
1324+
public void visitSelect(JCFieldAccess tree) {
1325+
SelectScanner ss = new SelectScanner();
1326+
ss.scan(tree);
1327+
if (ss.scanLater == null) {
1328+
analyzeSymbol(tree);
1329+
} else {
1330+
boolean prevLhs = isInLHS;
1331+
try {
1332+
isInLHS = false;
1333+
scan(ss.scanLater);
1334+
} finally {
1335+
isInLHS = prevLhs;
1336+
}
1337+
}
1338+
}
1339+
1340+
@Override
1341+
public void visitNewClass(JCNewClass tree) {
1342+
super.visitNewClass(tree);
1343+
checkNewClassAndMethRefs(tree, tree.type);
1344+
}
1345+
1346+
@Override
1347+
public void visitReference(JCMemberReference tree) {
1348+
super.visitReference(tree);
1349+
if (tree.getMode() == JCMemberReference.ReferenceMode.NEW) {
1350+
checkNewClassAndMethRefs(tree, tree.expr.type);
1351+
}
1352+
}
1353+
1354+
void checkNewClassAndMethRefs(JCTree tree, Type t) {
1355+
if (t.tsym.isEnclosedBy(localEnv.enclClass.sym) &&
1356+
!t.tsym.isStatic() &&
1357+
!t.tsym.isDirectlyOrIndirectlyLocal()) {
1358+
reportPrologueError(tree, t.getEnclosingType().tsym);
1359+
}
1360+
}
1361+
1362+
/* if a symbol is in the LHS of an assignment expression we won't consider it as a candidate
1363+
* for a proxy local variable later on
1364+
*/
1365+
boolean isInLHS = false;
1366+
1367+
@Override
1368+
public void visitAssign(JCAssign tree) {
1369+
boolean previousIsInLHS = isInLHS;
1370+
try {
1371+
isInLHS = true;
1372+
scan(tree.lhs);
1373+
} finally {
1374+
isInLHS = previousIsInLHS;
1375+
}
1376+
scan(tree.rhs);
1377+
}
1378+
1379+
@Override
1380+
public void visitMethodDef(JCMethodDecl tree) {
1381+
// ignore any declarative part, mainly to avoid scanning receiver parameters
1382+
scan(tree.body);
1383+
}
1384+
1385+
void analyzeSymbol(JCTree tree) {
1386+
Symbol sym = TreeInfo.symbolFor(tree);
1387+
if (isInLHS && !insideLambdaOrClassDef) {
1388+
// Check instance field assignments that appear in constructor prologues
1389+
if (isEarlyReference(localEnv, tree, sym)) {
1390+
// Field may not be inherited from a superclass
1391+
if (sym.owner != localEnv.enclClass.sym) {
1392+
log.error(tree, Errors.CantRefBeforeCtorCalled(sym));
1393+
return;
1394+
}
1395+
1396+
// Field may not have an initializer
1397+
if ((sym.flags() & HASINIT) != 0) {
1398+
log.error(tree, Errors.CantAssignInitializedBeforeCtorCalled(sym));
1399+
return;
1400+
}
1401+
}
1402+
return;
1403+
}
1404+
tree = TreeInfo.skipParens(tree);
1405+
if (sym != null) {
1406+
if (!sym.isStatic() && sym.kind == VAR && sym.owner.kind == TYP) {
1407+
if (sym.name == names._this || sym.name == names._super) {
1408+
// are we seeing something like `this` or `CurrentClass.this` or `SuperClass.super::foo`?
1409+
if (TreeInfo.isExplicitThisReference(
1410+
types,
1411+
(ClassType)localEnv.enclClass.sym.type,
1412+
tree)) {
1413+
reportPrologueError(tree, sym);
1414+
}
1415+
} else if (sym.kind == VAR && sym.owner.kind == TYP) { // now fields only
1416+
if (sym.owner != localEnv.enclClass.sym) {
1417+
if (localEnv.enclClass.sym.isSubClass(sym.owner, types) &&
1418+
sym.isInheritedIn(localEnv.enclClass.sym, types)) {
1419+
/* if we are dealing with a field that doesn't belong to the current class, but the
1420+
* field is inherited, this is an error. Unless, the super class is also an outer
1421+
* class and the field's qualifier refers to the outer class
1422+
*/
1423+
if (tree.hasTag(IDENT) ||
1424+
TreeInfo.isExplicitThisReference(
1425+
types,
1426+
(ClassType)localEnv.enclClass.sym.type,
1427+
((JCFieldAccess)tree).selected)) {
1428+
reportPrologueError(tree, sym);
1429+
}
1430+
}
1431+
} else if (isEarlyReference(localEnv, tree, sym)) {
1432+
/* now this is a `proper` instance field of the current class
1433+
* references to fields of identity classes which happen to have initializers are
1434+
* not allowed in the prologue
1435+
*/
1436+
if (insideLambdaOrClassDef ||
1437+
(!localEnv.enclClass.sym.isValueClass() && (sym.flags_field & HASINIT) != 0))
1438+
reportPrologueError(tree, sym);
1439+
// we will need to generate a proxy for this field later on
1440+
if (!isInLHS) {
1441+
if (allowValueClasses) {
1442+
localProxyVarsGen.addFieldReadInPrologue(localEnv.enclMethod, sym);
1443+
} else {
1444+
reportPrologueError(tree, sym);
1445+
}
1446+
}
1447+
}
1448+
}
1449+
}
1450+
}
1451+
}
1452+
1453+
/**
1454+
* Determine if the symbol appearance constitutes an early reference to the current class.
1455+
*
1456+
* <p>
1457+
* This means the symbol is an instance field, or method, of the current class and it appears
1458+
* in an early initialization context of it (i.e., one of its constructor prologues).
1459+
*
1460+
* @param env The current environment
1461+
* @param tree the AST referencing the variable
1462+
* @param sym The symbol
1463+
*/
1464+
private boolean isEarlyReference(Env<AttrContext> env, JCTree tree, Symbol sym) {
1465+
if ((sym.flags() & STATIC) == 0 &&
1466+
(sym.kind == VAR || sym.kind == MTH) &&
1467+
sym.isMemberOf(env.enclClass.sym, types)) {
1468+
// Allow "Foo.this.x" when "Foo" is (also) an outer class, as this refers to the outer instance
1469+
if (tree instanceof JCFieldAccess fa) {
1470+
return TreeInfo.isExplicitThisReference(types, (ClassType)env.enclClass.type, fa.selected);
1471+
}
1472+
return true;
1473+
}
1474+
return false;
1475+
}
1476+
1477+
/* scanner for a select expression, anything that is not a select or identifier
1478+
* will be stored for further analysis
1479+
*/
1480+
class SelectScanner extends DeferredAttr.FilterScanner {
1481+
JCTree scanLater;
1482+
1483+
SelectScanner() {
1484+
super(Set.of(IDENT, SELECT, PARENS));
1485+
}
1486+
1487+
@Override
1488+
void skip(JCTree tree) {
1489+
scanLater = tree;
1490+
}
1491+
}
1492+
}
1493+
12661494
public void visitVarDef(JCVariableDecl tree) {
12671495
// Local variables have not been entered yet, so we need to do it now:
12681496
if (env.info.scope.owner.kind == MTH || env.info.scope.owner.kind == VAR) {
@@ -1335,6 +1563,11 @@ public void visitVarDef(JCVariableDecl tree) {
13351563
//fixup local variable type
13361564
v.type = chk.checkLocalVarType(tree, tree.init.type, tree.name);
13371565
}
1566+
if (v.owner.kind == TYP && !v.isStatic() && v.isStrict()) {
1567+
// strict field initializers are inlined in constructor's prologues
1568+
CtorPrologueVisitor ctorPrologueVisitor = new CtorPrologueVisitor(initEnv);
1569+
ctorPrologueVisitor.scan(tree.init);
1570+
}
13381571
} finally {
13391572
initEnv.info.ctorPrologue = previousCtorPrologue;
13401573
}

0 commit comments

Comments
 (0)