Add optimizations for instanceof/checkcast.

The optimizations try to statically determine the outcome of the
type tests, replacing/removing the instructions when possible.

This required to fix the is_exact flag for ReferenceTypePropagation.

Change-Id: I6cea29b6c351d118b62060e8420333085e9383fb
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index fcb3471..98a5841 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -186,33 +186,92 @@
   return false;
 }
 
-void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
-  HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
-  if (!check_cast->InputAt(0)->CanBeNull() || IsDominatedByInputNullCheck(check_cast)) {
-    check_cast->ClearMustDoNullCheck();
-  }
-
-  if (!load_class->IsResolved()) {
+// Returns whether doing a type test between the class of `object` against `klass` has
+// a statically known outcome. The result of the test is stored in `outcome`.
+static bool TypeCheckHasKnownOutcome(HLoadClass* klass, HInstruction* object, bool* outcome) {
+  if (!klass->IsResolved()) {
     // If the class couldn't be resolve it's not safe to compare against it. It's
     // default type would be Top which might be wider that the actual class type
     // and thus producing wrong results.
-    return;
+    return false;
   }
-  ReferenceTypeInfo obj_rti = check_cast->InputAt(0)->GetReferenceTypeInfo();
-  ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
+
+  ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo();
+  ReferenceTypeInfo class_rti = klass->GetLoadedClassRTI();
   ScopedObjectAccess soa(Thread::Current());
   if (class_rti.IsSupertypeOf(obj_rti)) {
+    *outcome = true;
+    return true;
+  } else if (obj_rti.IsExact()) {
+    // The test failed at compile time so will also fail at runtime.
+    *outcome = false;
+    return true;
+  } else if (!class_rti.IsInterface() && !obj_rti.IsSupertypeOf(class_rti)) {
+    // Different type hierarchy. The test will fail.
+    *outcome = false;
+    return true;
+  }
+  return false;
+}
+
+void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
+  HInstruction* object = check_cast->InputAt(0);
+  if (!object->CanBeNull() || IsDominatedByInputNullCheck(check_cast)) {
+    check_cast->ClearMustDoNullCheck();
+  }
+
+  if (object->IsNullConstant()) {
     check_cast->GetBlock()->RemoveInstruction(check_cast);
     if (stats_ != nullptr) {
       stats_->RecordStat(MethodCompilationStat::kRemovedCheckedCast);
     }
+    return;
+  }
+
+  bool outcome;
+  if (TypeCheckHasKnownOutcome(check_cast->InputAt(1)->AsLoadClass(), object, &outcome)) {
+    if (outcome) {
+      check_cast->GetBlock()->RemoveInstruction(check_cast);
+      if (stats_ != nullptr) {
+        stats_->RecordStat(MethodCompilationStat::kRemovedCheckedCast);
+      }
+    } else {
+      // Don't do anything for exceptional cases for now. Ideally we should remove
+      // all instructions and blocks this instruction dominates.
+    }
   }
 }
 
 void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
-  if (!instruction->InputAt(0)->CanBeNull() || IsDominatedByInputNullCheck(instruction)) {
+  HInstruction* object = instruction->InputAt(0);
+  bool can_be_null = true;
+  if (!object->CanBeNull() || IsDominatedByInputNullCheck(instruction)) {
+    can_be_null = false;
     instruction->ClearMustDoNullCheck();
   }
+
+  HGraph* graph = GetGraph();
+  if (object->IsNullConstant()) {
+    instruction->ReplaceWith(graph->GetIntConstant(0));
+    instruction->GetBlock()->RemoveInstruction(instruction);
+    RecordSimplification();
+    return;
+  }
+
+  bool outcome;
+  if (TypeCheckHasKnownOutcome(instruction->InputAt(1)->AsLoadClass(), object, &outcome)) {
+    if (outcome && can_be_null) {
+      // Type test will succeed, we just need a null test.
+      HNotEqual* test = new (graph->GetArena()) HNotEqual(graph->GetNullConstant(), object);
+      instruction->GetBlock()->InsertInstructionBefore(test, instruction);
+      instruction->ReplaceWith(test);
+    } else {
+      // We've statically determined the result of the instanceof.
+      instruction->ReplaceWith(graph->GetIntConstant(outcome));
+    }
+    RecordSimplification();
+    instruction->GetBlock()->RemoveInstruction(instruction);
+  }
 }
 
 void InstructionSimplifierVisitor::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index f87775e..c5fc563 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1281,6 +1281,9 @@
 
   bool IsExact() const { return is_exact_; }
   bool IsTop() const { return is_top_; }
