United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-6500343 : compiler generates bad code when translating conditional expressions

Details
Type:
Bug
Submit Date:
2006-12-04
Status:
Closed
Updated Date:
2012-01-13
Project Name:
JDK
Resolved Date:
2012-01-13
Component:
tools
OS:
windows_vista,linux,windows_xp,windows_7,windows_2000
Sub-Component:
javac
CPU:
x86
Priority:
P2
Resolution:
Fixed
Affected Versions:
5.0,6,6u24,6u26
Fixed Versions:

Related Reports
Backport:
Duplicate:
Duplicate:

Sub Tasks

Description
J2SE Version (please include all output from java -version flag):
java version "1.6.0-rc"
Java(TM) SE Runtime Environment (build 1.6.0-rc-b96)
Java HotSpot(TM) Client VM (build 1.6.0-rc-b96, mixed mode, sharing)

Does this problem occur on J2SE 1.4.x or 5.0.x ?  Yes / No (pick one)
No, works fine with 5.0u9

Operating System Configuration Information (be specific):
Microsoft Windows 2000 [Version 5.00.2195]

An exception has occurred in the compiler (1.6.0-rc)

java.lang.AssertionError
        at com.sun.tools.javac.jvm.Code$State.forceStackTop(Code.java:1688)
        at com.sun.tools.javac.jvm.Gen.visitConditional(Gen.java:1644)
        at com.sun.tools.javac.tree.JCTree$JCConditional.accept(JCTree.java:1021)
        at com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:813)
        at com.sun.tools.javac.jvm.Gen.visitReturn(Gen.java:1591)
        at com.sun.tools.javac.tree.JCTree$JCReturn.accept(JCTree.java:1138)
        at com.sun.tools.javac.jvm.Gen.genDef(Gen.java:660)
        at com.sun.tools.javac.jvm.Gen.genStat(Gen.java:695)
        at com.sun.tools.javac.jvm.Gen.genStat(Gen.java:681)
        at com.sun.tools.javac.jvm.Gen.genStats(Gen.java:732)
        at com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:985)
        at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:739)
        at com.sun.tools.javac.jvm.Gen.genDef(Gen.java:660)
        at com.sun.tools.javac.jvm.Gen.genStat(Gen.java:695)
        at com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:918)
        at com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:854)
        at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:639)
        at com.sun.tools.javac.jvm.Gen.genDef(Gen.java:660)
        at com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2163)
        at com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:617)
        at com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1289)
        at com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1259)
        at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:765)
        at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:730)
        at com.sun.tools.javac.main.Main.compile(Main.java:353)
        at com.sun.tools.javac.main.Main.compile(Main.java:279)
        at com.sun.tools.javac.main.Main.compile(Main.java:270)
        at com.sun.tools.javac.Main.compile(Main.java:69)
        at com.sun.tools.javac.Main.main(Main.java:54)

import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;

public class Test extends JCheckBox implements TableCellRenderer {
  protected TableCellRenderer getCellRenderer(JTable aTable) {
    return aTable.getRowCount() == 0 ? aTable.getDefaultRenderer(Object.class)
                                     : aTable.getRowCount() == 1 ? new Test()
                                                                 : new DefaultTableCellRenderer() {
      public Component getTableCellRendererComponent(JTable aTable, Object aValue,
          boolean aSelectedFlag, boolean aHasFocusFlag, int aRowIndex, int aColumnIndex) {
        return this;
      }
    };
  }
 
  public Component getTableCellRendererComponent(JTable aTable, Object aValue, boolean aSelectedFlag
      , boolean aHasFocusFlag, int aRow, int aColumn) {
    return this;
  }
}
Compact equivalent test-case (does *not* depends on swing) :

class Base {}
interface I {}
class A1 extends Base implements I {}
class A2 extends Base implements I {}
class Test {
    Object crash(I i, A1 a1, A2 a2, boolean b1, boolean b2) {
        return b1 ? i : b2 ? a2 : a1;
    }
}

                                    

