Skip to content

Commit 96015d1

Browse files
AntonKozlovRealCLanger
authored andcommitted
8289647: AssertionError during annotation processing of record related tests
Reviewed-by: phh Backport-of: 64a1a08ff9f120648e466449f65750991cbf673c
1 parent f6851e9 commit 96015d1

File tree

3 files changed

+86
-39
lines changed

3 files changed

+86
-39
lines changed

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,10 +1503,7 @@ public RecordComponent getRecordComponent(VarSymbol field) {
15031503
return null;
15041504
}
15051505

1506-
/* creates a record component if non is related to the given variable and recreates a brand new one
1507-
* in other case
1508-
*/
1509-
public RecordComponent createRecordComponent(JCVariableDecl var, List<JCAnnotation> annotations) {
1506+
public RecordComponent findRecordComponentToRemove(JCVariableDecl var) {
15101507
RecordComponent toRemove = null;
15111508
for (RecordComponent rc : recordComponents) {
15121509
/* it could be that a record erroneously declares two record components with the same name, in that
@@ -1516,11 +1513,17 @@ public RecordComponent createRecordComponent(JCVariableDecl var, List<JCAnnotati
15161513
toRemove = rc;
15171514
}
15181515
}
1516+
return toRemove;
1517+
}
1518+
1519+
/* creates a record component if non is related to the given variable and recreates a brand new one
1520+
* in other case
1521+
*/
1522+
public RecordComponent createRecordComponent(RecordComponent existing, JCVariableDecl var, List<JCAnnotation> annotations) {
15191523
RecordComponent rc = null;
1520-
if (toRemove != null) {
1521-
// Found a record component with an erroneous type: remove it and create a new one
1522-
recordComponents = List.filter(recordComponents, toRemove);
1523-
recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, toRemove.originalAnnos, toRemove.isVarargs));
1524+
if (existing != null) {
1525+
recordComponents = List.filter(recordComponents, existing);
1526+
recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, existing.originalAnnos, existing.isVarargs));
15241527
} else {
15251528
// Didn't find the record component: create one.
15261529
recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, annotations));

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

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -963,9 +963,39 @@ protected void runPhase(Env<AttrContext> env) {
963963
ClassSymbol sym = tree.sym;
964964
if ((sym.flags_field & RECORD) != 0) {
965965
List<JCVariableDecl> fields = TreeInfo.recordFields(tree);
966-
memberEnter.memberEnter(fields, env);
966+
967967
for (JCVariableDecl field : fields) {
968-
sym.createRecordComponent(field,
968+
/** Some notes regarding the code below. Annotations applied to elements of a record header are propagated
969+
* to other elements which, when applicable, not explicitly declared by the user: the canonical constructor,
970+
* accessors, fields and record components. Of all these the only ones that can't be explicitly declared are
971+
* the fields and the record components.
972+
*
973+
* Now given that annotations are propagated to all possible targets regardless of applicability,
974+
* annotations not applicable to a given element should be removed. See Check::validateAnnotation. Once
975+
* annotations are removed we could lose the whole picture, that's why original annotations are stored in
976+
* the record component, see RecordComponent::originalAnnos, but there is no real AST representing a record
977+
* component so if there is an annotation processing round it could be that we need to reenter a record for
978+
* which we need to re-attribute its annotations. This is why one of the things the code below is doing is
979+
* copying the original annotations from the record component to the corresponding field, again this applies
980+
* only if APs are present.
981+
*
982+
* First, we find the record component by comparing its name and position with current field,
983+
* if any, and we mark it. Then we copy the annotations to the field so that annotations applicable only to the record component
984+
* can be attributed, as if declared in the field, and then stored in the metadata associated to the record
985+
* component. The invariance we need to keep here is that record components must be scheduled for
986+
* annotation only once during this process.
987+
*/
988+
RecordComponent rc = sym.findRecordComponentToRemove(field);
989+
990+
if (rc != null && (rc.getOriginalAnnos().length() != field.mods.annotations.length())) {
991+
TreeCopier<JCTree> tc = new TreeCopier<>(make.at(field.pos));
992+
List<JCAnnotation> originalAnnos = tc.copy(rc.getOriginalAnnos());
993+
field.mods.annotations = originalAnnos;
994+
}
995+
996+
memberEnter.memberEnter(field, env);
997+
998+
sym.createRecordComponent(rc, field,
969999
field.mods.annotations.isEmpty() ?
9701000
List.nil() :
9711001
new TreeCopier<JCTree>(make.at(field.pos)).copy(field.mods.annotations));
@@ -1211,37 +1241,10 @@ private void addRecordMembersIfNeeded(JCClassDecl tree, Env<AttrContext> env) {
12111241
memberEnter.memberEnter(equals, env);
12121242
}
12131243

1214-
/** Some notes regarding the code below. Annotations applied to elements of a record header are propagated
1215-
* to other elements which, when applicable, not explicitly declared by the user: the canonical constructor,
1216-
* accessors, fields and record components. Of all these the only ones that can't be explicitly declared are
1217-
* the fields and the record components.
1218-
*
1219-
* Now given that annotations are propagated to all possible targets regardless of applicability,
1220-
* annotations not applicable to a given element should be removed. See Check::validateAnnotation. Once
1221-
* annotations are removed we could lose the whole picture, that's why original annotations are stored in
1222-
* the record component, see RecordComponent::originalAnnos, but there is no real AST representing a record
1223-
* component so if there is an annotation processing round it could be that we need to reenter a record for
1224-
* which we need to re-attribute its annotations. This is why one of the things the code below is doing is
1225-
* copying the original annotations from the record component to the corresponding field, again this applies
1226-
* only if APs are present.
1227-
*
1228-
* We need to copy the annotations to the field so that annotations applicable only to the record component
1229-
* can be attributed as if declared in the field and then stored in the metadata associated to the record
1230-
* component.
1231-
*/
1244+
// fields can't be varargs, lets remove the flag
12321245
List<JCVariableDecl> recordFields = TreeInfo.recordFields(tree);
12331246
for (JCVariableDecl field: recordFields) {
1234-
RecordComponent rec = tree.sym.getRecordComponent(field.sym);
1235-
TreeCopier<JCTree> tc = new TreeCopier<>(make.at(field.pos));
1236-
List<JCAnnotation> originalAnnos = tc.copy(rec.getOriginalAnnos());
1237-
12381247
field.mods.flags &= ~Flags.VARARGS;
1239-
if (originalAnnos.length() != field.mods.annotations.length()) {
1240-
field.mods.annotations = originalAnnos;
1241-
annotate.annotateLater(originalAnnos, env, field.sym, field.pos());
1242-
}
1243-
1244-
// also here
12451248
field.sym.flags_field &= ~Flags.VARARGS;
12461249
}
12471250
// now lets add the accessors

test/langtools/tools/javac/records/RecordCompilationTests.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
* RecordCompilationTests
2626
*
2727
* @test
28-
* @bug 8250629 8252307 8247352 8241151 8246774 8259025 8288130
28+
* @bug 8250629 8252307 8247352 8241151 8246774 8259025 8288130 8289647
2929
* @summary Negative compilation tests, and positive compilation (smoke) tests for records
3030
* @library /lib/combo /tools/lib /tools/javac/lib
3131
* @modules
@@ -1335,6 +1335,47 @@ public void testAcceptRecordId() {
13351335
}
13361336
}
13371337

1338+
public void testMultipleAnnosInRecord() throws Exception {
1339+
String[] previousOptions = getCompileOptions();
1340+
1341+
try {
1342+
String imports = """
1343+
import java.lang.annotation.ElementType;
1344+
import java.lang.annotation.Target;
1345+
""";
1346+
1347+
String annotTemplate =
1348+
"""
1349+
@Target(ElementType.#TARGET)
1350+
@interface anno#TARGET { }
1351+
""";
1352+
1353+
String recordTemplate =
1354+
"""
1355+
record R(#TARGETS String s) {}
1356+
""";
1357+
1358+
String[] generalOptions = {
1359+
"-processor", Processor.class.getName(),
1360+
};
1361+
1362+
List<String> targets = List.of("FIELD", "RECORD_COMPONENT", "PARAMETER", "METHOD");
1363+
1364+
var interfaces = targets.stream().map(t -> annotTemplate.replaceAll("#TARGET", t)).collect(Collectors.joining("\n"));
1365+
var recordAnnotations = targets.stream().map(t -> "@anno" + t).collect(Collectors.joining(" "));
1366+
String record = recordTemplate.replaceFirst("#TARGETS", recordAnnotations);
1367+
String code = String.format("%s\n%s\n%s\n",imports,interfaces,record);
1368+
String[] testOptions = generalOptions.clone();
1369+
setCompileOptions(testOptions);
1370+
1371+
assertOK(true, code);
1372+
1373+
// let's reset the default compiler options for other tests
1374+
} finally {
1375+
setCompileOptions(previousOptions);
1376+
}
1377+
}
1378+
13381379
public void testAnnos() throws Exception {
13391380
String[] previousOptions = getCompileOptions();
13401381
try {

0 commit comments

Comments
 (0)