RFR: 8273408: java.lang.AssertionError: typeSig ERROR on generated class property of record
Guoxiong Li
gli at openjdk.java.net
Wed Sep 15 07:18:45 UTC 2021
On Wed, 15 Sep 2021 02:34:10 GMT, Vicente Romero <vromero at openjdk.org> wrote:
> could you please add a link to where this is done in the compiler for other cases different from records? thanks
The compiler would [parse the newly generated source files](https://github.com/openjdk/jdk/blob/febcc72a549e973de4649503fc686fc520e3b3cd/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java#L1393) and [enter all the source files again](https://github.com/openjdk/jdk/blob/febcc72a549e973de4649503fc686fc520e3b3cd/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java#L1133). Many dummy types or symbols, such as ErrorType, would be replaced with the actually right ones.
> But when the feature record is added, the convention is broken.
This description may be excessive. Actually, new features always obey the convention, so as record. But the method `getRecordComponent` in this bug is an example which obscures the convention.
In detail, please read the following source code, currently in the main line.
// Symbol.java
public RecordComponent getRecordComponent(JCVariableDecl var, boolean addIfMissing, List<JCAnnotation> annotations) {
for (RecordComponent rc : recordComponents) {
/* it could be that a record erroneously declares two record components with the same name, in that
* case we need to use the position to disambiguate
*/
if (rc.name == var.name && var.pos == rc.pos) {
return rc;
}
}
RecordComponent rc = null;
if (addIfMissing) {
recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, annotations));
}
return rc;
}
Before the first processing round, the `var` is not right at all, because `var.sym.type` is `ErrorType`. So the `getRecordComponent` would generate a not good `RecordComponent` by using the wrong `var.sym` whose type is `ErrorType`.
After the first processing round, the new source files are created and we can catch the right type from the newly generated source files. When the compiler invokes `getRecordComponent` to get the `RecordComponent`, it only can get the wrong one(the type is `ErrorType`). At this time, the `var.sym.type` is the right `ClassType` which is created correctly earier. So we should use current argument `var` and the right `var.sym.type` instead of the old wrong one.
So you can see my patch, I judge whether the type is right. If it is right, which means the `RecordComponent` created earier is right, I reuse it. If the type is `ErrorType`, we shouldn't keep it and should create a new one by using the right `var` whose `var.sym.type` is not `ErrorType`.
if (rc.type.hasTag(TypeTag.ERROR) && !var.sym.type.hasTag(TypeTag.ERROR)) {
// Found a wrong record component: save it so that we can remove it later.
// If the class type of the record component is generated by annotation processor, it should
// use the new actual class type and symbol instead of the old dummy ErrorType.
toRemove = rc;
} else {
// Found a good record component: just return.
return rc;
}
You can put breakpoints at the method `getRecordComponent` to watch the `var.sym.type` and `rc.type`. You can observe that the `var.sym.type` is an `ErrorType` at first and is a right `ClassType` after first processing round.
-------------
PR: https://git.openjdk.java.net/jdk/pull/5511
More information about the compiler-dev
mailing list