JDK-7181320 : javac NullPointerException for switch labels with cast to String expressions
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 7
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: linux
  • CPU: x86
  • Submitted: 2012-07-03
  • Updated: 2013-06-26
  • Resolved: 2012-09-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8
7u40Fixed 8 b55Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
$ java -version
java version "1.7.0_05"
Java(TM) SE Runtime Environment (build 1.7.0_05-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.1-b03, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Linux <host> 2.6.18-92.el5 #1 SMP Tue Apr 29 13:16:15 EDT 2008 x86_64 x86_64 x86_64 GNU/Linux


A DESCRIPTION OF THE PROBLEM :
Compiling the source code included with this bug report results in a NullPointerException in the Java 1.7.0_05 compiler, at least on Linux x86_64.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Save the source code included with this bug report as a file named SwitchString.java and compile it with:
  javac SwitchString.java

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The provided source code should compile successfully.
ACTUAL -
The Java compiler aborts the compilation due to a NullPointerException.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
$ javac SwitchString.java
An exception has occurred in the compiler (1.7.0_05). Please file a bug at the Java Developer Connection (http://java.sun.com/webapps/bugreport)  after checking the Bug Parade for duplicates. Include your program and the following diagnostic in your report.  Thank you.
java.lang.NullPointerException
        at com.sun.tools.javac.comp.Lower.visitStringSwitch(Lower.java:3465)
        at com.sun.tools.javac.comp.Lower.visitSwitch(Lower.java:3365)
        at com.sun.tools.javac.tree.JCTree$JCSwitch.accept(JCTree.java:959)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2160)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70)
        at com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:160)
        at com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3319)
        at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:781)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2160)
        at com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:144)
        at com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2627)
        at com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2546)
        at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:669)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2160)
        at com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2291)
        at com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:591)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2160)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2180)
        at com.sun.tools.javac.comp.Lower.translateTopLevelClass(Lower.java:3659)
        at com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1395)
        at com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1273)
        at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:870)
        at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:829)
        at com.sun.tools.javac.main.Main.compile(Main.java:439)
        at com.sun.tools.javac.main.Main.compile(Main.java:353)
        at com.sun.tools.javac.main.Main.compile(Main.java:342)
        at com.sun.tools.javac.main.Main.compile(Main.java:333)
        at com.sun.tools.javac.Main.compile(Main.java:76)
        at com.sun.tools.javac.Main.main(Main.java:61)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/*
 * Compiling this program with the 1.7.0_05 Java compiler, at least on Linux
 *  x86_64, causes the compiler to abort due to a NullPointerException.
 *
 * To reproduce the NPE, compile with:
 *  javac SwitchString.java
 *
 * This test case was constructed to test the use of strings in switch
 *  statements as provided by Java 7 language enhancements.  It was drafted
 *  based on the following clauses from JLS 7 (JSR-000901):
 *
 * 14.11 The switch Statement
 * ...
 * These labels are said to be associated with the switch statement, as are
 * the values of the constant expressions (15.28) or enum constants (8.9.1)
 * in the case labels.
 *
 * 15.28 Constant Expressions
 * ...
 * A compile-time constant expression is an expression denoting a value of
 * primitive type or a String that does not complete abruptly and is composed
 * using only the following:
 * ...
 * - Casts to primitive types and casts to type String (15.16)
 *
 * The cast to String in the case label below has no semantic relevance, but
 *  is permitted according to JLS 7 (JSR-000901)
 */

public class SwitchString {
    public static void main(String [] args) {
        switch (args[0]) {
            case (String)"text":
                break;
        }
    }
}

---------- END SOURCE ----------

Comments
5 tests:http://hg.openjdk.java.net/jdk8/tl/langtools/file/tip/test/tools/javac/StringsInSwitch/7181320/
18-12-2012

EVALUATION Constant value associated with a type is erased during type erasure.
14-08-2012

SUGGESTED FIX When type erasure occurs in TransTypes, constant value associated with a Type (in this case reference Type String) is erased. For primitive types, constant value is preserved. Hence the following code compiles fine: public class Test { public void func(int i) { switch (i) { case (int)33: break } } } But, the constant value associated with String type is erased and so example in "Description" results in NPE. The following diff fixes the issue by restoring constant value after type erasure. Note that we restore type only when needed (to avoid having to use anon-class instance for type - see Type.constType). diff -r 1d2db0e5eabc src/share/classes/com/sun/tools/javac/code/Types.java --- a/src/share/classes/com/sun/tools/javac/code/Types.java Fri Aug 10 10:14:48 2012 -0700 +++ b/src/share/classes/com/sun/tools/javac/code/Types.java Tue Aug 14 10:38:35 2012 +0530 @@ -1589,7 +1589,13 @@ * type parameters in t are deleted. */ public Type erasure(Type t) { - return erasure(t, false); + Object constVal = t.constValue(); + Type erasedType = erasure(t, false); + // Type erasure should not erase constant value + if (constVal != null && erasedType.constValue() == null) { + erasedType = erasedType.constType(constVal); + } + return erasedType; } //where private Type erasure(Type t, boolean recurse) {
14-08-2012

SUGGESTED FIX Suspect the extraction of the labelExpr on line 3454 needs some additional code to look through an useless cast to get the real value: 3452 if (expression != null) { // expression for a "default" case is null 3453 expression = TreeInfo.skipParens(expression); 3454 String labelExpr = (String) expression.type.constValue(); 3455 Integer mapping = caseLabelToPosition.put(labelExpr, casePosition); 3456 Assert.checkNull(mapping);
03-07-2012