@@ -124,6 +124,7 @@ public class Attr extends JCTree.Visitor {
124
124
final ArgumentAttr argumentAttr ;
125
125
final MatchBindingsComputer matchBindingsComputer ;
126
126
final AttrRecover attrRecover ;
127
+ final LocalProxyVarsGen localProxyVarsGen ;
127
128
128
129
public static Attr instance (Context context ) {
129
130
Attr instance = context .get (attrKey );
@@ -163,6 +164,7 @@ protected Attr(Context context) {
163
164
argumentAttr = ArgumentAttr .instance (context );
164
165
matchBindingsComputer = MatchBindingsComputer .instance (context );
165
166
attrRecover = AttrRecover .instance (context );
167
+ localProxyVarsGen = LocalProxyVarsGen .instance (context );
166
168
167
169
Options options = Options .instance (context );
168
170
@@ -301,9 +303,7 @@ boolean isAssignableAsBlankFinal(VarSymbol v, Env<AttrContext> env) {
301
303
void checkAssignable (DiagnosticPosition pos , VarSymbol v , JCTree base , Env <AttrContext > env ) {
302
304
if (v .name == names ._this ) {
303
305
log .error (pos , Errors .CantAssignValToThis );
304
- return ;
305
- }
306
- if ((v .flags () & FINAL ) != 0 &&
306
+ } else if ((v .flags () & FINAL ) != 0 &&
307
307
((v .flags () & HASINIT ) != 0
308
308
||
309
309
!((base == null ||
@@ -314,23 +314,6 @@ void checkAssignable(DiagnosticPosition pos, VarSymbol v, JCTree base, Env<AttrC
314
314
} else {
315
315
log .error (pos , Errors .CantAssignValToVar (Flags .toSource (v .flags () & (STATIC | FINAL )), v ));
316
316
}
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
- }
334
317
}
335
318
}
336
319
@@ -1252,6 +1235,25 @@ public void visitMethodDef(JCMethodDecl tree) {
1252
1235
1253
1236
// Attribute method body.
1254
1237
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
+ }
1255
1257
}
1256
1258
1257
1259
localEnv .info .scope .leave ();
@@ -1263,6 +1265,232 @@ public void visitMethodDef(JCMethodDecl tree) {
1263
1265
}
1264
1266
}
1265
1267
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
+
1266
1494
public void visitVarDef (JCVariableDecl tree ) {
1267
1495
// Local variables have not been entered yet, so we need to do it now:
1268
1496
if (env .info .scope .owner .kind == MTH || env .info .scope .owner .kind == VAR ) {
@@ -1335,6 +1563,11 @@ public void visitVarDef(JCVariableDecl tree) {
1335
1563
//fixup local variable type
1336
1564
v .type = chk .checkLocalVarType (tree , tree .init .type , tree .name );
1337
1565
}
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
+ }
1338
1571
} finally {
1339
1572
initEnv .info .ctorPrologue = previousCtorPrologue ;
1340
1573
}
0 commit comments