Add support for synthesizing classpath classes

Change-Id: Ifb4731a712e4755de632762c38bbaa643c980ea9
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index 219b723..af6d9f6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -98,6 +98,8 @@
 
   abstract List<DexProgramClass> programClasses();
 
+  abstract List<DexClasspathClass> classpathClasses();
+
   public List<DexProgramClass> classes() {
     ReorderBox<DexProgramClass> box = new ReorderBox<>(programClasses());
     assert box.reorderClasses();
@@ -134,7 +136,8 @@
     // new or removing existing classes), classpath and library
     // collections will be considered monolithic collections.
 
-    List<DexProgramClass> programClasses = new ArrayList<>();
+    private List<DexProgramClass> programClasses = new ArrayList<>();
+    private List<DexClasspathClass> classpathClasses = new ArrayList<>();
 
     final List<DataResourceProvider> dataResourceProviders = new ArrayList<>();
 
@@ -157,6 +160,7 @@
 
     public Builder(DexApplication application) {
       programClasses.addAll(application.programClasses());
+      classpathClasses.addAll(application.classpathClasses());
       dataResourceProviders.addAll(application.dataResourceProviders);
       proguardMap = application.getProguardMap();
       timing = application.timing;
@@ -172,13 +176,21 @@
       return self();
     }
 
-    public synchronized T replaceProgramClasses(List<DexProgramClass> newProgramClasses) {
+    public synchronized T replaceProgramClasses(Collection<DexProgramClass> newProgramClasses) {
       assert newProgramClasses != null;
       this.programClasses.clear();
       this.programClasses.addAll(newProgramClasses);
       return self();
     }
 
+    public synchronized T replaceClasspathClasses(
+        Collection<DexClasspathClass> newClasspathClasses) {
+      assert newClasspathClasses != null;
+      classpathClasses.clear();
+      classpathClasses.addAll(newClasspathClasses);
+      return self();
+    }
+
     public synchronized T addDataResourceProvider(DataResourceProvider provider) {
       dataResourceProviders.add(provider);
       return self();
@@ -194,6 +206,21 @@
       return self();
     }
 
+    public synchronized T addProgramClasses(Collection<DexProgramClass> classes) {
+      programClasses.addAll(classes);
+      return self();
+    }
+
+    public synchronized T addClasspathClass(DexClasspathClass clazz) {
+      classpathClasses.add(clazz);
+      return self();
+    }
+
+    public synchronized T addClasspathClasses(Collection<DexClasspathClass> classes) {
+      classpathClasses.addAll(classes);
+      return self();
+    }
+
     public synchronized T addSynthesizedClass(DexProgramClass synthesizedClass) {
       assert synthesizedClass.isProgramClass() : "All synthesized classes must be program classes";
       addProgramClass(synthesizedClass);
@@ -201,10 +228,14 @@
       return self();
     }
 
-    public Collection<DexProgramClass> getProgramClasses() {
+    public List<DexProgramClass> getProgramClasses() {
       return programClasses;
     }
 
+    public List<DexClasspathClass> getClasspathClasses() {
+      return classpathClasses;
+    }
+
     public Collection<DexProgramClass> getSynthesizedClasses() {
       return synthesizedClasses;
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index dc3ff8d..7fb6023 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -12,13 +12,17 @@
 import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 
-public class DexClasspathClass extends DexClass implements Supplier<DexClasspathClass> {
+public class DexClasspathClass extends DexClass
+    implements Supplier<DexClasspathClass>, StructuralItem<DexClasspathClass> {
 
   public DexClasspathClass(
       DexType type,
@@ -129,4 +133,30 @@
     }
     return !isInterface() || appView.options().classpathInterfacesMayHaveStaticInitialization;
   }
+
+  @Override
+  public DexClasspathClass self() {
+    return this;
+  }
+
+  @Override
+  public StructuralMapping<DexClasspathClass> getStructuralMapping() {
+    return DexClasspathClass::specify;
+  }
+
+  private static void specify(StructuralSpecification<DexClasspathClass, ?> spec) {
+    spec.withItem(DexClass::getType)
+        .withItem(DexClass::getSuperType)
+        .withItem(DexClass::getInterfaces)
+        .withItem(DexClass::getAccessFlags)
+        .withNullableItem(DexClass::getSourceFile)
+        .withNullableItem(DexClass::getNestHostClassAttribute)
+        .withItemCollection(DexClass::getNestMembersClassAttributes)
+        .withItem(DexDefinition::annotations)
+        // TODO(b/158159959): Make signatures structural.
+        .withAssert(c -> c.classSignature == ClassSignature.noSignature())
+        .withItemArray(c -> c.staticFields)
+        .withItemArray(c -> c.instanceFields)
+        .withItemCollection(DexClass::allMethodsSorted);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index c6cf5f8..c267e7c 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -67,7 +67,8 @@
     return libraryClasses;
   }
 
-  public Collection<DexClasspathClass> classpathClasses() {
+  @Override
+  public List<DexClasspathClass> classpathClasses() {
     return classpathClasses;
   }
 
@@ -181,21 +182,19 @@
   public static class Builder extends DexApplication.Builder<Builder> {
 
     private ImmutableList<DexLibraryClass> libraryClasses;
-    private ImmutableList<DexClasspathClass> classpathClasses;
 
     Builder(LazyLoadedDexApplication application) {
       super(application);
       // As a side-effect, this will force-load all classes.
       AllClasses allClasses = application.loadAllClasses();
       libraryClasses = allClasses.getLibraryClasses();
-      classpathClasses = allClasses.getClasspathClasses();
       replaceProgramClasses(allClasses.getProgramClasses());
+      replaceClasspathClasses(allClasses.getClasspathClasses());
     }
 
     private Builder(DirectMappedDexApplication application) {
       super(application);
       libraryClasses = application.libraryClasses;
-      classpathClasses = application.classpathClasses;
     }
 
     @Override
@@ -208,26 +207,6 @@
       return self();
     }
 
-    public Builder replaceClasspathClasses(Collection<DexClasspathClass> classpathClasses) {
-      this.classpathClasses = ImmutableList.copyOf(classpathClasses);
-      return self();
-    }
-
-    public Builder addProgramClasses(Collection<DexProgramClass> classes) {
-      programClasses =
-          ImmutableList.<DexProgramClass>builder().addAll(programClasses).addAll(classes).build();
-      return self();
-    }
-
-    public Builder addClasspathClasses(Collection<DexClasspathClass> classes) {
-      classpathClasses =
-          ImmutableList.<DexClasspathClass>builder()
-              .addAll(classpathClasses)
-              .addAll(classes)
-              .build();
-      return self();
-    }
-
     public Builder addLibraryClasses(Collection<DexLibraryClass> classes) {
       libraryClasses =
           ImmutableList.<DexLibraryClass>builder().addAll(libraryClasses).addAll(classes).build();
@@ -240,17 +219,17 @@
       // TODO(zerny): Consider not rebuilding the map if no program classes are added.
       Map<DexType, DexClass> allClasses =
           new IdentityHashMap<>(
-              programClasses.size() + classpathClasses.size() + libraryClasses.size());
+              getProgramClasses().size() + getClasspathClasses().size() + libraryClasses.size());
       // Note: writing classes in reverse priority order, so a duplicate will be correctly ordered.
       // There should never be duplicates and that is asserted in the addAll subroutine.
       addAll(allClasses, libraryClasses);
-      addAll(allClasses, classpathClasses);
-      addAll(allClasses, programClasses);
+      addAll(allClasses, getClasspathClasses());
+      addAll(allClasses, getProgramClasses());
       return new DirectMappedDexApplication(
           proguardMap,
           allClasses,
-          ImmutableList.copyOf(programClasses),
-          classpathClasses,
+          ImmutableList.copyOf(getProgramClasses()),
+          ImmutableList.copyOf(getClasspathClasses()),
           libraryClasses,
           ImmutableList.copyOf(dataResourceProviders),
           options,
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index ce5ff83..ba240f6 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -54,6 +54,12 @@
   }
 
   @Override
+  List<DexClasspathClass> classpathClasses() {
+    classpathClasses.forceLoad(t -> true);
+    return classpathClasses.getAllClasses();
+  }
+
+  @Override
   public DexClass definitionFor(DexType type) {
     assert type.isClassType() : "Cannot lookup definition for type: " + type;
     DexClass clazz = null;
@@ -199,7 +205,7 @@
     Builder(ProgramClassConflictResolver resolver, InternalOptions options, Timing timing) {
       super(options, timing);
       this.resolver = resolver;
-      this.classpathClasses = null;
+      this.classpathClasses = ClasspathClassCollection.empty();
       this.libraryClasses = null;
     }
 
@@ -229,7 +235,7 @@
     public LazyLoadedDexApplication build() {
       return new LazyLoadedDexApplication(
           proguardMap,
-          ProgramClassCollection.create(programClasses, resolver),
+          ProgramClassCollection.create(getProgramClasses(), resolver),
           ImmutableList.copyOf(dataResourceProviders),
           classpathClasses,
           libraryClasses,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 966cfec..477d8d8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -34,7 +34,7 @@
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
-import com.android.tools.r8.synthesis.SyntheticClassBuilder;
+import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
 import com.android.tools.r8.utils.OptionalBool;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -72,7 +72,7 @@
   private DexProgramClass clazz = null;
 
   LambdaClass(
-      SyntheticClassBuilder builder,
+      SyntheticProgramClassBuilder builder,
       AppView<?> appView,
       LambdaRewriter rewriter,
       ProgramMethod accessedFrom,
@@ -115,7 +115,7 @@
     this.clazz = clazz;
   }
 
-  private void synthesizeLambdaClass(SyntheticClassBuilder builder) {
+  private void synthesizeLambdaClass(SyntheticProgramClassBuilder builder) {
     builder.setInterfaces(descriptor.interfaces);
     synthesizeStaticFields(builder);
     synthesizeInstanceFields(builder);
@@ -137,7 +137,7 @@
   }
 
   // Synthesize virtual methods.
-  private void synthesizeVirtualMethods(SyntheticClassBuilder builder) {
+  private void synthesizeVirtualMethods(SyntheticProgramClassBuilder builder) {
     DexMethod mainMethod =
         appView.dexItemFactory().createMethod(type, descriptor.erasedProto, descriptor.name);
 
@@ -178,7 +178,7 @@
   }
 
   // Synthesize direct methods.
-  private void synthesizeDirectMethods(SyntheticClassBuilder builder) {
+  private void synthesizeDirectMethods(SyntheticProgramClassBuilder builder) {
     boolean stateless = isStateless();
     List<DexEncodedMethod> methods = new ArrayList<>(stateless ? 2 : 1);
 
@@ -214,7 +214,7 @@
   }
 
   // Synthesize instance fields to represent captured values.
-  private void synthesizeInstanceFields(SyntheticClassBuilder builder) {
+  private void synthesizeInstanceFields(SyntheticProgramClassBuilder builder) {
     DexType[] fieldTypes = descriptor.captures.values;
     int fieldCount = fieldTypes.length;
     List<DexEncodedField> fields = new ArrayList<>(fieldCount);
@@ -234,7 +234,7 @@
   }
 
   // Synthesize static fields to represent singleton instance.
-  private void synthesizeStaticFields(SyntheticClassBuilder builder) {
+  private void synthesizeStaticFields(SyntheticProgramClassBuilder builder) {
     if (isStateless()) {
       // Create instance field for stateless lambda.
       assert this.lambdaField != null;
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 8d83434..e88a714 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -279,7 +279,7 @@
         previous.getMainDexClasses(),
         previous.deadProtoTypes,
         previous.getMissingClasses(),
-        CollectionUtils.mergeSets(previous.liveTypes, committedItems.getCommittedTypes()),
+        CollectionUtils.mergeSets(previous.liveTypes, committedItems.getCommittedProgramTypes()),
         previous.targetedMethods,
         previous.failedResolutionTargets,
         previous.bootstrapMethods,
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
index e60dfa3..2681a90 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
@@ -27,17 +27,17 @@
   final DexApplication application;
   final int nextSyntheticId;
   final CommittedSyntheticsCollection committed;
-  final ImmutableList<DexType> committedTypes;
+  final ImmutableList<DexType> committedProgramTypes;
 
   CommittedItems(
       int nextSyntheticId,
       DexApplication application,
       CommittedSyntheticsCollection committed,
-      ImmutableList<DexType> committedTypes) {
+      ImmutableList<DexType> committedProgramTypes) {
     this.nextSyntheticId = nextSyntheticId;
     this.application = application;
     this.committed = committed;
-    this.committedTypes = committedTypes;
+    this.committedProgramTypes = committedProgramTypes;
     committed.verifyTypesAreInApp(application);
   }
 
@@ -50,8 +50,8 @@
     return application;
   }
 
-  public Collection<DexType> getCommittedTypes() {
-    return committedTypes;
+  public Collection<DexType> getCommittedProgramTypes() {
+    return committedProgramTypes;
   }
 
   @Deprecated
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
index 582a39a..053b20b 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
@@ -26,7 +26,8 @@
 
   static class Builder {
     private final CommittedSyntheticsCollection parent;
-    private ImmutableMap.Builder<DexType, SyntheticClassReference> newNonLegacyClasses = null;
+    private ImmutableMap.Builder<DexType, SyntheticProgramClassReference> newNonLegacyClasses =
+        null;
     private ImmutableMap.Builder<DexType, SyntheticMethodReference> newNonLegacyMethods = null;
     private ImmutableSet.Builder<DexType> newLegacyClasses = null;
 
@@ -34,16 +35,18 @@
       this.parent = parent;
     }
 
-    public Builder addItem(SyntheticDefinition<?, ?> definition) {
-      definition.toReference().apply(this::addNonLegacyMethod, this::addNonLegacyClass);
+    public Builder addItem(SyntheticDefinition<?, ?, ?> definition) {
+      if (definition.isProgramDefinition()) {
+        definition.asProgramDefinition().apply(this::addNonLegacyMethod, this::addNonLegacyClass);
+      }
       return this;
     }
 
-    public Builder addNonLegacyClass(SyntheticClassDefinition definition) {
+    public Builder addNonLegacyClass(SyntheticProgramClassDefinition definition) {
       return addNonLegacyClass(definition.toReference());
     }
 
-    public Builder addNonLegacyClass(SyntheticClassReference reference) {
+    public Builder addNonLegacyClass(SyntheticProgramClassReference reference) {
       if (newNonLegacyClasses == null) {
         newNonLegacyClasses = ImmutableMap.builder();
       }
@@ -83,7 +86,7 @@
       if (newNonLegacyClasses == null && newNonLegacyMethods == null && newLegacyClasses == null) {
         return parent;
       }
-      ImmutableMap<DexType, SyntheticClassReference> allNonLegacyClasses =
+      ImmutableMap<DexType, SyntheticProgramClassReference> allNonLegacyClasses =
           newNonLegacyClasses == null
               ? parent.nonLegacyClasses
               : newNonLegacyClasses.putAll(parent.nonLegacyClasses).build();
@@ -114,12 +117,12 @@
   private final ImmutableMap<DexType, SyntheticMethodReference> nonLegacyMethods;
 
   /** Mapping from synthetic type to its synthetic class item description. */
-  private final ImmutableMap<DexType, SyntheticClassReference> nonLegacyClasses;
+  private final ImmutableMap<DexType, SyntheticProgramClassReference> nonLegacyClasses;
 
   public CommittedSyntheticsCollection(
       ImmutableSet<DexType> legacyTypes,
       ImmutableMap<DexType, SyntheticMethodReference> nonLegacyMethods,
-      ImmutableMap<DexType, SyntheticClassReference> nonLegacyClasses) {
+      ImmutableMap<DexType, SyntheticProgramClassReference> nonLegacyClasses) {
     this.legacyTypes = legacyTypes;
     this.nonLegacyMethods = nonLegacyMethods;
     this.nonLegacyClasses = nonLegacyClasses;
@@ -160,11 +163,11 @@
     return nonLegacyMethods;
   }
 
-  public ImmutableMap<DexType, SyntheticClassReference> getNonLegacyClasses() {
+  public ImmutableMap<DexType, SyntheticProgramClassReference> getNonLegacyClasses() {
     return nonLegacyClasses;
   }
 
-  public SyntheticReference<?, ?> getNonLegacyItem(DexType type) {
+  public SyntheticReference<?, ?, ?> getNonLegacyItem(DexType type) {
     SyntheticMethodReference reference = nonLegacyMethods.get(type);
     if (reference != null) {
       return reference;
@@ -172,7 +175,7 @@
     return nonLegacyClasses.get(type);
   }
 
-  public void forEachNonLegacyItem(Consumer<SyntheticReference<?, ?>> fn) {
+  public void forEachNonLegacyItem(Consumer<SyntheticReference<?, ?, ?>> fn) {
     nonLegacyMethods.forEach((t, r) -> fn.accept(r));
     nonLegacyClasses.forEach((t, r) -> fn.accept(r));
   }
@@ -198,7 +201,7 @@
         builder.addNonLegacyMethod(reference);
       }
     }
-    for (SyntheticClassReference reference : nonLegacyClasses.values()) {
+    for (SyntheticProgramClassReference reference : nonLegacyClasses.values()) {
       if (removed.contains(reference.getHolder())) {
         changed = true;
       } else {
@@ -215,7 +218,7 @@
         rewriteItems(nonLegacyClasses, lens));
   }
 
-  private static <R extends SyntheticReference<R, ?>> ImmutableMap<DexType, R> rewriteItems(
+  private static <R extends SyntheticReference<R, ?, ?>> ImmutableMap<DexType, R> rewriteItems(
       Map<DexType, R> items, NonIdentityGraphLens lens) {
     ImmutableMap.Builder<DexType, R> rewrittenItems = ImmutableMap.builder();
     for (R reference : items.values()) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index 2e371d0..932359d 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
@@ -39,6 +40,13 @@
   private final DexType inputContextType;
   private final Origin inputContextOrigin;
 
+  static SynthesizingContext fromNonSyntheticInputContext(DexClass context) {
+    // A context that is itself non-synthetic is the single context, thus both the input context
+    // and synthesizing context coincide.
+    return new SynthesizingContext(
+        context.getContextType(), context.getContextType(), context.getOrigin());
+  }
+
   static SynthesizingContext fromNonSyntheticInputContext(ProgramDefinition context) {
     // A context that is itself non-synthetic is the single context, thus both the input context
     // and synthesizing context coincide.
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
index 9d2460b..01e6c75 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
@@ -1,16 +1,18 @@
 // Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
+
 package com.android.tools.r8.synthesis;
 
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.ClassKind;
 import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
@@ -25,12 +27,14 @@
 import java.util.List;
 import java.util.function.Consumer;
 
-public class SyntheticClassBuilder {
+abstract class SyntheticClassBuilder<B extends SyntheticClassBuilder<B, C>, C extends DexClass> {
+
   private final DexItemFactory factory;
 
   private final DexType type;
   private final Origin origin;
 
+  private Kind originKind;
   private DexType superType;
   private DexTypeList interfaces = DexTypeList.empty();
   private List<DexEncodedField> staticFields = new ArrayList<>();
@@ -46,6 +50,10 @@
     this.superType = factory.objectType;
   }
 
+  public abstract B self();
+
+  public abstract ClassKind<C> getClassKind();
+
   public DexItemFactory getFactory() {
     return factory;
   }
@@ -54,50 +62,54 @@
     return type;
   }
 
-  public SyntheticClassBuilder setInterfaces(List<DexType> interfaces) {
+  public B setInterfaces(List<DexType> interfaces) {
     this.interfaces =
         interfaces.isEmpty()
             ? DexTypeList.empty()
             : new DexTypeList(interfaces.toArray(DexType.EMPTY_ARRAY));
-    return this;
+    return self();
   }
 
-  public SyntheticClassBuilder setStaticFields(List<DexEncodedField> fields) {
+  public B setOriginKind(Kind originKind) {
+    this.originKind = originKind;
+    return self();
+  }
+
+  public B setStaticFields(List<DexEncodedField> fields) {
     staticFields.clear();
     staticFields.addAll(fields);
-    return this;
+    return self();
   }
 
-  public SyntheticClassBuilder setInstanceFields(List<DexEncodedField> fields) {
+  public B setInstanceFields(List<DexEncodedField> fields) {
     instanceFields.clear();
     instanceFields.addAll(fields);
-    return this;
+    return self();
   }
 
-  public SyntheticClassBuilder setDirectMethods(Iterable<DexEncodedMethod> methods) {
+  public B setDirectMethods(Iterable<DexEncodedMethod> methods) {
     directMethods.clear();
     methods.forEach(directMethods::add);
-    return this;
+    return self();
   }
 
-  public SyntheticClassBuilder setVirtualMethods(Iterable<DexEncodedMethod> methods) {
+  public B setVirtualMethods(Iterable<DexEncodedMethod> methods) {
     virtualMethods.clear();
     methods.forEach(virtualMethods::add);
-    return this;
+    return self();
   }
 
-  public SyntheticClassBuilder addMethod(Consumer<SyntheticMethodBuilder> fn) {
+  public B addMethod(Consumer<SyntheticMethodBuilder> fn) {
     SyntheticMethodBuilder method = new SyntheticMethodBuilder(this);
     fn.accept(method);
     methods.add(method);
-    return this;
+    return self();
   }
 
-  DexProgramClass build() {
+  public C build() {
     ClassAccessFlags accessFlags =
         ClassAccessFlags.fromSharedAccessFlags(
             Constants.ACC_FINAL | Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
-    Kind originKind = null;
     DexString sourceFile = null;
     NestHostClassAttribute nestHost = null;
     List<NestMemberClassAttribute> nestMembers = Collections.emptyList();
@@ -116,25 +128,26 @@
             + 11 * (long) virtualMethods.hashCode()
             + 13 * (long) staticFields.hashCode()
             + 17 * (long) instanceFields.hashCode();
-    return new DexProgramClass(
-        type,
-        originKind,
-        origin,
-        accessFlags,
-        superType,
-        interfaces,
-        sourceFile,
-        nestHost,
-        nestMembers,
-        enclosingMembers,
-        innerClasses,
-        ClassSignature.noSignature(),
-        DexAnnotationSet.empty(),
-        staticFields.toArray(new DexEncodedField[staticFields.size()]),
-        instanceFields.toArray(new DexEncodedField[instanceFields.size()]),
-        directMethods.toArray(new DexEncodedMethod[directMethods.size()]),
-        virtualMethods.toArray(new DexEncodedMethod[virtualMethods.size()]),
-        factory.getSkipNameValidationForTesting(),
-        c -> checksum);
+    return getClassKind()
+        .create(
+            type,
+            originKind,
+            origin,
+            accessFlags,
+            superType,
+            interfaces,
+            sourceFile,
+            nestHost,
+            nestMembers,
+            enclosingMembers,
+            innerClasses,
+            ClassSignature.noSignature(),
+            DexAnnotationSet.empty(),
+            staticFields.toArray(DexEncodedField.EMPTY_ARRAY),
+            instanceFields.toArray(DexEncodedField.EMPTY_ARRAY),
+            directMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
+            virtualMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
+            factory.getSkipNameValidationForTesting(),
+            c -> checksum);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassDefinition.java
index 4b0b2cf..3a915bf 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassDefinition.java
@@ -3,37 +3,29 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.synthesis;
 
-import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.structural.RepresentativeMap;
-import com.google.common.hash.Hasher;
 
 /**
  * Definition of a synthetic class item.
  *
  * <p>This class is internal to the synthetic items collection, thus package-protected.
  */
-class SyntheticClassDefinition
-    extends SyntheticDefinition<SyntheticClassReference, SyntheticClassDefinition> {
+public abstract class SyntheticClassDefinition<
+        R extends SyntheticClassReference<R, D, C>,
+        D extends SyntheticClassDefinition<R, D, C>,
+        C extends DexClass>
+    extends SyntheticDefinition<R, D, C> {
 
-  private final DexProgramClass clazz;
+  final C clazz;
 
-  SyntheticClassDefinition(SyntheticKind kind, SynthesizingContext context, DexProgramClass clazz) {
+  SyntheticClassDefinition(SyntheticKind kind, SynthesizingContext context, C clazz) {
     super(kind, context);
     this.clazz = clazz;
   }
 
-  public DexProgramClass getProgramClass() {
-    return clazz;
-  }
-
   @Override
-  SyntheticClassReference toReference() {
-    return new SyntheticClassReference(getKind(), getContext(), clazz.getType());
-  }
-
-  @Override
-  DexProgramClass getHolder() {
+  public final C getHolder() {
     return clazz;
   }
 
@@ -43,16 +35,6 @@
   }
 
   @Override
-  void internalComputeHash(Hasher hasher, RepresentativeMap map) {
-    clazz.hashWithTypeEquivalence(hasher, map);
-  }
-
-  @Override
-  int internalCompareTo(SyntheticClassDefinition o, RepresentativeMap map) {
-    return clazz.compareWithTypeEquivalenceTo(o.clazz, map);
-  }
-
-  @Override
   public String toString() {
     return "SyntheticClass{ clazz = "
         + clazz.type.toSourceString()
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassReference.java
index 9c79ab9..25142ee 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassReference.java
@@ -1,22 +1,24 @@
 // Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
+
 package com.android.tools.r8.synthesis;
 
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import java.util.function.Consumer;
-import java.util.function.Function;
 
 /**
  * Reference to a synthetic class item.
  *
  * <p>This class is internal to the synthetic items collection, thus package-protected.
  */
-class SyntheticClassReference
-    extends SyntheticReference<SyntheticClassReference, SyntheticClassDefinition> {
+abstract class SyntheticClassReference<
+        R extends SyntheticClassReference<R, D, C>,
+        D extends SyntheticClassDefinition<R, D, C>,
+        C extends DexClass>
+    extends SyntheticReference<R, D, C> {
+
   final DexType type;
 
   SyntheticClassReference(SyntheticKind kind, SynthesizingContext context, DexType type) {
@@ -28,46 +30,4 @@
   DexType getHolder() {
     return type;
   }
-
-  @Override
-  SyntheticClassDefinition lookupDefinition(Function<DexType, DexClass> definitions) {
-    DexClass clazz = definitions.apply(type);
-    if (clazz == null) {
-      return null;
-    }
-    assert clazz.isProgramClass();
-    return new SyntheticClassDefinition(getKind(), getContext(), clazz.asProgramClass());
-  }
-
-  @Override
-  SyntheticClassReference rewrite(NonIdentityGraphLens lens) {
-    DexType rewritten = lens.lookupType(type);
-    // If the reference has been non-trivially rewritten the compiler has changed it and it can no
-    // longer be considered a synthetic. The context may or may not have changed.
-    if (type != rewritten && !lens.isSimpleRenaming(type, rewritten)) {
-      // If the referenced item is rewritten, it should be moved to another holder as the
-      // synthetic holder is no longer part of the synthetic collection.
-      assert SyntheticNaming.verifyNotInternalSynthetic(rewritten);
-      return null;
-    }
-    SynthesizingContext context = getContext().rewrite(lens);
-    if (context == getContext() && rewritten == type) {
-      return this;
-    }
-    // Ensure that if a synthetic moves its context moves consistently.
-    if (type != rewritten) {
-      context =
-          SynthesizingContext.fromSyntheticContextChange(rewritten, context, lens.dexItemFactory());
-      if (context == null) {
-        return null;
-      }
-    }
-    return new SyntheticClassReference(getKind(), context, rewritten);
-  }
-
-  @Override
-  void apply(
-      Consumer<SyntheticMethodReference> onMethod, Consumer<SyntheticClassReference> onClass) {
-    onClass.accept(this);
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassBuilder.java
new file mode 100644
index 0000000..20cd075
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassBuilder.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.synthesis;
+
+import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.graph.ClassKind;
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+
+public class SyntheticClasspathClassBuilder
+    extends SyntheticClassBuilder<SyntheticClasspathClassBuilder, DexClasspathClass> {
+
+  SyntheticClasspathClassBuilder(
+      DexType type, SynthesizingContext context, DexItemFactory factory) {
+    super(type, context, factory);
+    setOriginKind(Kind.CF);
+  }
+
+  @Override
+  public ClassKind<DexClasspathClass> getClassKind() {
+    return ClassKind.CLASSPATH;
+  }
+
+  @Override
+  public SyntheticClasspathClassBuilder self() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassDefinition.java
new file mode 100644
index 0000000..a05d505
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassDefinition.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.synthesis;
+
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.structural.RepresentativeMap;
+import com.google.common.hash.Hasher;
+
+/**
+ * Definition of a synthetic classpath class.
+ *
+ * <p>This class is internal to the synthetic items collection, thus package-protected.
+ */
+class SyntheticClasspathClassDefinition
+    extends SyntheticClassDefinition<
+        SyntheticClasspathClassReference, SyntheticClasspathClassDefinition, DexClasspathClass>
+    implements SyntheticClasspathDefinition {
+
+  SyntheticClasspathClassDefinition(
+      SyntheticKind kind, SynthesizingContext context, DexClasspathClass clazz) {
+    super(kind, context, clazz);
+  }
+
+  @Override
+  public boolean isClasspathDefinition() {
+    return true;
+  }
+
+  @Override
+  public SyntheticClasspathDefinition asClasspathDefinition() {
+    return this;
+  }
+
+  @Override
+  SyntheticClasspathClassReference toReference() {
+    return new SyntheticClasspathClassReference(getKind(), getContext(), clazz.getType());
+  }
+
+  @Override
+  public boolean isValid() {
+    return clazz.isPublic() && clazz.isFinal() && clazz.accessFlags.isSynthetic();
+  }
+
+  @Override
+  void internalComputeHash(Hasher hasher, RepresentativeMap map) {
+    clazz.hashWithTypeEquivalence(hasher, map);
+  }
+
+  @Override
+  int internalCompareTo(SyntheticClasspathClassDefinition o, RepresentativeMap map) {
+    return clazz.compareWithTypeEquivalenceTo(o.clazz, map);
+  }
+
+  @Override
+  public String toString() {
+    return "SyntheticClasspathClass{ clazz = "
+        + clazz.type.toSourceString()
+        + ", kind = "
+        + getKind()
+        + ", context = "
+        + getContext()
+        + " }";
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassReference.java
new file mode 100644
index 0000000..5b9ba3c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassReference.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.synthesis;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import java.util.function.Function;
+
+/**
+ * Reference to a synthetic class item.
+ *
+ * <p>This class is internal to the synthetic items collection, thus package-protected.
+ */
+class SyntheticClasspathClassReference
+    extends SyntheticClassReference<
+        SyntheticClasspathClassReference, SyntheticClasspathClassDefinition, DexClasspathClass> {
+
+  SyntheticClasspathClassReference(SyntheticKind kind, SynthesizingContext context, DexType type) {
+    super(kind, context, type);
+  }
+
+  @Override
+  SyntheticClasspathClassDefinition lookupDefinition(Function<DexType, DexClass> definitions) {
+    DexClass clazz = definitions.apply(type);
+    if (clazz == null) {
+      return null;
+    }
+    assert clazz.isClasspathClass();
+    return new SyntheticClasspathClassDefinition(getKind(), getContext(), clazz.asClasspathClass());
+  }
+
+  @Override
+  SyntheticClasspathClassReference rewrite(NonIdentityGraphLens lens) {
+    assert type == lens.lookupType(type)
+        : "Unexpected classpath rewrite of type " + type.toSourceString();
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathDefinition.java
new file mode 100644
index 0000000..0904dff
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathDefinition.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.synthesis;
+
+import com.android.tools.r8.graph.DexClasspathClass;
+
+public interface SyntheticClasspathDefinition {
+
+  DexClasspathClass getHolder();
+}
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 c04ad0a..071e7b6 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.synthesis;
 
-import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
@@ -18,7 +18,9 @@
  * <p>This class is internal to the synthetic items collection, thus package-protected.
  */
 abstract class SyntheticDefinition<
-    R extends SyntheticReference<R, D>, D extends SyntheticDefinition<R, D>> {
+    R extends SyntheticReference<R, D, C>,
+    D extends SyntheticDefinition<R, D, C>,
+    C extends DexClass> {
 
   private final SyntheticKind kind;
   private final SynthesizingContext context;
@@ -30,6 +32,22 @@
     this.context = context;
   }
 
+  public boolean isClasspathDefinition() {
+    return false;
+  }
+
+  public SyntheticClasspathDefinition asClasspathDefinition() {
+    return null;
+  }
+
+  public boolean isProgramDefinition() {
+    return false;
+  }
+
+  public SyntheticProgramDefinition asProgramDefinition() {
+    return null;
+  }
+
   abstract R toReference();
 
   final SyntheticKind getKind() {
@@ -40,7 +58,7 @@
     return context;
   }
 
-  abstract DexProgramClass getHolder();
+  public abstract C getHolder();
 
   final HashCode computeHash(RepresentativeMap map, boolean intermediate) {
     Hasher hasher = Hashing.murmur3_128().newHasher();
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 ca29c94..128fcef 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -172,7 +172,7 @@
     }
   }
 
-  public static class EquivalenceGroup<T extends SyntheticDefinition<?, T>> {
+  public static class EquivalenceGroup<T extends SyntheticDefinition<?, T, ?>> {
     private final List<T> members;
 
     public EquivalenceGroup(T representative, List<T> members) {
@@ -260,7 +260,7 @@
             .build());
   }
 
-  private <R extends SyntheticReference<R, D>, D extends SyntheticDefinition<R, D>>
+  private <R extends SyntheticReference<R, D, ?>, D extends SyntheticDefinition<R, D, ?>>
       Map<DexType, EquivalenceGroup<D>> computeEquivalences(
           AppView<?> appView,
           ImmutableCollection<R> references,
@@ -380,7 +380,7 @@
   private static DexApplication buildLensAndProgram(
       AppView<?> appView,
       Map<DexType, EquivalenceGroup<SyntheticMethodDefinition>> syntheticMethodGroups,
-      Map<DexType, EquivalenceGroup<SyntheticClassDefinition>> syntheticClassGroups,
+      Map<DexType, EquivalenceGroup<SyntheticProgramClassDefinition>> syntheticClassGroups,
       MainDexClasses mainDexClasses,
       Builder lensBuilder,
       List<DexProgramClass> newSyntheticClasses) {
@@ -426,14 +426,14 @@
     List<DexProgramClass> deduplicatedClasses = new ArrayList<>();
     syntheticClassGroups.forEach(
         (syntheticType, syntheticGroup) -> {
-          SyntheticClassDefinition representative = syntheticGroup.getRepresentative();
+          SyntheticProgramClassDefinition representative = syntheticGroup.getRepresentative();
           SynthesizingContext context = representative.getContext();
           context.registerPrefixRewriting(syntheticType, appView);
-          DexProgramClass externalSyntheticClass = representative.getProgramClass();
+          DexProgramClass externalSyntheticClass = representative.getHolder();
           newSyntheticClasses.add(externalSyntheticClass);
           addSyntheticMarker(representative.getKind(), externalSyntheticClass, context, appView);
-          for (SyntheticClassDefinition member : syntheticGroup.getMembers()) {
-            DexProgramClass memberClass = member.getProgramClass();
+          for (SyntheticProgramClassDefinition member : syntheticGroup.getMembers()) {
+            DexProgramClass memberClass = member.getHolder();
             DexType memberType = memberClass.getType();
             pruned.add(memberType);
             if (memberType != syntheticType) {
@@ -495,7 +495,7 @@
         (syntheticType, syntheticGroup) -> {
           DexProgramClass externalSyntheticClass = appForLookup.programDefinitionFor(syntheticType);
           newSyntheticClasses.add(externalSyntheticClass);
-          for (SyntheticClassDefinition member : syntheticGroup.getMembers()) {
+          for (SyntheticProgramClassDefinition member : syntheticGroup.getMembers()) {
             addMainDexAndSynthesizedFromForMember(
                 member,
                 externalSyntheticClass,
@@ -542,8 +542,8 @@
 
   private static DexProgramClass createExternalMethodClass(
       DexType syntheticType, SyntheticMethodDefinition representative, DexItemFactory factory) {
-    SyntheticClassBuilder builder =
-        new SyntheticClassBuilder(syntheticType, representative.getContext(), factory);
+    SyntheticProgramClassBuilder builder =
+        new SyntheticProgramClassBuilder(syntheticType, representative.getContext(), factory);
     // TODO(b/158159959): Support grouping multiple methods per synthetic class.
     builder.addMethod(
         methodBuilder -> {
@@ -560,7 +560,7 @@
   }
 
   private static void addMainDexAndSynthesizedFromForMember(
-      SyntheticDefinition<?, ?> member,
+      SyntheticDefinition<?, ?, ?> member,
       DexProgramClass externalSyntheticClass,
       MainDexClasses mainDexClasses,
       Set<DexType> derivedMainDexTypes,
@@ -584,7 +584,7 @@
     return options.intermediate && !options.cfToCfDesugar;
   }
 
-  private <T extends SyntheticDefinition<?, T>>
+  private <T extends SyntheticDefinition<?, T, ?>>
       Map<DexType, EquivalenceGroup<T>> computeActualEquivalences(
           Collection<List<T>> potentialEquivalences,
           Map<DexType, NumberGenerator> generators,
@@ -627,7 +627,7 @@
     return equivalences;
   }
 
-  private static <T extends SyntheticDefinition<?, T>> List<List<T>> groupEquivalent(
+  private static <T extends SyntheticDefinition<?, T, ?>> List<List<T>> groupEquivalent(
       List<T> potentialEquivalence, boolean intermediate, GraphLens graphLens) {
     List<List<T>> groups = new ArrayList<>();
     // Each other member is in a shared group if it is actually equivalent to the first member.
@@ -649,7 +649,7 @@
     return groups;
   }
 
-  private static <T extends SyntheticDefinition<?, T>> boolean checkGroupsAreDistinct(
+  private static <T extends SyntheticDefinition<?, T, ?>> boolean checkGroupsAreDistinct(
       EquivalenceGroup<T> g1, EquivalenceGroup<T> g2, GraphLens graphLens) {
     int order = g1.compareToIncludingContext(g2, graphLens);
     assert order != 0;
@@ -657,7 +657,7 @@
     return true;
   }
 
-  private static <T extends SyntheticDefinition<?, T>> T findDeterministicRepresentative(
+  private static <T extends SyntheticDefinition<?, T, ?>> T findDeterministicRepresentative(
       List<T> members, GraphLens graphLens) {
     // Pick a deterministic member as representative.
     T smallest = members.get(0);
@@ -695,7 +695,7 @@
     return externalType;
   }
 
-  private static <T extends SyntheticDefinition<?, T>>
+  private static <T extends SyntheticDefinition<?, T, ?>>
       Collection<List<T>> computePotentialEquivalences(
           Map<DexType, T> definitions,
           boolean intermediate,
@@ -729,7 +729,7 @@
     return equivalences.values();
   }
 
-  private <R extends SyntheticReference<R, D>, D extends SyntheticDefinition<R, D>>
+  private <R extends SyntheticReference<R, D, ?>, D extends SyntheticDefinition<R, D, ?>>
       Map<DexType, D> lookupDefinitions(AppView<?> appView, Collection<R> references) {
     Map<DexType, D> definitions = new IdentityHashMap<>(references.size());
     for (R reference : references) {
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 9c03710..657db43 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
@@ -49,7 +50,7 @@
     private final Map<DexType, DexProgramClass> legacyClasses = new ConcurrentHashMap<>();
 
     /** Thread safe collection of synthetic items not yet committed to the application. */
-    private final ConcurrentHashMap<DexType, SyntheticDefinition<?, ?>> nonLegacyDefinitions =
+    private final ConcurrentHashMap<DexType, SyntheticDefinition<?, ?, ?>> nonLegacyDefinitions =
         new ConcurrentHashMap<>();
 
     boolean isEmpty() {
@@ -66,11 +67,13 @@
       return true;
     }
 
-    Collection<DexProgramClass> getAllClasses() {
+    Collection<DexProgramClass> getAllProgramClasses() {
       List<DexProgramClass> allPending =
           new ArrayList<>(nonLegacyDefinitions.size() + legacyClasses.size());
-      for (SyntheticDefinition<?, ?> item : nonLegacyDefinitions.values()) {
-        allPending.add(item.getHolder());
+      for (SyntheticDefinition<?, ?, ?> item : nonLegacyDefinitions.values()) {
+        if (item.isProgramDefinition()) {
+          allPending.add(item.asProgramDefinition().getHolder());
+        }
       }
       allPending.addAll(legacyClasses.values());
       return Collections.unmodifiableList(allPending);
@@ -121,7 +124,7 @@
             });
       } else if (marker.isSyntheticClass()) {
         builder.addNonLegacyClass(
-            new SyntheticClassDefinition(marker.getKind(), marker.getContext(), clazz));
+            new SyntheticProgramClassDefinition(marker.getKind(), marker.getContext(), clazz));
       }
     }
     CommittedSyntheticsCollection committed = builder.build();
@@ -148,11 +151,13 @@
 
   @Override
   public DexClass definitionFor(DexType type, Function<DexType, DexClass> baseDefinitionFor) {
-    DexProgramClass clazz = pending.legacyClasses.get(type);
+    DexClass clazz = pending.legacyClasses.get(type);
     if (clazz == null) {
-      SyntheticDefinition<?, ?> item = pending.nonLegacyDefinitions.get(type);
+      SyntheticDefinition<?, ?, ?> item = pending.nonLegacyDefinitions.get(type);
       if (item != null) {
         clazz = item.getHolder();
+        assert clazz.isProgramClass() == item.isProgramDefinition();
+        assert clazz.isClasspathClass() == item.isClasspathDefinition();
       }
     }
     if (clazz != null) {
@@ -168,7 +173,7 @@
         : "Uncommitted synthetics: "
             + pending.nonLegacyDefinitions.keySet().stream()
                 .map(DexType::getName)
-                .collect(Collectors.joining());
+                .collect(Collectors.joining(", "));
     return true;
   }
 
@@ -177,7 +182,7 @@
   }
 
   public Collection<DexProgramClass> getPendingSyntheticClasses() {
-    return pending.getAllClasses();
+    return pending.getAllProgramClasses();
   }
 
   private boolean isCommittedSynthetic(DexType type) {
@@ -213,9 +218,9 @@
       DexProgramClass clazz,
       Predicate<DexProgramClass> ifIsLambda,
       Predicate<DexProgramClass> ifNotLambda) {
-    SyntheticReference<?, ?> reference = committed.getNonLegacyItem(clazz.getType());
+    SyntheticReference<?, ?, ?> reference = committed.getNonLegacyItem(clazz.getType());
     if (reference == null) {
-      SyntheticDefinition<?, ?> definition = pending.nonLegacyDefinitions.get(clazz.getType());
+      SyntheticDefinition<?, ?, ?> definition = pending.nonLegacyDefinitions.get(clazz.getType());
       if (definition != null) {
         reference = definition.toReference();
       }
@@ -242,11 +247,11 @@
 
   private SynthesizingContext getSynthesizingContext(ProgramDefinition context) {
     DexType contextType = context.getContextType();
-    SyntheticDefinition<?, ?> existingDefinition = pending.nonLegacyDefinitions.get(contextType);
+    SyntheticDefinition<?, ?, ?> existingDefinition = pending.nonLegacyDefinitions.get(contextType);
     if (existingDefinition != null) {
       return existingDefinition.getContext();
     }
-    SyntheticReference<?, ?> existingReference = committed.getNonLegacyItem(contextType);
+    SyntheticReference<?, ?, ?> existingReference = committed.getNonLegacyItem(contextType);
     if (existingReference != null) {
       return existingReference.getContext();
     }
@@ -270,16 +275,29 @@
       DexProgramClass context,
       DexItemFactory factory,
       Supplier<String> syntheticIdSupplier,
-      Consumer<SyntheticClassBuilder> fn) {
+      Consumer<SyntheticProgramClassBuilder> fn) {
     // Obtain the outer synthesizing context in the case the context itself is synthetic.
     // This is to ensure a flat input-type -> synthetic-item mapping.
     SynthesizingContext outerContext = getSynthesizingContext(context);
     DexType type =
         SyntheticNaming.createInternalType(kind, outerContext, syntheticIdSupplier.get(), factory);
-    SyntheticClassBuilder classBuilder = new SyntheticClassBuilder(type, outerContext, factory);
+    SyntheticProgramClassBuilder classBuilder =
+        new SyntheticProgramClassBuilder(type, outerContext, factory);
     fn.accept(classBuilder);
     DexProgramClass clazz = classBuilder.build();
-    addPendingDefinition(new SyntheticClassDefinition(kind, outerContext, clazz));
+    addPendingDefinition(new SyntheticProgramClassDefinition(kind, outerContext, clazz));
+    return clazz;
+  }
+
+  public DexClasspathClass createClasspathClass(
+      SyntheticKind kind, DexType type, DexClass context, DexItemFactory factory) {
+    // Obtain the outer synthesizing context in the case the context itself is synthetic.
+    // This is to ensure a flat input-type -> synthetic-item mapping.
+    SynthesizingContext outerContext = SynthesizingContext.fromNonSyntheticInputContext(context);
+    SyntheticClasspathClassBuilder classBuilder =
+        new SyntheticClasspathClassBuilder(type, outerContext, factory);
+    DexClasspathClass clazz = classBuilder.build();
+    addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz));
     return clazz;
   }
 
@@ -322,7 +340,8 @@
     SynthesizingContext outerContext = getSynthesizingContext(context);
     DexType type =
         SyntheticNaming.createInternalType(kind, outerContext, syntheticIdSupplier.get(), factory);
-    SyntheticClassBuilder classBuilder = new SyntheticClassBuilder(type, outerContext, factory);
+    SyntheticProgramClassBuilder classBuilder =
+        new SyntheticProgramClassBuilder(type, outerContext, factory);
     DexProgramClass clazz =
         classBuilder
             .addMethod(fn.andThen(m -> m.setName(SyntheticNaming.INTERNAL_SYNTHETIC_METHOD_PREFIX)))
@@ -332,7 +351,7 @@
     return method;
   }
 
-  private void addPendingDefinition(SyntheticDefinition<?, ?> definition) {
+  private void addPendingDefinition(SyntheticDefinition<?, ?, ?> definition) {
     pending.nonLegacyDefinitions.put(definition.getHolder().getType(), definition);
   }
 
@@ -365,26 +384,34 @@
     assert verifyClassesAreInApp(application, pending.legacyClasses.values());
     builder.addLegacyClasses(pending.legacyClasses.values());
     // Compute the synthetic additions and add them to the application.
-    ImmutableList<DexType> additions;
+    ImmutableList<DexType> committedProgramTypes;
     DexApplication amendedApplication;
     if (pending.nonLegacyDefinitions.isEmpty()) {
-      additions = ImmutableList.of();
+      committedProgramTypes = ImmutableList.of();
       amendedApplication = application;
     } else {
       DexApplication.Builder<?> appBuilder = application.builder();
-      ImmutableList.Builder<DexType> additionsBuilder = ImmutableList.builder();
-      for (SyntheticDefinition<?, ?> definition : pending.nonLegacyDefinitions.values()) {
+      ImmutableList.Builder<DexType> committedProgramTypesBuilder = ImmutableList.builder();
+      for (SyntheticDefinition<?, ?, ?> definition : pending.nonLegacyDefinitions.values()) {
         if (!removedClasses.contains(definition.getHolder().getType())) {
-          additionsBuilder.add(definition.getHolder().getType());
-          appBuilder.addProgramClass(definition.getHolder());
+          if (definition.isProgramDefinition()) {
+            committedProgramTypesBuilder.add(definition.getHolder().getType());
+            appBuilder.addProgramClass(definition.asProgramDefinition().getHolder());
+          } else {
+            assert definition.isClasspathDefinition();
+            appBuilder.addClasspathClass(definition.asClasspathDefinition().getHolder());
+          }
           builder.addItem(definition);
         }
       }
-      additions = additionsBuilder.build();
+      committedProgramTypes = committedProgramTypesBuilder.build();
       amendedApplication = appBuilder.build();
     }
     return new CommittedItems(
-        nextSyntheticId, amendedApplication, builder.build().pruneItems(prunedItems), additions);
+        nextSyntheticId,
+        amendedApplication,
+        builder.build().pruneItems(prunedItems),
+        committedProgramTypes);
   }
 
   private static boolean verifyClassesAreInApp(
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 60e3079..ab4cd49 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -20,14 +20,14 @@
     Code generate(DexMethod method);
   }
 
-  private final SyntheticClassBuilder parent;
+  private final SyntheticClassBuilder<?, ?> parent;
   private DexString name = null;
   private DexProto proto = null;
   private CfVersion classFileVersion;
   private SyntheticCodeGenerator codeGenerator = null;
   private MethodAccessFlags accessFlags = null;
 
-  SyntheticMethodBuilder(SyntheticClassBuilder parent) {
+  SyntheticMethodBuilder(SyntheticClassBuilder<?, ?> parent) {
     this.parent = parent;
   }
 
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 0ba1127..87393f7 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.structural.RepresentativeMap;
 import com.google.common.hash.Hasher;
+import java.util.function.Consumer;
 
 /**
  * Definition of a synthetic method item.
@@ -15,7 +16,9 @@
  * <p>This class is internal to the synthetic items collection, thus package-protected.
  */
 class SyntheticMethodDefinition
-    extends SyntheticDefinition<SyntheticMethodReference, SyntheticMethodDefinition> {
+    extends SyntheticDefinition<
+        SyntheticMethodReference, SyntheticMethodDefinition, DexProgramClass>
+    implements SyntheticProgramDefinition {
 
   private final ProgramMethod method;
 
@@ -24,17 +27,34 @@
     this.method = method;
   }
 
+  @Override
+  public void apply(
+      Consumer<SyntheticMethodDefinition> onMethod,
+      Consumer<SyntheticProgramClassDefinition> onClass) {
+    onMethod.accept(this);
+  }
+
   public ProgramMethod getMethod() {
     return method;
   }
 
   @Override
+  public boolean isProgramDefinition() {
+    return true;
+  }
+
+  @Override
+  public SyntheticProgramDefinition asProgramDefinition() {
+    return this;
+  }
+
+  @Override
   SyntheticMethodReference toReference() {
     return new SyntheticMethodReference(getKind(), getContext(), method.getReference());
   }
 
   @Override
-  DexProgramClass getHolder() {
+  public DexProgramClass getHolder() {
     return method.getHolder();
   }
 
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
index cade86e..09a0f1a 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -18,7 +19,8 @@
  * <p>This class is internal to the synthetic items collection, thus package-protected.
  */
 class SyntheticMethodReference
-    extends SyntheticReference<SyntheticMethodReference, SyntheticMethodDefinition> {
+    extends SyntheticReference<SyntheticMethodReference, SyntheticMethodDefinition, DexProgramClass>
+    implements SyntheticProgramReference {
   final DexMethod method;
 
   SyntheticMethodReference(SyntheticKind kind, SynthesizingContext context, DexMethod method) {
@@ -73,8 +75,9 @@
   }
 
   @Override
-  void apply(
-      Consumer<SyntheticMethodReference> onMethod, Consumer<SyntheticClassReference> onClass) {
+  public void apply(
+      Consumer<SyntheticMethodReference> onMethod,
+      Consumer<SyntheticProgramClassReference> onClass) {
     onMethod.accept(this);
   }
 }
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 93c3f64..b33e260 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -22,12 +22,14 @@
    */
   public enum SyntheticKind {
     // Class synthetics.
+    COMPANION_CLASS("CompanionClass", false),
     LAMBDA("Lambda", false),
     // Method synthetics.
     BACKPORT("Backport", true),
     STATIC_INTERFACE_CALL("StaticInterfaceCall", true),
     TO_STRING_IF_NOT_NULL("ToStringIfNotNull", true),
     THROW_CCE_IF_NOT_NULL("ThrowCCEIfNotNull", true),
+    THROW_ICCE("ThrowICCE", true),
     THROW_NSME("ThrowNSME", true),
     TWR_CLOSE_RESOURCE("TwrCloseResource", true),
     SERVICE_LOADER("ServiceLoad", true);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassBuilder.java
new file mode 100644
index 0000000..6408621
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassBuilder.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.synthesis;
+
+import com.android.tools.r8.graph.ClassKind;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+
+public class SyntheticProgramClassBuilder
+    extends SyntheticClassBuilder<SyntheticProgramClassBuilder, DexProgramClass> {
+
+  SyntheticProgramClassBuilder(DexType type, SynthesizingContext context, DexItemFactory factory) {
+    super(type, context, factory);
+  }
+
+  @Override
+  public ClassKind<DexProgramClass> getClassKind() {
+    return ClassKind.PROGRAM;
+  }
+
+  @Override
+  public SyntheticProgramClassBuilder self() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java
new file mode 100644
index 0000000..8b42eb6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.synthesis;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.structural.RepresentativeMap;
+import com.google.common.hash.Hasher;
+import java.util.function.Consumer;
+
+/**
+ * Definition of a synthetic class item.
+ *
+ * <p>This class is internal to the synthetic items collection, thus package-protected.
+ */
+class SyntheticProgramClassDefinition
+    extends SyntheticClassDefinition<
+        SyntheticProgramClassReference, SyntheticProgramClassDefinition, DexProgramClass>
+    implements SyntheticProgramDefinition {
+
+  SyntheticProgramClassDefinition(
+      SyntheticKind kind, SynthesizingContext context, DexProgramClass clazz) {
+    super(kind, context, clazz);
+  }
+
+  @Override
+  public void apply(
+      Consumer<SyntheticMethodDefinition> onMethod,
+      Consumer<SyntheticProgramClassDefinition> onClass) {
+    onClass.accept(this);
+  }
+
+  @Override
+  public boolean isProgramDefinition() {
+    return true;
+  }
+
+  @Override
+  public SyntheticProgramDefinition asProgramDefinition() {
+    return this;
+  }
+
+  @Override
+  SyntheticProgramClassReference toReference() {
+    return new SyntheticProgramClassReference(getKind(), getContext(), clazz.getType());
+  }
+
+  @Override
+  public boolean isValid() {
+    return clazz.isPublic() && clazz.isFinal() && clazz.accessFlags.isSynthetic();
+  }
+
+  @Override
+  void internalComputeHash(Hasher hasher, RepresentativeMap map) {
+    clazz.hashWithTypeEquivalence(hasher, map);
+  }
+
+  @Override
+  int internalCompareTo(SyntheticProgramClassDefinition o, RepresentativeMap map) {
+    return clazz.compareWithTypeEquivalenceTo(o.clazz, map);
+  }
+
+  @Override
+  public String toString() {
+    return "SyntheticProgramClass{ clazz = "
+        + clazz.type.toSourceString()
+        + ", kind = "
+        + getKind()
+        + ", context = "
+        + getContext()
+        + " }";
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassReference.java
new file mode 100644
index 0000000..3472268
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassReference.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.synthesis;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Reference to a synthetic class item.
+ *
+ * <p>This class is internal to the synthetic items collection, thus package-protected.
+ */
+class SyntheticProgramClassReference
+    extends SyntheticClassReference<
+        SyntheticProgramClassReference, SyntheticProgramClassDefinition, DexProgramClass>
+    implements SyntheticProgramReference {
+
+  SyntheticProgramClassReference(SyntheticKind kind, SynthesizingContext context, DexType type) {
+    super(kind, context, type);
+  }
+
+  @Override
+  SyntheticProgramClassDefinition lookupDefinition(Function<DexType, DexClass> definitions) {
+    DexClass clazz = definitions.apply(type);
+    if (clazz == null) {
+      return null;
+    }
+    assert clazz.isProgramClass();
+    return new SyntheticProgramClassDefinition(getKind(), getContext(), clazz.asProgramClass());
+  }
+
+  @Override
+  SyntheticProgramClassReference rewrite(NonIdentityGraphLens lens) {
+    DexType rewritten = lens.lookupType(type);
+    // If the reference has been non-trivially rewritten the compiler has changed it and it can no
+    // longer be considered a synthetic. The context may or may not have changed.
+    if (type != rewritten && !lens.isSimpleRenaming(type, rewritten)) {
+      // If the referenced item is rewritten, it should be moved to another holder as the
+      // synthetic holder is no longer part of the synthetic collection.
+      assert SyntheticNaming.verifyNotInternalSynthetic(rewritten);
+      return null;
+    }
+    SynthesizingContext context = getContext().rewrite(lens);
+    if (context == getContext() && rewritten == type) {
+      return this;
+    }
+    // Ensure that if a synthetic moves its context moves consistently.
+    if (type != rewritten) {
+      context =
+          SynthesizingContext.fromSyntheticContextChange(rewritten, context, lens.dexItemFactory());
+      if (context == null) {
+        return null;
+      }
+    }
+    return new SyntheticProgramClassReference(getKind(), context, rewritten);
+  }
+
+  @Override
+  public void apply(
+      Consumer<SyntheticMethodReference> onMethod,
+      Consumer<SyntheticProgramClassReference> onClass) {
+    onClass.accept(this);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramDefinition.java
new file mode 100644
index 0000000..dd8953a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramDefinition.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.synthesis;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import java.util.function.Consumer;
+
+public interface SyntheticProgramDefinition {
+
+  void apply(
+      Consumer<SyntheticMethodDefinition> onMethod,
+      Consumer<SyntheticProgramClassDefinition> onClass);
+
+  DexProgramClass getHolder();
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramReference.java
new file mode 100644
index 0000000..4df1f46
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramReference.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.synthesis;
+
+import java.util.function.Consumer;
+
+public interface SyntheticProgramReference {
+
+  void apply(
+      Consumer<SyntheticMethodReference> onMethod,
+      Consumer<SyntheticProgramClassReference> onClass);
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
index ea33564..1a2d2fb 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import java.util.function.Consumer;
 import java.util.function.Function;
 
 /**
@@ -16,7 +15,9 @@
  * <p>This class is internal to the synthetic items collection, thus package-protected.
  */
 abstract class SyntheticReference<
-    R extends SyntheticReference<R, D>, D extends SyntheticDefinition<R, D>> {
+    R extends SyntheticReference<R, D, C>,
+    D extends SyntheticDefinition<R, D, C>,
+    C extends DexClass> {
 
   private final SyntheticKind kind;
   private final SynthesizingContext context;
@@ -41,7 +42,4 @@
   abstract DexType getHolder();
 
   abstract R rewrite(NonIdentityGraphLens lens);
-
-  abstract void apply(
-      Consumer<SyntheticMethodReference> onMethod, Consumer<SyntheticClassReference> onClass);
 }
diff --git a/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java b/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java
index 28eb17b..e14d8ed 100644
--- a/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ClasspathClassCollection.java
@@ -10,10 +10,19 @@
 
 /** Represents a collection of classpath classes. */
 public class ClasspathClassCollection extends ClassMap<DexClasspathClass> {
+
+  private ClasspathClassCollection() {
+    this(null);
+  }
+
   public ClasspathClassCollection(ClassProvider<DexClasspathClass> classProvider) {
     super(null, classProvider);
   }
 
+  public static ClasspathClassCollection empty() {
+    return new ClasspathClassCollection();
+  }
+
   @Override
   DexClasspathClass resolveClassConflict(DexClasspathClass a, DexClasspathClass b) {
     throw new CompilationError("Classpath type already present: " + a.type.toSourceString());