Revert "More detailed enum unboxing diagnostics for debugging"

This reverts commit 23964b1f670c52c715dd0533594154aedaf21fac.

Reason for revert: Failures

Change-Id: Ibbb087d255f7c730761b37df2f9a49c010f2de88
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index 58862d7..6ab995d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -48,10 +48,6 @@
     return getReference().asMethodReference();
   }
 
-  public DexType getParameter(int index) {
-    return getReference().getParameter(index);
-  }
-
   public DexTypeList getParameters() {
     return getReference().getParameters();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 6fe8c6f..5793ec1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -25,7 +25,6 @@
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.conversion.DexBuilder;
-import com.android.tools.r8.utils.BooleanUtils;
 import java.util.List;
 import java.util.Set;
 import org.objectweb.asm.Opcodes;
@@ -161,11 +160,6 @@
     return arguments().get(index);
   }
 
-  public Value getArgumentForParameter(int index) {
-    int offset = BooleanUtils.intValue(!isInvokeStatic());
-    return getArgument(index + offset);
-  }
-
   public Value getFirstArgument() {
     return getArgument(0);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 3a65557..b637199 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -77,13 +77,6 @@
 import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldOrdinalData;
 import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldUnknownData;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason;
-import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.IllegalInvokeWithImpreciseParameterTypeReason;
-import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingContentsForEnumValuesArrayReason;
-import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingEnumStaticFieldValuesReason;
-import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingInstanceFieldValueForEnumInstanceReason;
-import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.MissingObjectStateForEnumInstanceReason;
-import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedInstanceFieldValueForEnumInstanceReason;
-import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedLibraryInvokeReason;
 import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
@@ -91,6 +84,7 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
 import com.android.tools.r8.shaking.KeepInfoCollection;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
@@ -344,7 +338,7 @@
         if (singleTarget != null && singleTarget.getReference() == factory.enumMembers.valueOf) {
           // The name data is required for the correct mapping from the enum name to the ordinal in
           // the valueOf utility method.
-          addRequiredNameData(enumClass);
+          addRequiredNameData(enumType);
           continue;
         }
       }
@@ -354,9 +348,9 @@
     eligibleEnums.add(enumType);
   }
 