+  bool IsInterface() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return !IsTop() && GetTypeHandle()->IsInterface();
+  }
 
   Handle<mirror::Class> GetTypeHandle() const { return type_handle_; }
 
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 4f1f457..3d81c20 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -166,31 +166,35 @@
   }
 }
 
-void ReferenceTypePropagation::SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass) {
+void ReferenceTypePropagation::SetClassAsTypeInfo(HInstruction* instr,
+                                                  mirror::Class* klass,
+                                                  bool is_exact) {
   if (klass != nullptr) {
     ScopedObjectAccess soa(Thread::Current());
     MutableHandle<mirror::Class> handle = handles_->NewHandle(klass);
-    instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, true));
+    is_exact = is_exact || klass->IsFinal();
+    instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
   }
 }
 
 void ReferenceTypePropagation::UpdateReferenceTypeInfo(HInstruction* instr,
                                                        uint16_t type_idx,
-                                                       const DexFile& dex_file) {
+                                                       const DexFile& dex_file,
+                                                       bool is_exact) {
   DCHECK_EQ(instr->GetType(), Primitive::kPrimNot);
 
   ScopedObjectAccess soa(Thread::Current());
   mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file);
   // Get type from dex cache assuming it was populated by the verifier.
-  SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx));
+  SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact);
 }
 
 void ReferenceTypePropagation::VisitNewInstance(HNewInstance* instr) {
-  UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile());
+  UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
 }
 
 void ReferenceTypePropagation::VisitNewArray(HNewArray* instr) {
-  UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile());
+  UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
 }
 
 void ReferenceTypePropagation::UpdateFieldAccessTypeInfo(HInstruction* instr,
@@ -206,7 +210,7 @@
   ArtField* field = cl->GetResolvedField(info.GetFieldIndex(), dex_cache);
   DCHECK(field != nullptr);
   mirror::Class* klass = field->GetType<false>();
-  SetClassAsTypeInfo(instr, klass);
+  SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
 }
 
 void ReferenceTypePropagation::VisitInstanceFieldGet(HInstanceFieldGet* instr) {
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 74e425f..0a1d4c4 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -46,14 +46,17 @@
   void VisitPhi(HPhi* phi);
   void VisitBasicBlock(HBasicBlock* block);
   void UpdateFieldAccessTypeInfo(HInstruction* instr, const FieldInfo& info);
-  void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass);
+  void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass, bool is_exact);
 
   void UpdateBoundType(HBoundType* bound_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void UpdatePhi(HPhi* phi) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void BoundTypeForIfNotNull(HBasicBlock* block);
   void BoundTypeForIfInstanceOf(HBasicBlock* block);
-  void UpdateReferenceTypeInfo(HInstruction* instr, uint16_t type_idx, const DexFile& dex_file);
+  void UpdateReferenceTypeInfo(HInstruction* instr,
+                               uint16_t type_idx,
+                               const DexFile& dex_file,
+                               bool is_exact);
   void VisitInstanceFieldGet(HInstanceFieldGet* instr);
   void VisitStaticFieldGet(HStaticFieldGet* instr);
 
diff --git a/test/494-checker-instanceof-tests/expected.txt b/test/494-checker-instanceof-tests/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/494-checker-instanceof-tests/expected.txt
diff --git a/test/494-checker-instanceof-tests/info.txt b/test/494-checker-instanceof-tests/info.txt
new file mode 100644
index 0000000..59e20bd
--- /dev/null
+++ b/test/494-checker-instanceof-tests/info.txt
@@ -0,0 +1 @@
+Checker test for optimizations on instanceof.
diff --git a/test/494-checker-instanceof-tests/src/Main.java b/test/494-checker-instanceof-tests/src/Main.java
new file mode 100644
index 0000000..bff9c72
--- /dev/null
+++ b/test/494-checker-instanceof-tests/src/Main.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static boolean $inline$classTypeTest(Object o) {
+    return o instanceof SubMain;
+  }
+
+  public static boolean $inline$interfaceTypeTest(Object o) {
+    return o instanceof Itf;
+  }
+
+  public static SubMain subMain;
+  public static Main mainField;
+  public static Unrelated unrelatedField;
+  public static FinalUnrelated finalUnrelatedField;
+
+  /// CHECK-START: boolean Main.classTypeTestNull() register (after)
+  /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+  /// CHECK-DAG:                Return [<<Const>>]
+  public static boolean classTypeTestNull() {
+    return $inline$classTypeTest(null);
+  }
+
+  /// CHECK-START: boolean Main.classTypeTestExactMain() register (after)
+  /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+  /// CHECK-DAG:                Return [<<Const>>]
+  public static boolean classTypeTestExactMain() {
+    return $inline$classTypeTest(new Main());
+  }
+
+  /// CHECK-START: boolean Main.classTypeTestExactSubMain() register (after)
+  /// CHECK-DAG: <<Const:i\d+>> IntConstant 1
+  /// CHECK-DAG:                Return [<<Const>>]
+  public static boolean classTypeTestExactSubMain() {
+    return $inline$classTypeTest(new SubMain());
+  }
+
+  /// CHECK-START: boolean Main.classTypeTestSubMainOrNull() register (after)
+  /// CHECK-DAG: <<Value:z\d+>> NotEqual
+  /// CHECK-DAG:                Return [<<Value>>]
+  public static boolean classTypeTestSubMainOrNull() {
+    return $inline$classTypeTest(subMain);
+  }
+
+  /// CHECK-START: boolean Main.classTypeTestMainOrNull() register (after)
+  /// CHECK-DAG: <<Value:z\d+>> InstanceOf
+  /// CHECK-DAG:                Return [<<Value>>]
+  public static boolean classTypeTestMainOrNull() {
+    return $inline$classTypeTest(mainField);
+  }
+
+  /// CHECK-START: boolean Main.classTypeTestUnrelated() register (after)
+  /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+  /// CHECK-DAG:                Return [<<Const>>]
+  public static boolean classTypeTestUnrelated() {
+    return $inline$classTypeTest(unrelatedField);
+  }
+
+  /// CHECK-START: boolean Main.classTypeTestFinalUnrelated() register (after)
+  /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+  /// CHECK-DAG:                Return [<<Const>>]
+  public static boolean classTypeTestFinalUnrelated() {
+    return $inline$classTypeTest(finalUnrelatedField);
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestNull() register (after)
+  /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+  /// CHECK-DAG:                Return [<<Const>>]
+  public static boolean interfaceTypeTestNull() {
+    return $inline$interfaceTypeTest(null);
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestExactMain() register (after)
+  /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+  /// CHECK-DAG:                Return [<<Const>>]
+  public static boolean interfaceTypeTestExactMain() {
+    return $inline$interfaceTypeTest(new Main());
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestExactSubMain() register (after)
+  /// CHECK-DAG: <<Const:i\d+>> IntConstant 1
+  /// CHECK-DAG:                Return [<<Const>>]
+  public static boolean interfaceTypeTestExactSubMain() {
+    return $inline$interfaceTypeTest(new SubMain());
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestSubMainOrNull() register (after)
+  /// CHECK-DAG: <<Value:z\d+>> NotEqual
+  /// CHECK-DAG:                Return [<<Value>>]
+  public static boolean interfaceTypeTestSubMainOrNull() {
+    return $inline$interfaceTypeTest(subMain);
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestMainOrNull() register (after)
+  /// CHECK-DAG: <<Value:z\d+>> InstanceOf
+  /// CHECK-DAG:                Return [<<Value>>]
+  public static boolean interfaceTypeTestMainOrNull() {
+    return $inline$interfaceTypeTest(mainField);
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestUnrelated() register (after)
+  /// CHECK-DAG: <<Value:z\d+>> InstanceOf
+  /// CHECK-DAG:                Return [<<Value>>]
+  public static boolean interfaceTypeTestUnrelated() {
+    // This method is the main difference between doing an instanceof on an interface
+    // or a class. We have to keep the instanceof in case a subclass of Unrelated
+    // implements the interface.
+    return $inline$interfaceTypeTest(unrelatedField);
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestFinalUnrelated() register (after)
+  /// CHECK-DAG: <<Const:i\d+>> IntConstant 0
+  /// CHECK-DAG:                Return [<<Const>>]
+  public static boolean interfaceTypeTestFinalUnrelated() {
+    return $inline$interfaceTypeTest(finalUnrelatedField);
+  }
+
+  public static void expect(boolean expected, boolean actual) {
+    if (expected != actual) {
+      throw new Error("Unexpected result");
+    }
+  }
+
+  public static void main(String[] args) {
+    expect(false, classTypeTestNull());
+    expect(false, classTypeTestExactMain());
+    expect(true, classTypeTestExactSubMain());
+
+    subMain = null;
+    expect(false, classTypeTestSubMainOrNull());
+    subMain = new SubMain();
+    expect(true, classTypeTestSubMainOrNull());
+
+    mainField = null;
+    expect(false, classTypeTestMainOrNull());
+    mainField = new Main();
+    expect(false, classTypeTestMainOrNull());
+    mainField = new SubMain();
+    expect(true, classTypeTestMainOrNull());
+
+    unrelatedField = null;
+    expect(false, classTypeTestUnrelated());
+    unrelatedField = new Unrelated();
+    expect(false, classTypeTestUnrelated());
+
+    finalUnrelatedField = null;
+    expect(false, classTypeTestFinalUnrelated());
+    finalUnrelatedField = new FinalUnrelated();
+    expect(false, classTypeTestFinalUnrelated());
+
+    expect(false, interfaceTypeTestNull());
+    expect(false, interfaceTypeTestExactMain());
+    expect(true, interfaceTypeTestExactSubMain());
+
+    subMain = null;
+    expect(false, interfaceTypeTestSubMainOrNull());
+    subMain = new SubMain();
+    expect(true, interfaceTypeTestSubMainOrNull());
+
+    mainField = null;
+    expect(false, interfaceTypeTestMainOrNull());
+    mainField = new Main();
+    expect(false, interfaceTypeTestMainOrNull());
+    mainField = new SubMain();
+    expect(true, interfaceTypeTestMainOrNull());
+
+    unrelatedField = null;
+    expect(false, interfaceTypeTestUnrelated());
+    unrelatedField = new Unrelated();
+    expect(false, interfaceTypeTestUnrelated());
+
+    finalUnrelatedField = null;
+    expect(false, interfaceTypeTestFinalUnrelated());
+    finalUnrelatedField = new FinalUnrelated();
+    expect(false, interfaceTypeTestFinalUnrelated());
+  }
+}
+
+interface Itf {
+}
+
+class SubMain extends Main implements Itf {
+}
+
+class Unrelated {
+}
+
+final class FinalUnrelated {
+}
diff --git a/test/495-checker-checkcast-tests/expected.txt b/test/495-checker-checkcast-tests/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/495-checker-checkcast-tests/expected.txt
diff --git a/test/495-checker-checkcast-tests/info.txt b/test/495-checker-checkcast-tests/info.txt
new file mode 100644
index 0000000..4517b22
--- /dev/null
+++ b/test/495-checker-checkcast-tests/info.txt
@@ -0,0 +1 @@
+Checker tests for optimizations on checkcast.
diff --git a/test/495-checker-checkcast-tests/src/Main.java b/test/495-checker-checkcast-tests/src/Main.java
new file mode 100644
index 0000000..aa6d5a7
--- /dev/null
+++ b/test/495-checker-checkcast-tests/src/Main.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static boolean $inline$classTypeTest(Object o) {
+    return ((SubMain)o) == o;
+  }
+
+  public static boolean $inline$interfaceTypeTest(Object o) {
+    return ((Itf)o) == o;
+  }
+
+  public static SubMain subMain;
+  public static Main mainField;
+  public static Unrelated unrelatedField;
+  public static FinalUnrelated finalUnrelatedField;
+
+  /// CHECK-START: boolean Main.classTypeTestNull() register (after)
+  /// CHECK-NOT: CheckCast
+  public static boolean classTypeTestNull() {
+    return $inline$classTypeTest(null);
+  }
+
+  /// CHECK-START: boolean Main.classTypeTestExactMain() register (after)
+  /// CHECK: CheckCast
+  public static boolean classTypeTestExactMain() {
+    return $inline$classTypeTest(new Main());
+  }
+
+  /// CHECK-START: boolean Main.classTypeTestExactSubMain() register (after)
+  /// CHECK-NOT: CheckCast
+  public static boolean classTypeTestExactSubMain() {
+    return $inline$classTypeTest(new SubMain());
+  }
+
+  /// CHECK-START: boolean Main.classTypeTestSubMainOrNull() register (after)
+  /// CHECK-NOT: CheckCast
+  public static boolean classTypeTestSubMainOrNull() {
+    return $inline$classTypeTest(subMain);
+  }
+
+  /// CHECK-START: boolean Main.classTypeTestMainOrNull() register (after)
+  /// CHECK: CheckCast
+  public static boolean classTypeTestMainOrNull() {
+    return $inline$classTypeTest(mainField);
+  }
+
+  /// CHECK-START: boolean Main.classTypeTestUnrelated() register (after)
+  /// CHECK: CheckCast
+  public static boolean classTypeTestUnrelated() {
+    return $inline$classTypeTest(unrelatedField);
+  }
+
+  /// CHECK-START: boolean Main.classTypeTestFinalUnrelated() register (after)
+  /// CHECK: CheckCast
+  public static boolean classTypeTestFinalUnrelated() {
+    return $inline$classTypeTest(finalUnrelatedField);
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestNull() register (after)
+  /// CHECK-NOT: CheckCast
+  public static boolean interfaceTypeTestNull() {
+    return $inline$interfaceTypeTest(null);
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestExactMain() register (after)
+  /// CHECK: CheckCast
+  public static boolean interfaceTypeTestExactMain() {
+    return $inline$interfaceTypeTest(new Main());
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestExactSubMain() register (after)
+  /// CHECK-NOT: CheckCast
+  public static boolean interfaceTypeTestExactSubMain() {
+    return $inline$interfaceTypeTest(new SubMain());
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestSubMainOrNull() register (after)
+  /// CHECK-NOT: CheckCast
+  public static boolean interfaceTypeTestSubMainOrNull() {
+    return $inline$interfaceTypeTest(subMain);
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestMainOrNull() register (after)
+  /// CHECK: CheckCast
+  public static boolean interfaceTypeTestMainOrNull() {
+    return $inline$interfaceTypeTest(mainField);
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestUnrelated() register (after)
+  /// CHECK: CheckCast
+  public static boolean interfaceTypeTestUnrelated() {
+    return $inline$interfaceTypeTest(unrelatedField);
+  }
+
+  /// CHECK-START: boolean Main.interfaceTypeTestFinalUnrelated() register (after)
+  /// CHECK: CheckCast
+  public static boolean interfaceTypeTestFinalUnrelated() {
+    return $inline$interfaceTypeTest(finalUnrelatedField);
+  }
+
+  public static void main(String[] args) {
+    classTypeTestNull();
+    try {
+      classTypeTestExactMain();
+      throw new Error("ClassCastException expected");
+    } catch (ClassCastException e) {}
+    classTypeTestExactSubMain();
+
+    subMain = null;
+    classTypeTestSubMainOrNull();
+    subMain = new SubMain();
+    classTypeTestSubMainOrNull();
+
+    mainField = null;
+    classTypeTestMainOrNull();
+    mainField = new Main();
+    try {
+      classTypeTestMainOrNull();
+      throw new Error("ClassCastException expected");
+    } catch (ClassCastException e) {}
+    mainField = new SubMain();
+    classTypeTestMainOrNull();
+
+    unrelatedField = null;
+    classTypeTestUnrelated();
+    unrelatedField = new Unrelated();
+    try {
+      classTypeTestUnrelated();
+      throw new Error("ClassCastException expected");
+    } catch (ClassCastException e) {}
+
+    finalUnrelatedField = null;
+    classTypeTestFinalUnrelated();
+    finalUnrelatedField = new FinalUnrelated();
+    try {
+      classTypeTestFinalUnrelated();
+      throw new Error("ClassCastException expected");
+    } catch (ClassCastException e) {}
+
+    interfaceTypeTestNull();
+    try {
+      interfaceTypeTestExactMain();
+      throw new Error("ClassCastException expected");
+    } catch (ClassCastException e) {}
+    interfaceTypeTestExactSubMain();
+
+    subMain = null;
+    interfaceTypeTestSubMainOrNull();
+    subMain = new SubMain();
+    interfaceTypeTestSubMainOrNull();
+
+    mainField = null;
+    interfaceTypeTestMainOrNull();
+    mainField = new Main();
+    try {
+      interfaceTypeTestMainOrNull();
+      throw new Error("ClassCastException expected");
+    } catch (ClassCastException e) {}
+    mainField = new SubMain();
+    interfaceTypeTestMainOrNull();
+
+    unrelatedField = null;
+    interfaceTypeTestUnrelated();
+    unrelatedField = new Unrelated();
+    try {
+      interfaceTypeTestUnrelated();
+      throw new Error("ClassCastException expected");
+    } catch (ClassCastException e) {}
+
+    finalUnrelatedField = null;
+    interfaceTypeTestFinalUnrelated();
+    finalUnrelatedField = new FinalUnrelated();
+    try {
+      interfaceTypeTestFinalUnrelated();
+      throw new Error("ClassCastException expected");
+    } catch (ClassCastException e) {}
+  }
+}
+
+interface Itf {
+}
+
+class SubMain extends Main implements Itf {
+}
+
+class Unrelated {
+}
+
+final class FinalUnrelated {
+}