RFR: 8328536: javac - crash on unknown type referenced in yield statement [v2]
Jan Lahoda
jlahoda at openjdk.org
Thu May 16 17:04:04 UTC 2024
On Thu, 28 Mar 2024 08:37:00 GMT, Hannes Greule <hgreule at openjdk.org> wrote:
>> Pasting e.g.
>>
>> I m(I i, int x) {
>> return switch (x) {
>> default -> i;
>> };
>> }
>>
>> in jshell will cause a crash if `I` is not declared already. This comes down to javac not creating an error type for the value of the (implicit) yield from the switch.
>>
>> Javac will not crash but swallow the exception, and create a file containing the command line options.
>>
>> I first thought about just checking for null of the type here https://github.com/openjdk/jdk/blob/9ca4ae3d3b746f1d75036d189ff98f02b73b948f/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java#L1640 but after a closer look, the `checkIdInternal` method seems a better fit, as it also updates the type normally.
>
> Hannes Greule has updated the pull request with a new target base due to a merge or a rebase. The incremental webrev excludes the unrelated changes brought in by the merge/rebase. The pull request contains three additional commits since the last revision:
>
> - Merge branch 'refs/heads/master' into fix/switch-yield-unknown-type-crash
> - add bug id
> - Fix jshell crash on unknown type in switch yield
I apologize for a belated reply. I agree with not adding a null check, that is usually a wrong/insufficient approach. And I think I agree with your approach, but I am not sure if it goes far enough.
I think I would try to go with something like:
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
index d0f3ae7464a..4fe1b4430ec 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -4623,9 +4623,6 @@ Type checkIdInternal(JCTree tree,
Type pt,
Env<AttrContext> env,
ResultInfo resultInfo) {
- if (pt.isErroneous()) {
- return types.createErrorType(site);
- }
Type owntype; // The computed type of this identifier occurrence.
switch (sym.kind) {
case TYP:
@@ -4734,6 +4731,10 @@ else if (ownOuter.hasTag(CLASS) && site != ownOuter) {
chk.checkPreview(tree.pos(), env.info.scope.owner, sym);
}
+ if (pt.isErroneous()) {
+ owntype = types.createErrorType(owntype);
+ }
+
// If symbol is a variable, check that its type and
// kind are compatible with the prototype and protokind.
return check(tree, owntype, sym.kind.toSelector(), resultInfo);
I.e., let the `checkIdInternal` run as normally, just wrap the type with an error type. When I ran tests with this change, three failed for me:
FAILED: tools/javac/generics/diamond/7188968/T7188968.java
FAILED: tools/javac/lambda/methodReference/MethodRefToInnerWithoutOuter.java
FAILED: tools/javac/lambda/MethodReference23.java
For the `tools/javac/lambda` tests, the new errors seemed better/more appropriate than the original ones, so those are not a problem I think (but an independent check would be good).
The `tools/javac/generics/diamond/7188968/T7188968.java` is producing more unchecked warnings with that change, and I am not quite sure if that's appropriate.
As for a test - test inside JShell is good, but there should be a test inside somewhere `tools/javac`. A good place might be `test/langtools/tools/javac/recovery/AttrRecovery.java`:
@Test
public void testErroneousTarget() throws Exception {
String code = """
public class C {
public Undefined g(Undefined u) {
return switch (0) {
default -> u;
};
}
}
""";
Path curPath = Path.of(".");
List<String> actual = new JavacTask(tb)
.options("-XDrawDiagnostics")
.sources(code)
.outdir(curPath)
.run(Expect.FAIL, 1)
.writeAll()
.getOutputLines(OutputKind.DIRECT);
List<String> expected = List.of(
"C.java:2:24: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, C, null)",
"C.java:2:12: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, C, null)",
"2 errors"
);
if (!Objects.equals(actual, expected)) {
error("Expected: " + expected + ", but got: " + actual);
}
}
(Note the explicit use of the expected exit code, which should guard against crashes. An alternative is to use `-XDdev` in the list of parameters, see other tests in the class - that should print the exception if it happens.)
-------------
PR Comment: https://git.openjdk.org/jdk/pull/18383#issuecomment-2115778378
More information about the compiler-dev
mailing list