-  private void addRequiredNameData(DexProgramClass enumClass) {
+  private void addRequiredNameData(DexType enumType) {
     enumUnboxingCandidatesInfo.addRequiredEnumInstanceFieldData(
-        enumClass, factory.enumMembers.nameField);
+        enumType, factory.enumMembers.nameField);
   }
 
   private boolean isUnboxableNameMethod(DexMethod method) {
@@ -513,9 +507,10 @@
   private EnumDataMap analyzeEnumInstances() {
     ImmutableMap.Builder<DexType, EnumData> builder = ImmutableMap.builder();
     enumUnboxingCandidatesInfo.forEachCandidateAndRequiredInstanceFieldData(
-        (enumClass, instanceFields) -> {
-          EnumData data = buildData(enumClass, instanceFields);
+        (enumClass, fields) -> {
+          EnumData data = buildData(enumClass, fields);
           if (data == null) {
+            markEnumAsUnboxable(Reason.MISSING_INSTANCE_FIELD_DATA, enumClass);
             return;
           }
           if (!debugLogEnabled || !debugLogs.containsKey(enumClass.getType())) {
@@ -526,7 +521,7 @@
     return new EnumDataMap(builder.build());
   }
 
-  private EnumData buildData(DexProgramClass enumClass, Set<DexField> instanceFields) {
+  private EnumData buildData(DexProgramClass enumClass, Set<DexField> fields) {
     // This map holds all the accessible fields to their unboxed value, so we can remap the field
     // read to the unboxed value.
     ImmutableMap.Builder<DexField, Integer> unboxedValues = ImmutableMap.builder();
@@ -540,7 +535,6 @@
 
     EnumStaticFieldValues enumStaticFieldValues = staticFieldValuesMap.get(enumClass.type);
     if (enumStaticFieldValues == null) {
-      reportFailure(enumClass, new MissingEnumStaticFieldValuesReason());
       return null;
     }
 
@@ -556,16 +550,10 @@
             continue;
           }
           // We could not track the content of that field. We bail out.
-          reportFailure(
-              enumClass, new MissingObjectStateForEnumInstanceReason(staticField.getReference()));
           return null;
         }
         OptionalInt optionalOrdinal = getOrdinal(enumState);
         if (!optionalOrdinal.isPresent()) {
-          reportFailure(
-              enumClass,
-              new MissingInstanceFieldValueForEnumInstanceReason(
-                  staticField.getReference(), factory.enumMembers.ordinalField));
           return null;
         }
         int ordinal = optionalOrdinal.getAsInt();
@@ -582,8 +570,6 @@
           // We could not track the content of that field. We bail out.
           // We could not track the content of that field, and the field could be a values field.
           // We conservatively bail out.
-          reportFailure(
-              enumClass, new MissingContentsForEnumValuesArrayReason(staticField.getReference()));
           return null;
         }
         assert valuesState.isEnumValuesObjectState();
@@ -614,41 +600,25 @@
     // The ordinalToObjectState map may have holes at this point, if some enum instances are never
     // used ($VALUES unused or removed, and enum instance field unused or removed), it contains
     // only data for reachable enum instance, that is what we're interested in.
-    ImmutableMap<DexField, EnumInstanceFieldKnownData> instanceFieldsData =
-        computeRequiredEnumInstanceFieldsData(enumClass, instanceFields, ordinalToObjectState);
-    if (instanceFieldsData == null) {
-      return null;
+    ImmutableMap.Builder<DexField, EnumInstanceFieldKnownData> instanceFieldBuilder =
+        ImmutableMap.builder();
+    for (DexField instanceField : fields) {
+      EnumInstanceFieldData fieldData =
+          computeEnumFieldData(instanceField, enumClass, ordinalToObjectState);
+      if (fieldData.isUnknown()) {
+        return null;
+      }
+      instanceFieldBuilder.put(instanceField, fieldData.asEnumFieldKnownData());
     }
 
     return new EnumData(
-        instanceFieldsData,
+        instanceFieldBuilder.build(),
         unboxedValues.build(),
         valuesField.build(),
         valuesContents == null ? EnumData.INVALID_VALUES_SIZE : valuesContents.getEnumValuesSize());
   }
 
-  private ImmutableMap<DexField, EnumInstanceFieldKnownData> computeRequiredEnumInstanceFieldsData(
-      DexProgramClass enumClass,
-      Set<DexField> instanceFields,
-      Int2ReferenceMap<ObjectState> ordinalToObjectState) {
-    ImmutableMap.Builder<DexField, EnumInstanceFieldKnownData> builder = ImmutableMap.builder();
-    for (DexField instanceField : instanceFields) {
-      EnumInstanceFieldData fieldData =
-          computeRequiredEnumInstanceFieldData(instanceField, enumClass, ordinalToObjectState);
-      if (fieldData.isUnknown()) {
-        if (!debugLogEnabled) {
-          return null;
-        }
-        builder = null;
-      }
-      if (builder != null) {
-        builder.put(instanceField, fieldData.asEnumFieldKnownData());
-      }
-    }
-    return builder != null ? builder.build() : null;
-  }
-
-  private EnumInstanceFieldData computeRequiredEnumInstanceFieldData(
+  private EnumInstanceFieldData computeEnumFieldData(
       DexField instanceField,
       DexProgramClass enumClass,
       Int2ReferenceMap<ObjectState> ordinalToObjectState) {
@@ -661,15 +631,7 @@
     for (Integer ordinal : ordinalToObjectState.keySet()) {
       ObjectState state = ordinalToObjectState.get(ordinal);
       AbstractValue fieldValue = state.getAbstractFieldValue(encodedInstanceField);
-      if (!fieldValue.isSingleValue()) {
-        reportFailure(
-            enumClass, new MissingInstanceFieldValueForEnumInstanceReason(ordinal, instanceField));
-        return EnumInstanceFieldUnknownData.getInstance();
-      }
       if (!(fieldValue.isSingleNumberValue() || fieldValue.isSingleStringValue())) {
-        reportFailure(
-            enumClass,
-            new UnsupportedInstanceFieldValueForEnumInstanceReason(ordinal, instanceField));
         return EnumInstanceFieldUnknownData.getInstance();
       }
       data.put(ordinalToUnboxedInt(ordinal), fieldValue);
@@ -1134,7 +1096,7 @@
       Value enumValue) {
     assert instanceGet.getField().holder == enumClass.type;
     DexField field = instanceGet.getField();
-    enumUnboxingCandidatesInfo.addRequiredEnumInstanceFieldData(enumClass, field);
+    enumUnboxingCandidatesInfo.addRequiredEnumInstanceFieldData(enumClass.type, field);
     return Reason.ELIGIBLE;
   }
 
@@ -1156,11 +1118,10 @@
     if (singleTarget == null) {
       return Reason.INVALID_INVOKE;
     }
-    DexMethod singleTargetReference = singleTarget.getReference();
-    DexClass targetHolder = singleTarget.getHolder();
-    if (targetHolder.isProgramClass()) {
-      if (targetHolder.isEnum() && singleTarget.getDefinition().isInstanceInitializer()) {
-        if (code.context().getHolder() == targetHolder && code.method().isClassInitializer()) {
+    DexClass dexClass = singleTarget.getHolder();
+    if (dexClass.isProgramClass()) {
+      if (dexClass.isEnum() && singleTarget.getDefinition().isInstanceInitializer()) {
+        if (code.method().getHolderType() == dexClass.type && code.method().isClassInitializer()) {
           // The enum instance initializer is allowed to be called only from the enum clinit.
           return Reason.ELIGIBLE;
         } else {
@@ -1169,28 +1130,28 @@
       }
       // Check that the enum-value only flows into parameters whose type exactly matches the
       // enum's type.
-      for (int i = 0; i < singleTarget.getParameters().size(); i++) {
-        if (invoke.getArgumentForParameter(i) == enumValue
-            && singleTarget.getParameter(i).toBaseType(factory) != enumClass.getType()) {
-          return new IllegalInvokeWithImpreciseParameterTypeReason(singleTargetReference);
+      int offset = BooleanUtils.intValue(!singleTarget.getDefinition().isStatic());
+      for (int i = 0; i < singleTarget.getReference().getParameters().size(); i++) {
+        if (invoke.getArgument(offset + i) == enumValue) {
+          if (singleTarget.getReference().getParameter(i).toBaseType(factory) != enumClass.type) {
+            return Reason.GENERIC_INVOKE;
+          }
         }
       }
       if (invoke.isInvokeMethodWithReceiver()) {
         Value receiver = invoke.asInvokeMethodWithReceiver().getReceiver();
-        if (receiver == enumValue && targetHolder.isInterface()) {
+        if (receiver == enumValue && dexClass.isInterface()) {
           return Reason.DEFAULT_METHOD_INVOKE;
         }
       }
       return Reason.ELIGIBLE;
     }
-
-    if (targetHolder.isClasspathClass()) {
-      return Reason.INVALID_INVOKE_CLASSPATH;
+    if (dexClass.isClasspathClass()) {
+      return Reason.INVALID_INVOKE;
     }
-
-    assert targetHolder.isLibraryClass();
-
-    if (targetHolder.getType() != factory.enumType) {
+    assert dexClass.isLibraryClass();
+    DexMethod singleTargetReference = singleTarget.getReference();
+    if (dexClass.type != factory.enumType) {
       // System.identityHashCode(Object) is supported for proto enums.
       // Object#getClass without outValue and Objects.requireNonNull are supported since R8
       // rewrites explicit null checks to such instructions.
@@ -1198,7 +1159,7 @@
         return Reason.ELIGIBLE;
       }
       if (singleTargetReference == factory.stringMembers.valueOf) {
-        addRequiredNameData(enumClass);
+        addRequiredNameData(enumClass.type);
         return Reason.ELIGIBLE;
       }
       if (singleTargetReference == factory.objectMembers.getClass
@@ -1210,7 +1171,7 @@
           || singleTargetReference == factory.objectsMethods.requireNonNullWithMessage) {
         return Reason.ELIGIBLE;
       }
-      return new UnsupportedLibraryInvokeReason(singleTargetReference);
+      return Reason.UNSUPPORTED_LIBRARY_CALL;
     }
     // TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
     if (singleTargetReference == factory.enumMembers.compareTo) {
@@ -1220,7 +1181,7 @@
     } else if (singleTargetReference == factory.enumMembers.nameMethod
         || singleTargetReference == factory.enumMembers.toString) {
       assert invoke.asInvokeMethodWithReceiver().getReceiver() == enumValue;
-      addRequiredNameData(enumClass);
+      addRequiredNameData(enumClass.type);
       return Reason.ELIGIBLE;
     } else if (singleTargetReference == factory.enumMembers.ordinalMethod) {
       return Reason.ELIGIBLE;
@@ -1228,11 +1189,12 @@
       return Reason.ELIGIBLE;
     } else if (singleTargetReference == factory.enumMembers.constructor) {
       // Enum constructor call is allowed only if called from an enum initializer.
-      if (code.method().isInstanceInitializer() && code.context().getHolder() == enumClass) {
+      if (code.method().isInstanceInitializer()
+          && code.method().getHolderType() == enumClass.type) {
         return Reason.ELIGIBLE;
       }
     }
-    return new UnsupportedLibraryInvokeReason(singleTargetReference);
+    return Reason.UNSUPPORTED_LIBRARY_CALL;
   }
 
   // Return is used for valueOf methods.
@@ -1309,27 +1271,24 @@
     sb.append(System.lineSeparator());
 
     // Print information about how often a given Reason kind prevents enum unboxing.
-    Object2IntMap<Object> reasonKindCount = new Object2IntOpenHashMap<>();
+    Object2IntMap<Reason> reasonCount = new Object2IntOpenHashMap<>();
     debugLogs.forEach(
         (type, reasons) ->
-            reasons.forEach(
-                reason ->
-                    reasonKindCount.put(reason.getKind(), reasonKindCount.getInt(reason) + 1)));
-    List<Object> differentReasonKinds = new ArrayList<>(reasonKindCount.keySet());
-    differentReasonKinds.sort(
-        (reasonKind, other) -> {
-          int freq = reasonKindCount.getInt(reasonKind) - reasonKindCount.getInt(other);
-          return freq != 0
-              ? freq
-              : System.identityHashCode(reasonKind) - System.identityHashCode(other);
+            reasons.forEach(reason -> reasonCount.put(reason, reasonCount.getInt(reason) + 1)));
+    List<Reason> differentReasons = new ArrayList<>(reasonCount.keySet());
+    differentReasons.sort(
+        (x, y) -> {
+          int freq = reasonCount.getInt(x) - reasonCount.getInt(y);
+          return freq != 0 ? freq : System.identityHashCode(x) - System.identityHashCode(y);
         });
-    differentReasonKinds.forEach(
-        reasonKind ->
-            sb.append(reasonKind)
-                .append(" (")
-                .append(reasonKindCount.getInt(reasonKind))
-                .append(")")
-                .append(System.lineSeparator()));
+    differentReasons.forEach(
+        x -> {
+          sb.append(x)
+              .append(" (")
+              .append(reasonCount.getInt(x))
+              .append(")")
+              .append(System.lineSeparator());
+        });
 
     reporter.info(new StringDiagnostic(sb.toString()));
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index 62b05a9..c45e71a9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.optimize.enums.eligibility.Reason;
-import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedStaticFieldReason;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.KeepInfoCollection;
 
@@ -71,6 +70,9 @@
       result = false;
     }
     if (!enumHasBasicStaticFields(clazz)) {
+      if (!enumUnboxer.reportFailure(clazz, Reason.UNEXPECTED_STATIC_FIELD)) {
+        return false;
+      }
       result = false;
     }
     return result;
@@ -79,37 +81,32 @@
   // The enum should have the $VALUES static field and only fields directly referencing the enum
   // instances.
   private boolean enumHasBasicStaticFields(DexProgramClass clazz) {
-    boolean result = true;
     for (DexEncodedField staticField : clazz.staticFields()) {
-      if (isEnumField(staticField, clazz)) {
+      if (isEnumField(staticField, clazz.type)) {
         // Enum field, valid, do nothing.
-      } else if (matchesValuesField(staticField, clazz, factory)) {
+      } else if (matchesValuesField(staticField, clazz.type, factory)) {
         // Field $VALUES, valid, do nothing.
       } else if (appView.appInfo().isFieldRead(staticField)) {
         // Only non read static fields are valid, and they are assumed unused.
-        if (!enumUnboxer.reportFailure(
-            clazz, new UnsupportedStaticFieldReason(staticField.getReference()))) {
-          return false;
-        }
-        result = false;
+        return false;
       }
     }
-    return result;
+    return true;
   }
 
-  static boolean isEnumField(DexEncodedField staticField, DexProgramClass enumClass) {
-    return staticField.getType() == enumClass.getType()
-        && staticField.isEnum()
-        && staticField.isFinal();
+  static boolean isEnumField(DexEncodedField staticField, DexType enumType) {
+    return staticField.getReference().type == enumType
+        && staticField.accessFlags.isEnum()
+        && staticField.accessFlags.isFinal();
   }
 
   static boolean matchesValuesField(
-      DexEncodedField staticField, DexProgramClass enumClass, DexItemFactory factory) {
-    return staticField.getType().isArrayType()
-        && staticField.getType().toArrayElementType(factory) == enumClass.getType()
-        && staticField.isSynthetic()
-        && staticField.isFinal()
-        && staticField.getName() == factory.enumValuesFieldName;
+      DexEncodedField staticField, DexType enumType, DexItemFactory factory) {
+    return staticField.getReference().type.isArrayType()
+        && staticField.getReference().type.toArrayElementType(factory) == enumType
+        && staticField.accessFlags.isSynthetic()
+        && staticField.accessFlags.isFinal()
+        && staticField.getReference().name == factory.enumValuesFieldName;
   }
 
   private void removeEnumsInAnnotations() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
index 7d85b8d..f36474d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
@@ -81,11 +81,11 @@
     info.addMethodDependency(programMethod);
   }
 
-  public void addRequiredEnumInstanceFieldData(DexProgramClass enumClass, DexField field) {
+  public void addRequiredEnumInstanceFieldData(DexType enumType, DexField field) {
     // The enumType may be removed concurrently map from enumTypeToInfo. It means in that
     // case the enum is no longer a candidate, and dependencies don't need to be recorded
     // anymore.
-    EnumUnboxingCandidateInfo info = enumTypeToInfo.get(enumClass.getType());
+    EnumUnboxingCandidateInfo info = enumTypeToInfo.get(enumType);
     if (info == null) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
index 5aef5cc..2cd2098 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/eligibility/Reason.java
@@ -4,238 +4,46 @@
 
 package com.android.tools.r8.ir.optimize.enums.eligibility;
 
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.google.common.collect.ImmutableList;
-
-public abstract class Reason {
-  public static final Reason ELIGIBLE = new StringReason("ELIGIBLE");
-  public static final Reason ACCESSIBILITY = new StringReason("ACCESSIBILITY");
-  public static final Reason ANNOTATION = new StringReason("ANNOTATION");
-  public static final Reason PINNED = new StringReason("PINNED");
-  public static final Reason DOWN_CAST = new StringReason("DOWN_CAST");
-  public static final Reason SUBTYPES = new StringReason("SUBTYPES");
-  public static final Reason MANY_INSTANCE_FIELDS = new StringReason("MANY_INSTANCE_FIELDS");
-  public static final Reason DEFAULT_METHOD_INVOKE = new StringReason("DEFAULT_METHOD_INVOKE");
-  public static final Reason UNRESOLVABLE_FIELD = new StringReason("UNRESOLVABLE_FIELD");
-  public static final Reason CONST_CLASS = new StringReason("CONST_CLASS");
-  public static final Reason INVALID_PHI = new StringReason("INVALID_PHI");
-  public static final Reason NO_INIT = new StringReason("NO_INIT");
-  public static final Reason INVALID_INIT = new StringReason("INVALID_INIT");
-  public static final Reason INVALID_CLINIT = new StringReason("INVALID_CLINIT");
-  public static final Reason INVALID_INVOKE = new StringReason("INVALID_INVOKE");
-  public static final Reason INVALID_INVOKE_CLASSPATH =
-      new StringReason("INVALID_INVOKE_CLASSPATH");
-  public static final Reason INVALID_INVOKE_ON_ARRAY = new StringReason("INVALID_INVOKE_ON_ARRAY");
-  public static final Reason IMPLICIT_UP_CAST_IN_RETURN =
-      new StringReason("IMPLICIT_UP_CAST_IN_RETURN");
+public class Reason {
+  public static final Reason ELIGIBLE = new Reason("ELIGIBLE");
+  public static final Reason ACCESSIBILITY = new Reason("ACCESSIBILITY");
+  public static final Reason ANNOTATION = new Reason("ANNOTATION");
+  public static final Reason PINNED = new Reason("PINNED");
+  public static final Reason DOWN_CAST = new Reason("DOWN_CAST");
+  public static final Reason SUBTYPES = new Reason("SUBTYPES");
+  public static final Reason MANY_INSTANCE_FIELDS = new Reason("MANY_INSTANCE_FIELDS");
+  public static final Reason GENERIC_INVOKE = new Reason("GENERIC_INVOKE");
+  public static final Reason DEFAULT_METHOD_INVOKE = new Reason("DEFAULT_METHOD_INVOKE");
+  public static final Reason UNEXPECTED_STATIC_FIELD = new Reason("UNEXPECTED_STATIC_FIELD");
+  public static final Reason UNRESOLVABLE_FIELD = new Reason("UNRESOLVABLE_FIELD");
+  public static final Reason CONST_CLASS = new Reason("CONST_CLASS");
+  public static final Reason INVALID_PHI = new Reason("INVALID_PHI");
+  public static final Reason NO_INIT = new Reason("NO_INIT");
+  public static final Reason INVALID_INIT = new Reason("INVALID_INIT");
+  public static final Reason INVALID_CLINIT = new Reason("INVALID_CLINIT");
+  public static final Reason INVALID_INVOKE = new Reason("INVALID_INVOKE");
+  public static final Reason INVALID_INVOKE_ON_ARRAY = new Reason("INVALID_INVOKE_ON_ARRAY");
+  public static final Reason IMPLICIT_UP_CAST_IN_RETURN = new Reason("IMPLICIT_UP_CAST_IN_RETURN");
+  public static final Reason UNSUPPORTED_LIBRARY_CALL = new Reason("UNSUPPORTED_LIBRARY_CALL");
   public static final Reason MISSING_INSTANCE_FIELD_DATA =
-      new StringReason("MISSING_INSTANCE_FIELD_DATA");
-  public static final Reason INVALID_FIELD_PUT = new StringReason("INVALID_FIELD_PUT");
-  public static final Reason INVALID_ARRAY_PUT = new StringReason("INVALID_ARRAY_PUT");
-  public static final Reason TYPE_MISMATCH_FIELD_PUT = new StringReason("TYPE_MISMATCH_FIELD_PUT");
-  public static final Reason INVALID_IF_TYPES = new StringReason("INVALID_IF_TYPES");
+      new Reason("MISSING_INSTANCE_FIELD_DATA");
+  public static final Reason INVALID_FIELD_PUT = new Reason("INVALID_FIELD_PUT");
+  public static final Reason INVALID_ARRAY_PUT = new Reason("INVALID_ARRAY_PUT");
+  public static final Reason TYPE_MISMATCH_FIELD_PUT = new Reason("TYPE_MISMATCH_FIELD_PUT");
+  public static final Reason INVALID_IF_TYPES = new Reason("INVALID_IF_TYPES");
   public static final Reason ENUM_METHOD_CALLED_WITH_NULL_RECEIVER =
-      new StringReason("ENUM_METHOD_CALLED_WITH_NULL_RECEIVER");
+      new Reason("ENUM_METHOD_CALLED_WITH_NULL_RECEIVER");
   public static final Reason OTHER_UNSUPPORTED_INSTRUCTION =
-      new StringReason("OTHER_UNSUPPORTED_INSTRUCTION");
+      new Reason("OTHER_UNSUPPORTED_INSTRUCTION");
 
-  public abstract Object getKind();
+  private final String message;
+
+  public Reason(String message) {
+    this.message = message;
+  }
 
   @Override
-  public abstract String toString();
-
-  public static class StringReason extends Reason {
-
-    private final String message;
-
-    public StringReason(String message) {
-      this.message = message;
-    }
-
-    @Override
-    public Object getKind() {
-      return this;
-    }
-
-    @Override
-    public String toString() {
-      return message;
-    }
-  }
-
-  public static class IllegalInvokeWithImpreciseParameterTypeReason extends Reason {
-
-    private final DexMethod invokedMethod;
-
-    public IllegalInvokeWithImpreciseParameterTypeReason(DexMethod invokedMethod) {
-      this.invokedMethod = invokedMethod;
-    }
-
-    @Override
-    public Object getKind() {
-      return getClass();
-    }
-
-    @Override
-    public String toString() {
-      return "IllegalInvokeWithImpreciseParameterType(" + invokedMethod.toSourceString() + ")";
-    }
-  }
-
-  public static class MissingEnumStaticFieldValuesReason extends Reason {
-
-    @Override
-    public Object getKind() {
-      return getClass();
-    }
-
-    @Override
-    public String toString() {
-      return "MissingEnumStaticFieldValues";
-    }
-  }
-
-  public static class MissingContentsForEnumValuesArrayReason extends Reason {
-
-    private final DexField valuesField;
-
-    public MissingContentsForEnumValuesArrayReason(DexField valuesField) {
-      this.valuesField = valuesField;
-    }
-
-    @Override
-    public Object getKind() {
-      return getClass();
-    }
-
-    @Override
-    public String toString() {
-      return "MissingContentsForEnumValuesArray(" + valuesField.toSourceString() + ")";
-    }
-  }
-
-  public static class MissingInstanceFieldValueForEnumInstanceReason extends Reason {
-
-    private final DexField enumField;
-    private final int ordinal;
-    private final DexField instanceField;
-
-    public MissingInstanceFieldValueForEnumInstanceReason(
-        DexField enumField, DexField instanceField) {
-      this.enumField = enumField;
-      this.ordinal = -1;
-      this.instanceField = instanceField;
-    }
-
-    public MissingInstanceFieldValueForEnumInstanceReason(int ordinal, DexField instanceField) {
-      this.enumField = null;
-      this.ordinal = ordinal;
-      this.instanceField = instanceField;
-    }
-
-    @Override
-    public Object getKind() {
-      return getClass();
-    }
-
-    @Override
-    public String toString() {
-      if (enumField != null) {
-        return "MissingInstanceFieldValueForEnumInstance(enum field="
-            + enumField.toSourceString()
-            + ", instance field="
-            + instanceField.toSourceString()
-            + ")";
-      }
-      assert ordinal >= 0;
-      return "MissingInstanceFieldValueForEnumInstance(ordinal="
-          + ordinal
-          + ", instance field="
-          + instanceField.toSourceString()
-          + ")";
-    }
-  }
-
-  public static class MissingObjectStateForEnumInstanceReason extends Reason {
-
-    private final DexField enumField;
-
-    public MissingObjectStateForEnumInstanceReason(DexField enumField) {
-      this.enumField = enumField;
-    }
-
-    @Override
-    public Object getKind() {
-      return getClass();
-    }
-
-    @Override
-    public String toString() {
-      return "MissingObjectStateForEnumInstance(" + enumField + ")";
-    }
-  }
-
-  public static class UnsupportedInstanceFieldValueForEnumInstanceReason extends Reason {
-
-    private final int ordinal;
-    private final DexField instanceField;
-
-    public UnsupportedInstanceFieldValueForEnumInstanceReason(int ordinal, DexField instanceField) {
-      this.ordinal = ordinal;
-      this.instanceField = instanceField;
-    }
-
-    @Override
-    public Object getKind() {
-      return getClass();
-    }
-
-    @Override
-    public String toString() {
-      return "UnsupportedInstanceFieldValueForEnumInstance(ordinal="
-          + ordinal
-          + ", instance field="
-          + instanceField.toSourceString()
-          + ")";
-    }
-  }
-
-  public static class UnsupportedLibraryInvokeReason extends Reason {
-
-    private final DexMethod invokedMethod;
-
-    public UnsupportedLibraryInvokeReason(DexMethod invokedMethod) {
-      this.invokedMethod = invokedMethod;
-    }
-
-    @Override
-    public Object getKind() {
-      return ImmutableList.of(getClass(), invokedMethod);
-    }
-
-    @Override
-    public String toString() {
-      return "UnsupportedLibraryInvoke(" + invokedMethod.toSourceString() + ")";
-    }
-  }
-
-  public static class UnsupportedStaticFieldReason extends Reason {
-
-    private final DexField field;
-
-    public UnsupportedStaticFieldReason(DexField field) {
-      this.field = field;
-    }
-
-    @Override
-    public Object getKind() {
-      return getClass();
-    }
-
-    @Override
-    public String toString() {
-      return "UnsupportedStaticField(" + field.toSourceString() + ")";
-    }
+  public String toString() {
+    return message;
   }
 }