When replacing a field reference with a nullField reference, override the type of the reference
to be the original type.  This is important if the original type was a primitive type, because
otherwise the nullField would be of type null and thus incompatible with a primitive type.

Review by: scottb

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2986 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java
index 5abe912..c0b7118 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java
@@ -30,20 +30,33 @@
   /**
    * The referenced field.
    */
-  private JField field;
+  private final JField field;
 
   /**
    * This can only be null if the referenced field is static.
    */
   private JExpression instance;
 
+  /**
+   * An overridden type for this reference. Normally the type of a field
+   * reference is the same as the type of the field itself.  That default
+   * can be overridden by setting this field.
+   */
+  private final JType overriddenType;
+
   public JFieldRef(JProgram program, SourceInfo info, JExpression instance,
       JField field, JReferenceType enclosingType) {
+    this(program, info, instance, field, enclosingType, null);
+  }
+  
+  public JFieldRef(JProgram program, SourceInfo info, JExpression instance,
+      JField field, JReferenceType enclosingType, JType overriddenType) {
     super(program, info, field);
     assert (instance != null || field.isStatic());
     this.instance = instance;
     this.field = field;
     this.enclosingType = enclosingType;
+    this.overriddenType = overriddenType;
   }
 
   public JReferenceType getEnclosingType() {
@@ -57,7 +70,16 @@
   public JExpression getInstance() {
     return instance;
   }
+  
+  @Override
+  public JType getType() {
+    if (overriddenType != null) {
+      return overriddenType;
+    }
+    return super.getType();
+  }
 
+  @Override
   public boolean hasSideEffects() {
     // A cross-class reference to a static, non constant field forces clinit
     if (field.isStatic()
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index 36ec779..e3f4063 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -112,7 +112,7 @@
           instance = program.getLiteralNull();
         }
         JFieldRef fieldRef = new JFieldRef(program, x.getSourceInfo(),
-            instance, program.getNullField(), null);
+            instance, program.getNullField(), null, x.getType());
         ctx.replaceMe(fieldRef);
       }
     }
@@ -379,10 +379,12 @@
      */
     private boolean myDidChange = false;
 
+    @Override
     public boolean didChange() {
       return myDidChange || super.didChange();
     }
 
+    @Override
     public void endVisit(JCastOperation x, Context ctx) {
       JType argType = x.getExpr().getType();
       if (!(x.getCastType() instanceof JReferenceType)
@@ -602,6 +604,7 @@
       return true;
     }
 
+    @Override
     public boolean visit(JMethod x, Context ctx) {
       /*
        * Explicitly NOT visiting native methods since we can't infer further
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
index 50d8803..3a19a3d 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
@@ -125,6 +125,7 @@
 
   private static final class UninstantiableType {
     public Object field;
+    public int intField;
 
     private UninstantiableType() {
     }
@@ -140,6 +141,10 @@
 
   private static volatile boolean TRUE = true;
 
+  private static volatile int volatileInt;
+  private static volatile boolean volatileBoolean;
+  private static volatile UninstantiableType volatileUninstantiableType;
+
   private static native void accessUninstantiableField(UninstantiableType u) /*-{
     u.@com.google.gwt.dev.jjs.test.CompilerTest$UninstantiableType::field.toString();
   }-*/;
@@ -162,6 +167,7 @@
     return @com.google.gwt.dev.jjs.test.CompilerTest$SideEffectCauser5::causeClinitSideEffectOnRead;
   }-*/;
 
+  @Override
   public String getModuleName() {
     return "com.google.gwt.dev.jjs.CompilerSuite";
   }
@@ -575,6 +581,7 @@
         a = foo;
       }
 
+      @Override
       public String toString() {
         return new Object() {
 
@@ -586,6 +593,7 @@
             ai = foo;
           }
 
+          @Override
           public String toString() {
             // this line used to cause ICE due to no synthetic path to bar
             bar.valueOf(false);
@@ -837,7 +845,7 @@
     assertEquals(-7, x);
   }
 
-  public void testUninstantiableNativeAccess() {
+  public void testUninstantiableAccess() {
     UninstantiableType u = null;
 
     try {
@@ -851,6 +859,48 @@
       fail("Expected JavaScriptException");
     } catch (JavaScriptException expected) {
     }
+
+    try {
+      volatileUninstantiableType = (UninstantiableType) (new Object());
+      fail("Expected ClassCastException");
+    } catch (ClassCastException expected) {
+    }
+
+    try {
+      volatileInt = u.intField++;
+      fail("Expected NullPointerException (1)");
+    } catch (Exception expected) {
+    }
+
+    try {
+      volatileInt = u.intField--;
+      fail("Expected NullPointerException (2)");
+    } catch (Exception expected) {
+    }
+
+    try {
+      volatileInt = ++u.intField;
+      fail("Expected NullPointerException (3)");
+    } catch (Exception expected) {
+    }
+
+    try {
+      volatileInt = --u.intField;
+      fail("Expected NullPointerException (4)");
+    } catch (Exception expected) {
+    }
+
+    try {
+      u.intField = 0;
+      fail("Expected NullPointerException (5)");
+    } catch (Exception expected) {
+    }
+
+    try {
+      volatileBoolean = u.intField == 0;
+      fail("Expected NullPointerException (6)");
+    } catch (Exception expected) {
+    }
   }
 
   private boolean returnFalse() {