Replace SyntheticKind enum by a class structure.

Change-Id: Ic9ebd296c3e5581cb6ce7bf7f44c9902b3561247
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 366ad9c..4ff028a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -391,9 +391,7 @@
   public static DexAnnotation createAnnotationSynthesizedClass(
       SyntheticKind kind, DexItemFactory dexItemFactory) {
     DexAnnotationElement kindElement =
-        new DexAnnotationElement(
-            dexItemFactory.kindString,
-            new DexValueString(dexItemFactory.createString(kind.descriptor)));
+        new DexAnnotationElement(dexItemFactory.kindString, DexValueInt.create(kind.getId()));
     DexAnnotationElement[] elements = new DexAnnotationElement[] {kindElement};
     return new DexAnnotation(
         VISIBILITY_BUILD,
@@ -423,12 +421,11 @@
     if (kindElement.name != factory.kindString) {
       return null;
     }
-    if (!kindElement.value.isDexValueString()) {
+    if (!kindElement.value.isDexValueInt()) {
       return null;
     }
     SyntheticKind kind =
-        SyntheticNaming.SyntheticKind.fromDescriptor(
-            kindElement.value.asDexValueString().getValue().toString());
+        SyntheticNaming.SyntheticKind.fromId(kindElement.value.asDexValueInt().getValue());
     return kind;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
index 6636e1e..dab2a94 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
@@ -9,7 +9,7 @@
 public class InterfaceDesugaringForTesting {
 
   public static String getEmulateLibraryClassNameSuffix() {
-    return SyntheticKind.EMULATED_INTERFACE_CLASS.descriptor;
+    return SyntheticKind.EMULATED_INTERFACE_CLASS.getDescriptor();
   }
 
   public static String getCompanionClassNameSuffix() {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
index 574f518..097df5d 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
@@ -70,9 +70,9 @@
       ClassToFeatureSplitMap classToFeatureSplitMap,
       SyntheticItems syntheticItems) {
     HasherWrapper hasher = HasherWrapper.murmur3128Hasher();
-    hasher.putInt(kind.id);
-    if (getKind().isFixedSuffixSynthetic) {
-      // Fixed synthetics are non-shareable. Its unique type is used as the hash key.
+    hasher.putInt(kind.getId());
+    if (!getKind().isShareable()) {
+      // Non-shareable synthetics should use its assumed unique type as the hash.
       getHolder().getType().hash(hasher);
       return hasher.hash();
     }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index fdaaaee..bcf892c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -717,7 +717,7 @@
       AppView<?> appView,
       Predicate<DexType> reserved) {
     DexItemFactory factory = appView.dexItemFactory();
-    if (kind.isFixedSuffixSynthetic) {
+    if (kind.isFixedSuffixSynthetic()) {
       return SyntheticNaming.createExternalType(kind, externalSyntheticTypePrefix, "", factory);
     }
     NumberGenerator generator =
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 7740805..887941c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -190,7 +190,7 @@
     if (clazz != null) {
       assert kind != null;
       assert !baseDefinitionFor.apply(type).hasClassResolutionResult()
-              || kind.mayOverridesNonProgramType
+              || kind.isMayOverridesNonProgramType()
           : "Pending synthetic definition also present in the active program: " + type;
       return clazz;
     }
@@ -524,7 +524,7 @@
 
   public DexProgramClass getExistingFixedClass(
       SyntheticKind kind, DexClass context, AppView<?> appView) {
-    assert kind.isFixedSuffixSynthetic;
+    assert kind.isFixedSuffixSynthetic();
     SynthesizingContext outerContext = internalGetOuterContext(context, appView);
     DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
     DexClass clazz = appView.definitionFor(type);
@@ -553,7 +553,7 @@
       AppView<?> appView,
       Consumer<SyntheticProgramClassBuilder> fn,
       Consumer<DexProgramClass> onCreationConsumer) {
-    assert kind.isFixedSuffixSynthetic;
+    assert kind.isFixedSuffixSynthetic();
     SynthesizingContext outerContext = internalGetOuterContext(context, appView);
     return internalEnsureFixedProgramClass(kind, fn, onCreationConsumer, outerContext, appView);
   }
@@ -832,7 +832,7 @@
         if (!removedClasses.contains(definition.getHolder().getType())) {
           if (definition.isProgramDefinition()) {
             committedProgramTypesBuilder.add(definition.getHolder().getType());
-            if (definition.getKind().mayOverridesNonProgramType) {
+            if (definition.getKind().isMayOverridesNonProgramType()) {
               appBuilder.addProgramClassPotentiallyOverridingNonProgramClass(
                   definition.asProgramDefinition().getHolder());
             } else {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
index d660753..728947e 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
@@ -70,8 +70,8 @@
     protected ByteVector write(
         ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) {
       ByteVector byteVector = new ByteVector();
-      assert 0 <= kind.id && kind.id <= Short.MAX_VALUE;
-      byteVector.putShort(kind.id);
+      assert 0 <= kind.getId() && kind.getId() <= Short.MAX_VALUE;
+      byteVector.putShort(kind.getId());
       return byteVector;
     }
   }
@@ -122,7 +122,7 @@
       return NO_MARKER;
     }
     assert clazz.annotations().size() == 1;
-    if (kind.isSingleSyntheticMethod) {
+    if (kind.isSingleSyntheticMethod()) {
       if (!clazz.interfaces.isEmpty()) {
         return NO_MARKER;
       }
@@ -161,11 +161,11 @@
   }
 
   public boolean isSyntheticMethods() {
-    return kind != null && kind.isSingleSyntheticMethod;
+    return kind != null && kind.isSingleSyntheticMethod();
   }
 
   public boolean isSyntheticClass() {
-    return kind != null && !kind.isSingleSyntheticMethod;
+    return kind != null && !kind.isSingleSyntheticMethod();
   }
 
   public SyntheticKind getKind() {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index ec8a010..d2daa6c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -145,22 +145,21 @@
             .setOptimizationInfo(optimizationInfo)
             .applyIf(!checkAndroidApiLevels, DexEncodedMethod.Builder::disableAndroidApiLevelCheck)
             .build();
-    assert isValidSyntheticMethod(method, syntheticKind);
+    assert !syntheticKind.isSingleSyntheticMethod()
+        || isValidSingleSyntheticMethod(method, syntheticKind);
     return method;
   }
 
   /**
-   * Predicate for what is a "supported" synthetic method.
+   * Predicate for what is a "supported" single synthetic method.
    *
-   * <p>This method is used when identifying synthetic methods in the program input and should be as
-   * narrow as possible.
-   *
-   * <p>Methods in fixed suffix synthetics are identified differently (through the class name) and
-   * can have different properties.
+   * <p>This method is used when identifying single synthetic methods in the program input and
+   * should be as narrow as possible.
    */
-  public static boolean isValidSyntheticMethod(
+  public static boolean isValidSingleSyntheticMethod(
       DexEncodedMethod method, SyntheticKind syntheticKind) {
-    return isValidSingleSyntheticMethod(method) || syntheticKind.isFixedSuffixSynthetic;
+    assert syntheticKind.isSingleSyntheticMethod();
+    return isValidSingleSyntheticMethod(method);
   }
 
   public static boolean isValidSingleSyntheticMethod(DexEncodedMethod method) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
index d00eb21..2129b9d 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
@@ -11,7 +11,7 @@
 import java.util.function.Consumer;
 
 /**
- * Definition of a synthetic method item.
+ * Definition of a single synthetic method item.
  *
  * <p>This class is internal to the synthetic items collection, thus package-protected.
  */
@@ -24,6 +24,7 @@
 
   SyntheticMethodDefinition(SyntheticKind kind, SynthesizingContext context, ProgramMethod method) {
     super(kind, context);
+    assert kind.isSingleSyntheticMethod();
     this.method = method;
   }
 
@@ -70,7 +71,7 @@
 
   @Override
   public boolean isValid() {
-    return SyntheticMethodBuilder.isValidSyntheticMethod(method.getDefinition(), getKind());
+    return SyntheticMethodBuilder.isValidSingleSyntheticMethod(method.getDefinition(), getKind());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 657f7d5..0e80d65 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -9,11 +9,54 @@
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.DescriptorUtils;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import com.android.tools.r8.utils.structural.Equatable;
+import com.android.tools.r8.utils.structural.Ordered;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 public class SyntheticNaming {
 
+  private static class KindGenerator {
+    private int nextId = 1;
+    private List<SyntheticKind> kinds = new ArrayList<>();
+
+    private int getNextId() {
+      return nextId++;
+    }
+
+    private SyntheticKind register(SyntheticKind kind) {
+      kinds.add(kind);
+      return kind;
+    }
+
+    SyntheticKind forSingleMethod(String descriptor) {
+      return register(new SyntheticMethodKind(getNextId(), descriptor));
+    }
+
+    SyntheticKind forInstanceClass(String descriptor) {
+      return register(new SyntheticClassKind(getNextId(), descriptor));
+    }
+
+    SyntheticKind forFixedClass(String descriptor) {
+      return register(new SyntheticFixedClassKind(getNextId(), descriptor, false, false));
+    }
+
+    SyntheticKind forGlobalClass() {
+      return register(new SyntheticFixedClassKind(getNextId(), "", true, true));
+    }
+
+    SyntheticKind forGlobalClasspathClass() {
+      return register(new SyntheticFixedClassKind(getNextId(), "", false, false));
+    }
+
+    List<SyntheticKind> getAllKinds() {
+      List<SyntheticKind> kinds = this.kinds;
+      this.kinds = null;
+      return kinds;
+    }
+  }
+
   /**
    * Enumeration of all kinds of synthetic items.
    *
@@ -23,95 +66,202 @@
    * will be put into any non-minified synthetic name and thus the kind "descriptor" must be a
    * distinct for each kind.
    */
-  public enum SyntheticKind {
-    // Class synthetics.
-    ENUM_UNBOXING_LOCAL_UTILITY_CLASS("$EnumUnboxingLocalUtility", 24, false, true),
-    ENUM_UNBOXING_SHARED_UTILITY_CLASS("$EnumUnboxingSharedUtility", 25, false, true),
-    RECORD_TAG("", 1, false, true, true),
-    COMPANION_CLASS("$-CC", 2, false, true),
-    EMULATED_INTERFACE_CLASS("$-EL", 3, false, true),
-    RETARGET_CLASS("RetargetClass", 20, false, true),
-    RETARGET_STUB("", 36, false, true),
-    RETARGET_INTERFACE("RetargetInterface", 21, false, true),
-    WRAPPER("$Wrapper", 22, false, true),
-    VIVIFIED_WRAPPER("$VivifiedWrapper", 23, false, true),
-    LAMBDA("Lambda", 4, false),
-    INIT_TYPE_ARGUMENT("-IA", 5, false, true),
-    HORIZONTAL_INIT_TYPE_ARGUMENT_1(SYNTHETIC_CLASS_SEPARATOR + "IA$1", 6, false, true),
-    HORIZONTAL_INIT_TYPE_ARGUMENT_2(SYNTHETIC_CLASS_SEPARATOR + "IA$2", 7, false, true),
-    HORIZONTAL_INIT_TYPE_ARGUMENT_3(SYNTHETIC_CLASS_SEPARATOR + "IA$3", 8, false, true),
-    NON_FIXED_INIT_TYPE_ARGUMENT("$IA", 35, false),
-    // Method synthetics.
-    ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD("CheckNotZero", 27, true),
-    RECORD_HELPER("Record", 9, true),
-    BACKPORT("Backport", 10, true),
-    BACKPORT_WITH_FORWARDING("BackportWithForwarding", 34, true),
-    STATIC_INTERFACE_CALL("StaticInterfaceCall", 11, true),
-    TO_STRING_IF_NOT_NULL("ToStringIfNotNull", 12, true),
-    THROW_CCE_IF_NOT_NULL("ThrowCCEIfNotNull", 13, true),
-    THROW_IAE("ThrowIAE", 14, true),
-    THROW_ICCE("ThrowICCE", 15, true),
-    THROW_NSME("ThrowNSME", 16, true),
-    TWR_CLOSE_RESOURCE("TwrCloseResource", 17, true),
-    SERVICE_LOADER("ServiceLoad", 18, true),
-    OUTLINE("Outline", 19, true),
-    API_CONVERSION("APIConversion", 26, true),
-    API_CONVERSION_PARAMETERS("APIConversionParameters", 28, true),
-    EMULATED_INTERFACE_MARKER_CLASS("", 29, false, true, true),
-    CONST_DYNAMIC("$Condy", 30, false),
-    ENUM_CONVERSION("$EnumConversion", 31, false, true),
-    ARRAY_CONVERSION("$ArrayConversion", 37, true, false),
-    API_MODEL_OUTLINE("ApiModelOutline", 32, true, false, false),
-    API_MODEL_STUB("", 33, false, true, true);
+  public abstract static class SyntheticKind implements Ordered<SyntheticKind> {
+
+    public static final SyntheticKind ENUM_UNBOXING_LOCAL_UTILITY_CLASS,
+        ENUM_UNBOXING_SHARED_UTILITY_CLASS,
+        RECORD_TAG,
+        COMPANION_CLASS,
+        EMULATED_INTERFACE_CLASS,
+        RETARGET_CLASS,
+        RETARGET_STUB,
+        RETARGET_INTERFACE,
+        WRAPPER,
+        VIVIFIED_WRAPPER,
+        LAMBDA,
+        INIT_TYPE_ARGUMENT,
+        HORIZONTAL_INIT_TYPE_ARGUMENT_1,
+        HORIZONTAL_INIT_TYPE_ARGUMENT_2,
+        HORIZONTAL_INIT_TYPE_ARGUMENT_3,
+        NON_FIXED_INIT_TYPE_ARGUMENT,
+        ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD,
+        RECORD_HELPER,
+        BACKPORT,
+        BACKPORT_WITH_FORWARDING,
+        STATIC_INTERFACE_CALL,
+        TO_STRING_IF_NOT_NULL,
+        THROW_CCE_IF_NOT_NULL,
+        THROW_IAE,
+        THROW_ICCE,
+        THROW_NSME,
+        TWR_CLOSE_RESOURCE,
+        SERVICE_LOADER,
+        OUTLINE,
+        API_CONVERSION,
+        API_CONVERSION_PARAMETERS,
+        EMULATED_INTERFACE_MARKER_CLASS,
+        CONST_DYNAMIC,
+        ENUM_CONVERSION,
+        ARRAY_CONVERSION,
+        API_MODEL_OUTLINE,
+        API_MODEL_STUB;
+
+    private static final List<SyntheticKind> ALL_KINDS;
 
     static {
-      assert verifyNoOverlappingIds();
+      KindGenerator generator = new KindGenerator();
+      // Global synthetics.
+      RECORD_TAG = generator.forGlobalClass();
+      API_MODEL_STUB = generator.forGlobalClass();
+
+      // Classpath only synthetics in the global type namespace.
+      RETARGET_STUB = generator.forGlobalClasspathClass();
+      EMULATED_INTERFACE_MARKER_CLASS = generator.forGlobalClasspathClass();
+
+      // Fixed suffix synthetics. Each has a hygienic prefix type.
+      ENUM_UNBOXING_LOCAL_UTILITY_CLASS = generator.forFixedClass("$EnumUnboxingLocalUtility");
+      ENUM_UNBOXING_SHARED_UTILITY_CLASS = generator.forFixedClass("$EnumUnboxingSharedUtility");
+      COMPANION_CLASS = generator.forFixedClass("$-CC");
+      EMULATED_INTERFACE_CLASS = generator.forFixedClass("$-EL");
+      RETARGET_CLASS = generator.forFixedClass("RetargetClass");
+      RETARGET_INTERFACE = generator.forFixedClass("RetargetInterface");
+      WRAPPER = generator.forFixedClass("$Wrapper");
+      VIVIFIED_WRAPPER = generator.forFixedClass("$VivifiedWrapper");
+      INIT_TYPE_ARGUMENT = generator.forFixedClass("-IA");
+      HORIZONTAL_INIT_TYPE_ARGUMENT_1 = generator.forFixedClass(SYNTHETIC_CLASS_SEPARATOR + "IA$1");
+      HORIZONTAL_INIT_TYPE_ARGUMENT_2 = generator.forFixedClass(SYNTHETIC_CLASS_SEPARATOR + "IA$2");
+      HORIZONTAL_INIT_TYPE_ARGUMENT_3 = generator.forFixedClass(SYNTHETIC_CLASS_SEPARATOR + "IA$3");
+      ENUM_CONVERSION = generator.forFixedClass("$EnumConversion");
+
+      // Locally generated synthetic classes.
+      LAMBDA = generator.forInstanceClass("Lambda");
+      NON_FIXED_INIT_TYPE_ARGUMENT = generator.forInstanceClass("$IA");
+      CONST_DYNAMIC = generator.forInstanceClass("$Condy");
+
+      // Method synthetics.
+      ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD = generator.forSingleMethod("CheckNotZero");
+      RECORD_HELPER = generator.forSingleMethod("Record");
+      BACKPORT = generator.forSingleMethod("Backport");
+      BACKPORT_WITH_FORWARDING = generator.forSingleMethod("BackportWithForwarding");
+      STATIC_INTERFACE_CALL = generator.forSingleMethod("StaticInterfaceCall");
+      TO_STRING_IF_NOT_NULL = generator.forSingleMethod("ToStringIfNotNull");
+      THROW_CCE_IF_NOT_NULL = generator.forSingleMethod("ThrowCCEIfNotNull");
+      THROW_IAE = generator.forSingleMethod("ThrowIAE");
+      THROW_ICCE = generator.forSingleMethod("ThrowICCE");
+      THROW_NSME = generator.forSingleMethod("ThrowNSME");
+      TWR_CLOSE_RESOURCE = generator.forSingleMethod("TwrCloseResource");
+      SERVICE_LOADER = generator.forSingleMethod("ServiceLoad");
+      OUTLINE = generator.forSingleMethod("Outline");
+      API_CONVERSION = generator.forSingleMethod("APIConversion");
+      API_CONVERSION_PARAMETERS = generator.forSingleMethod("APIConversionParameters");
+      ARRAY_CONVERSION = generator.forSingleMethod("$ArrayConversion");
+      API_MODEL_OUTLINE = generator.forSingleMethod("ApiModelOutline");
+
+      ALL_KINDS = generator.getAllKinds();
     }
 
-    public final String descriptor;
-    public final int id;
-    public final boolean isSingleSyntheticMethod;
-    public final boolean isFixedSuffixSynthetic;
-    public final boolean mayOverridesNonProgramType;
+    private final int id;
+    private final String descriptor;
 
-    SyntheticKind(String descriptor, int id, boolean isSingleSyntheticMethod) {
-      this(descriptor, id, isSingleSyntheticMethod, false);
-    }
-
-    SyntheticKind(
-        String descriptor,
-        int id,
-        boolean isSingleSyntheticMethod,
-        boolean isFixedSuffixSynthetic) {
-      this(descriptor, id, isSingleSyntheticMethod, isFixedSuffixSynthetic, false);
-    }
-
-    SyntheticKind(
-        String descriptor,
-        int id,
-        boolean isSingleSyntheticMethod,
-        boolean isFixedSuffixSynthetic,
-        boolean mayOverridesNonProgramType) {
-      this.descriptor = descriptor;
+    SyntheticKind(int id, String descriptor) {
       this.id = id;
-      this.isSingleSyntheticMethod = isSingleSyntheticMethod;
-      this.isFixedSuffixSynthetic = isFixedSuffixSynthetic;
-      this.mayOverridesNonProgramType = mayOverridesNonProgramType;
+      this.descriptor = descriptor;
     }
 
-    public boolean allowSyntheticContext() {
-      return this == RECORD_TAG;
+    @Override
+    public int compareTo(SyntheticKind other) {
+      return Integer.compare(id, other.getId());
     }
 
-    public boolean isGlobal() {
-      return isFixedSuffixSynthetic && descriptor.isEmpty();
+    @Override
+    public int hashCode() {
+      return id;
     }
 
-    public boolean isShareable() {
-      if (isFixedSuffixSynthetic) {
-        // Fixed synthetics are non-shareable. Ordered by their unique type.
-        return false;
+    @Override
+    public boolean equals(Object o) {
+      return Equatable.equalsImpl(this, o);
+    }
+
+    public static Collection<SyntheticKind> values() {
+      return ALL_KINDS;
+    }
+
+    public static SyntheticKind fromId(int id) {
+      if (0 < id && id <= ALL_KINDS.size()) {
+        return ALL_KINDS.get(id - 1);
       }
+      assert false;
+      return null;
+    }
+
+    public int getId() {
+      return id;
+    }
+
+    public String getDescriptor() {
+      return descriptor;
+    }
+
+    public abstract boolean isShareable();
+
+    public abstract boolean isSingleSyntheticMethod();
+
+    public abstract boolean isFixedSuffixSynthetic();
+
+    public abstract boolean isGlobal();
+
+    public abstract boolean isMayOverridesNonProgramType();
+
+    public abstract boolean allowSyntheticContext();
+  }
+
+  private static class SyntheticMethodKind extends SyntheticKind {
+
+    public SyntheticMethodKind(int id, String descriptor) {
+      super(id, descriptor);
+    }
+
+    @Override
+    public boolean isShareable() {
+      // Single methods may always be shared.
+      return true;
+    }
+
+    @Override
+    public boolean isSingleSyntheticMethod() {
+      return true;
+    }
+
+    @Override
+    public boolean isFixedSuffixSynthetic() {
+      return false;
+    }
+
+    @Override
+    public boolean isGlobal() {
+      return false;
+    }
+
+    @Override
+    public boolean isMayOverridesNonProgramType() {
+      return false;
+    }
+
+    @Override
+    public boolean allowSyntheticContext() {
+      return false;
+    }
+  }
+
+  private static class SyntheticClassKind extends SyntheticKind {
+
+    public SyntheticClassKind(int id, String descriptor) {
+      super(id, descriptor);
+    }
+
+    @Override
+    public boolean isShareable() {
       if (this == NON_FIXED_INIT_TYPE_ARGUMENT) {
         // TODO(b/214901256): Sharing of synthetic classes may lead to duplicate method errors.
         return false;
@@ -119,33 +269,70 @@
       return true;
     }
 
-    public static SyntheticKind fromDescriptor(String descriptor) {
-      for (SyntheticKind kind : values()) {
-        if (kind.descriptor.equals(descriptor)) {
-          return kind;
-        }
-      }
-      return null;
+    @Override
+    public final boolean isSingleSyntheticMethod() {
+      return false;
     }
 
-    public static SyntheticKind fromId(int id) {
-      for (SyntheticKind kind : values()) {
-        if (kind.id == id) {
-          return kind;
-        }
-      }
-      return null;
+    @Override
+    public boolean isFixedSuffixSynthetic() {
+      return false;
     }
 
-    private static boolean verifyNoOverlappingIds() {
-      Int2ReferenceMap<SyntheticKind> idToKind = new Int2ReferenceOpenHashMap<>();
-      for (SyntheticKind kind : values()) {
-        SyntheticKind kindWithSameId = idToKind.put(kind.id, kind);
-        assert kindWithSameId == null
-            : "Synthetic kind " + idToKind + " has same id as " + kindWithSameId;
-      }
+    @Override
+    public boolean isGlobal() {
+      return false;
+    }
+
+    @Override
+    public boolean isMayOverridesNonProgramType() {
+      return false;
+    }
+
+    @Override
+    public boolean allowSyntheticContext() {
+      return false;
+    }
+  }
+
+  private static class SyntheticFixedClassKind extends SyntheticClassKind {
+    private final boolean mayOverridesNonProgramType;
+    private final boolean allowSyntheticContext;
+
+    private SyntheticFixedClassKind(
+        int id,
+        String descriptor,
+        boolean mayOverridesNonProgramType,
+        boolean allowSyntheticContext) {
+      super(id, descriptor);
+      this.mayOverridesNonProgramType = mayOverridesNonProgramType;
+      this.allowSyntheticContext = allowSyntheticContext;
+    }
+
+    @Override
+    public boolean isShareable() {
+      return false;
+    }
+
+    @Override
+    public boolean isFixedSuffixSynthetic() {
       return true;
     }
+
+    @Override
+    public boolean isGlobal() {
+      return getDescriptor().isEmpty();
+    }
+
+    @Override
+    public boolean isMayOverridesNonProgramType() {
+      return mayOverridesNonProgramType;
+    }
+
+    @Override
+    public boolean allowSyntheticContext() {
+      return allowSyntheticContext;
+    }
   }
 
   private static final String SYNTHETIC_CLASS_SEPARATOR = "$$";
@@ -169,22 +356,22 @@
     String binaryName = type.toBinaryName();
     int index =
         binaryName.lastIndexOf(
-            kind.isFixedSuffixSynthetic ? kind.descriptor : SYNTHETIC_CLASS_SEPARATOR);
+            kind.isFixedSuffixSynthetic() ? kind.descriptor : SYNTHETIC_CLASS_SEPARATOR);
     if (index < 0) {
       throw new Unreachable("Unexpected failure to compute an synthetic prefix");
     }
     return binaryName.substring(0, index);
   }
 
-  public static DexType createFixedType(
+  static DexType createFixedType(
       SyntheticKind kind, SynthesizingContext context, DexItemFactory factory) {
-    assert kind.isFixedSuffixSynthetic;
+    assert kind.isFixedSuffixSynthetic();
     return createType("", kind, context.getSynthesizingContextType(), "", factory);
   }
 
   static DexType createInternalType(
       SyntheticKind kind, SynthesizingContext context, String id, DexItemFactory factory) {
-    assert !kind.isFixedSuffixSynthetic;
+    assert !kind.isFixedSuffixSynthetic();
     return createType(
         INTERNAL_SYNTHETIC_CLASS_SEPARATOR,
         kind,
@@ -195,9 +382,9 @@
 
   static DexType createExternalType(
       SyntheticKind kind, String externalSyntheticTypePrefix, String id, DexItemFactory factory) {
-    assert kind.isFixedSuffixSynthetic == id.isEmpty();
+    assert kind.isFixedSuffixSynthetic() == id.isEmpty();
     return createType(
-        kind.isFixedSuffixSynthetic ? "" : EXTERNAL_SYNTHETIC_CLASS_SEPARATOR,
+        kind.isFixedSuffixSynthetic() ? "" : EXTERNAL_SYNTHETIC_CLASS_SEPARATOR,
         kind,
         externalSyntheticTypePrefix,
         id,
@@ -257,14 +444,9 @@
         createDescriptor(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR, kind, context.getBinaryName(), id));
   }
 
-  public static boolean isInternalStaticInterfaceCall(ClassReference reference) {
-    return SyntheticNaming.isSynthetic(
-        reference, Phase.INTERNAL, SyntheticKind.STATIC_INTERFACE_CALL);
-  }
-
   static boolean isSynthetic(ClassReference clazz, Phase phase, SyntheticKind kind) {
     String typeName = clazz.getTypeName();
-    if (kind.isFixedSuffixSynthetic) {
+    if (kind.isFixedSuffixSynthetic()) {
       assert phase == null;
       return clazz.getBinaryName().endsWith(kind.descriptor);
     }
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index 5977b9a..18fb93b 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -78,7 +78,7 @@
       if (kind.isGlobal()) {
         continue;
       }
-      if (kind.isFixedSuffixSynthetic) {
+      if (kind.isFixedSuffixSynthetic()) {
         if (SyntheticNaming.isSynthetic(reference, null, kind)) {
           return true;
         }