Comments
SUGGESTED FIX

a webrev of this fix is available at the folllowing URL
http://sa.sfbay.sun.com/projects/langtools_data/7/6500343
                                     
2008-09-29
WORK AROUND

add an explicit cast as in:

class Base {}
interface I {}
class A1 extends Base implements I {}
class A2 extends Base implements I {}
class Test {
    Object crash(I i, A1 a1, A2 a2, boolean b1, boolean b2) {
        return b1 ? i : b2 ? a2 : (I)a1;
    }
}

or (in the original example):

import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;

public class Test extends JCheckBox implements TableCellRenderer {
  protected TableCellRenderer getCellRenderer(JTable aTable) {
    return aTable.getRowCount() == 0 ? aTable.getDefaultRenderer(Object.class)
                                     : aTable.getRowCount() == 1 ? (TableCellRenderer)new Test() //cast here!!
                                                                 : new DefaultTableCellRenderer() {
      public Component getTableCellRendererComponent(JTable aTable, Object aValue,
          boolean aSelectedFlag, boolean aHasFocusFlag, int aRowIndex, int aColumnIndex) {
        return this;
      }
    };
  }
 
  public Component getTableCellRendererComponent(JTable aTable, Object aValue, boolean aSelectedFlag
      , boolean aHasFocusFlag, int aRow, int aColumn) {
    return this;
  }
}
                                     
2008-06-24
EVALUATION

JLS 15.25 says that, given a conditional expression of the kind:

a ? b : c

the type of this expression could be determined as follows (skipping unrelevant parts)

"[...]Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (??5.1.10) to lub(T1, T2) (??15.12.2.7)"

full description of lub is given in JLS 15.12.2.7; what it matters here is that, given two non-generic types T1 and T2, lub(T1,T2) = S1 & S2.& ... Sn, where S1, S2 ... Sn are the common supertypes between T1 and T2

In our case (simplified test-case) whe have that the expression is:

b1 ? i : b2 ? a2 : a1

then, accordingly to the above description the type of this expression should be:

lub(I, lub(A1, A2)) = lub(I, Base & I) = I

However, a bug in javac's Lower phase, prevents the compiler to type this expression correctly; here's what the compiler does:

*) type of true-part of the conditional expression is erased --> erasure(I) = I
*) type of false-part of the conditional expression is erased --> erasure(Base&I) = Base

at this point code generation fails since there's no way to merge the types I and Base.
The compiler should have inserted a cast expression when removing the synthetic intersection type in order to coerce the type of the false-part of the conditional expression to match the (previously attributed) type of the whole conditional expression (that we have shown to be I).
This missing synthetic cast is causing the failure.

Actually, this bug cannot be regarded as a regression, as all the compiler I tried (both 5, 6 an d 7) share the same buggy behaviour. If you try to compile this test-case with an elder compiler (1.4 or previous), the program is rejected, as conditional expression were typed differently in JLS 2 (because Java 1.4 is lacking lub and intersection types). Below is reported the former JLS typing rules for conditional expression:

"If the second and third operands are of different reference types, then it must be possible to convert one of the types to the other type (call this latter type T) by assignment conversion (??5.2); the type of the conditional expression is T. It is a compile-time error if neither type is assignment compatible with the other type."

Since in this case we have that neither Base <: I nor I <: Base, an 1.4 compiler would have rejected the submitted program.

For the above reasons, this hould be regarded as a simple bug in Lower.java, as javac doesn't take into account that the type of a conditional expression could be a synthetic type like an intersection type generated by lub.
                                     
2008-06-24
EVALUATION

The operand stack has an incompatible type while joining a new state. 
The type on the operand stack is javax.swing.JComponet, while the new 
type is javax.swing.table.TableCellRenderer. Looks like the compiler
pushes a wrong type on the operand stack while resolving jump targets.
                                     
2006-12-08



Hardware and Software, Engineered to Work Together