Merge "Add classlib and segments tool"
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 14dd1ae..aafe941 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.shaking.DiscardedChecker;
 import com.android.tools.r8.shaking.Enqueuer;
@@ -80,9 +80,9 @@
       }
       // Print -whyareyoukeeping results if any.
       if (whyAreYouKeepingConsumer != null) {
-        for (DexDefinition definition : mainDexRootSet.reasonAsked) {
+        for (DexReference reference : mainDexRootSet.reasonAsked) {
           whyAreYouKeepingConsumer.printWhyAreYouKeeping(
-              enqueuer.getGraphNode(definition), System.out);
+              enqueuer.getGraphNode(reference), System.out);
         }
       }
 
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 75b0c81..186caee 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -17,8 +17,8 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.ir.conversion.IRConverter;
@@ -403,8 +403,10 @@
               SeedMapper.seedMapperFromFile(
                   options.getProguardConfiguration().getApplyMappingFile());
           timing.begin("apply-mapping");
-          appView.setGraphLense(
-              new ProguardMapApplier(appView.withLiveness(), seedMapper).run(timing));
+          GraphLense applyMappingLense =
+              new ProguardMapApplier(appView.withLiveness(), seedMapper).run(timing);
+          rootSet = rootSet.rewrittenWithLense(applyMappingLense);
+          appView.setGraphLense(applyMappingLense);
           application = application.asDirect().rewrittenWithLense(appView.graphLense());
           appView.setAppInfo(
               appView
@@ -490,6 +492,7 @@
       // At this point all code has been mapped according to the graph lens. We cannot remove the
       // graph lens entirely, though, since it is needed for mapping all field and method signatures
       // back to the original program.
+      GraphLense finalLense = appView.graphLense();
       timing.begin("AppliedGraphLens construction");
       appView.setGraphLense(new AppliedGraphLens(appView, application.classes()));
       timing.end();
@@ -536,9 +539,9 @@
               .run();
         }
         if (whyAreYouKeepingConsumer != null) {
-          for (DexDefinition dexDefinition : mainDexRootSet.reasonAsked) {
+          for (DexReference reference : mainDexRootSet.reasonAsked) {
             whyAreYouKeepingConsumer.printWhyAreYouKeeping(
-                enqueuer.getGraphNode(dexDefinition), System.out);
+                enqueuer.getGraphNode(reference), System.out);
           }
         }
       }
@@ -578,9 +581,9 @@
 
             // Print reasons on the application after pruning, so that we reflect the actual result.
             if (whyAreYouKeepingConsumer != null) {
-              for (DexDefinition dexDefinition : rootSet.reasonAsked) {
+              for (DexReference reference : rootSet.reasonAsked) {
                 whyAreYouKeepingConsumer.printWhyAreYouKeeping(
-                    enqueuer.getGraphNode(dexDefinition), System.out);
+                    enqueuer.getGraphNode(reference), System.out);
               }
             }
             // Remove annotations that refer to types that no longer exist.
@@ -641,7 +644,8 @@
 
       // Validity checks.
       assert application.classes().stream().allMatch(DexClass::isValid);
-      assert rootSet.verifyKeptItemsAreKept(application, appView.appInfo(), options);
+      // Use the final lense while checking the validity of the final app against the root set.
+      assert rootSet.verifyKeptItemsAreKept(application, appView.appInfo(), finalLense, options);
       assert appView
           .graphLense()
           .verifyMappingToOriginalProgram(
diff --git a/src/main/java/com/android/tools/r8/StringConsumer.java b/src/main/java/com/android/tools/r8/StringConsumer.java
index 9c50682..2037cda 100644
--- a/src/main/java/com/android/tools/r8/StringConsumer.java
+++ b/src/main/java/com/android/tools/r8/StringConsumer.java
@@ -106,7 +106,7 @@
         if (parent != null && !parent.toFile().exists()) {
           Files.createDirectories(parent);
         }
-        Files.write(outputPath, string.getBytes(encoding));
+        com.google.common.io.Files.asCharSink(outputPath.toFile(), encoding).write(string);
       } catch (IOException e) {
         Origin origin = new PathOrigin(outputPath);
         handler.error(new ExceptionDiagnostic(e, origin));
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index c9edb88..028f5a2 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -66,6 +66,17 @@
     return app.classesWithDeterministicOrder();
   }
 
+  public DexDefinition definitionFor(DexReference reference) {
+    if (reference.isDexType()) {
+      return definitionFor(reference.asDexType());
+    }
+    if (reference.isDexMethod()) {
+      return definitionFor(reference.asDexMethod());
+    }
+    assert reference.isDexField();
+    return definitionFor(reference.asDexField());
+  }
+
   public DexClass definitionFor(DexType type) {
     return app.definitionFor(type);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 11d9730..bac74f6 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -10,6 +10,8 @@
 import com.android.tools.r8.utils.IteratorUtils;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.Sets;
@@ -25,6 +27,9 @@
 import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.function.Function;
 
 /**
  * A GraphLense implements a virtual view on top of the graph, used to delay global rewrites until
@@ -494,6 +499,23 @@
     return true;
   }
 
+  public ImmutableList<DexReference> rewriteReferencesConservatively(List<DexReference> original) {
+    ImmutableList.Builder<DexReference> builder = ImmutableList.builder();
+    for (DexReference item : original) {
+      if (item.isDexMethod()) {
+        DexMethod method = item.asDexMethod();
+        if (isContextFreeForMethod(method)) {
+          builder.add(lookupMethod(method));
+        } else {
+          builder.addAll(lookupMethodInAllContexts(method));
+        }
+      } else {
+        builder.add(lookupReference(item));
+      }
+    }
+    return builder.build();
+  }
+
   public ImmutableSet<DexReference> rewriteReferencesConservatively(Set<DexReference> original) {
     ImmutableSet.Builder<DexReference> builder = ImmutableSet.builder();
     for (DexReference item : original) {
@@ -511,6 +533,23 @@
     return builder.build();
   }
 
+  public Set<DexReference> rewriteMutableReferencesConservatively(Set<DexReference> original) {
+    Set<DexReference> result = Sets.newIdentityHashSet();
+    for (DexReference item : original) {
+      if (item.isDexMethod()) {
+        DexMethod method = item.asDexMethod();
+        if (isContextFreeForMethod(method)) {
+          result.add(lookupMethod(method));
+        } else {
+          result.addAll(lookupMethodInAllContexts(method));
+        }
+      } else {
+        result.add(lookupReference(item));
+      }
+    }
+    return result;
+  }
+
   public Object2BooleanMap<DexReference> rewriteReferencesConservatively(
       Object2BooleanMap<DexReference> original) {
     Object2BooleanMap<DexReference> result = new Object2BooleanArrayMap<>();
@@ -532,6 +571,22 @@
     return result;
   }
 
+  public ImmutableSet<DexType> rewriteTypesConservatively(Set<DexType> original) {
+    ImmutableSet.Builder<DexType> builder = ImmutableSet.builder();
+    for (DexType item : original) {
+      builder.add(lookupType(item));
+    }
+    return builder.build();
+  }
+
+  public Set<DexType> rewriteMutableTypesConservatively(Set<DexType> original) {
+    Set<DexType> result = Sets.newIdentityHashSet();
+    for (DexType item : original) {
+      result.add(lookupType(item));
+    }
+    return result;
+  }
+
   public ImmutableSortedSet<DexMethod> rewriteMethodsWithRenamedSignature(Set<DexMethod> methods) {
     ImmutableSortedSet.Builder<DexMethod> builder =
         new ImmutableSortedSet.Builder<>(PresortedComparable::slowCompare);
@@ -563,6 +618,45 @@
     return builder.build();
   }
 
+  public SortedSet<DexMethod> rewriteMutableMethodsConservatively(Set<DexMethod> original) {
+    SortedSet<DexMethod> result = new TreeSet<>(PresortedComparable::slowCompare);
+    if (isContextFreeForMethods()) {
+      for (DexMethod item : original) {
+        result.add(lookupMethod(item));
+      }
+    } else {
+      for (DexMethod item : original) {
+        // Avoid using lookupMethodInAllContexts when possible.
+        if (isContextFreeForMethod(item)) {
+          result.add(lookupMethod(item));
+        } else {
+          // The lense is context sensitive, but we do not have the context here. Therefore, we
+          // conservatively look up the method in all contexts.
+          result.addAll(lookupMethodInAllContexts(item));
+        }
+      }
+    }
+    return result;
+  }
+
+  public static <T extends DexReference, S> ImmutableMap<T, S> rewriteReferenceKeys(
+      Map<T, S> original, Function<T, T> rewrite) {
+    ImmutableMap.Builder<T, S> builder = new ImmutableMap.Builder<>();
+    for (T item : original.keySet()) {
+      builder.put(rewrite.apply(item), original.get(item));
+    }
+    return builder.build();
+  }
+
+  public static <T extends DexReference, S> Map<T, S> rewriteMutableReferenceKeys(
+      Map<T, S> original, Function<T, T> rewrite) {
+    Map<T, S> result = new IdentityHashMap<>();
+    for (T item : original.keySet()) {
+      result.put(rewrite.apply(item), original.get(item));
+    }
+    return result;
+  }
+
   public boolean verifyMappingToOriginalProgram(
       Iterable<DexProgramClass> classes,
       DexApplication originalApplication,
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index bef2765..a5070ca 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -686,6 +686,9 @@
   public boolean verifyNoImpreciseOrBottomTypes() {
     Predicate<Value> verifyValue =
         v -> {
+          assert !v.isNeverNull()
+              || (v.getTypeLattice().isReference()
+                  && v.getTypeLattice().nullability().isDefinitelyNotNull());
           assert v.getTypeLattice().isPreciseType();
           assert !v.getTypeLattice().isFineGrainedType();
           // For now we assume no bottom types on IR values. We may want to reconsider this for
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index fbdb9f3..0af7c9e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1905,7 +1905,7 @@
             String name = null;
             if (invokedMethod == appInfo.dexItemFactory.classMethods.getName) {
               if (code.options.enableMinification
-                  && !converter.rootSet.noObfuscation.contains(holder)) {
+                  && !converter.rootSet.noObfuscation.contains(holderType)) {
                 deferred = new DexItemBasedValueString(
                     holderType, new ClassNameComputationInfo(NAME));
               } else {
@@ -1915,7 +1915,7 @@
               // TODO(b/119426668): desugar Type#getTypeName
             } else if (invokedMethod == appInfo.dexItemFactory.classMethods.getCanonicalName) {
               if (code.options.enableMinification
-                  && !converter.rootSet.noObfuscation.contains(holder)) {
+                  && !converter.rootSet.noObfuscation.contains(holderType)) {
                 deferred = new DexItemBasedValueString(
                     holderType, new ClassNameComputationInfo(CANONICAL_NAME));
               } else {
@@ -1923,7 +1923,7 @@
               }
             } else if (invokedMethod == appInfo.dexItemFactory.classMethods.getSimpleName) {
               if (code.options.enableMinification
-                  && !converter.rootSet.noObfuscation.contains(holder)) {
+                  && !converter.rootSet.noObfuscation.contains(holderType)) {
                 deferred = new DexItemBasedValueString(
                     holderType, new ClassNameComputationInfo(SIMPLE_NAME));
               } else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index ad3fb38..3deeb63 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -6,13 +6,14 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialClassInitializer;
 import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -59,12 +60,16 @@
     this.appInfo = appInfo;
   }
 
-  private ProguardMemberRuleLookup lookupMemberRule(DexItem item) {
-    ProguardMemberRule rule = appInfo.noSideEffects.get(item);
+  private ProguardMemberRuleLookup lookupMemberRule(DexDefinition definition) {
+    if (definition == null) {
+      return null;
+    }
+    DexReference reference = definition.toReference();
+    ProguardMemberRule rule = appInfo.noSideEffects.get(reference);
     if (rule != null) {
       return new ProguardMemberRuleLookup(RuleType.ASSUME_NO_SIDE_EFFECTS, rule);
     }
-    rule = appInfo.assumedValues.get(item);
+    rule = appInfo.assumedValues.get(reference);
     if (rule != null) {
       return new ProguardMemberRuleLookup(RuleType.ASSUME_VALUES, rule);
     }
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index b02801c..e53f959 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -87,12 +87,10 @@
     this.classDictionary = options.getProguardConfiguration().getClassObfuscationDictionary();
     this.keepInnerClassStructure = options.getProguardConfiguration().getKeepAttributes().signature;
     this.noObfuscationTypes =
-        DexReference.filterDexType(
-            DexDefinition.mapToReference(rootSet.noObfuscation.stream()))
+        DexReference.filterDexType(rootSet.noObfuscation.stream())
             .collect(Collectors.toSet());
     this.keepPackageName =
-        DexReference.filterDexType(
-            DexDefinition.mapToReference(rootSet.keepPackageName.stream()))
+        DexReference.filterDexType(rootSet.keepPackageName.stream())
             .collect(Collectors.toSet());
 
     // Initialize top-level naming state.
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index 3b546a8..f00787b 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -67,8 +67,8 @@
       DexEncodedField encodedField,
       NamingState<DexType, ?> state,
       boolean isLibrary) {
-    if (isLibrary || rootSet.noObfuscation.contains(encodedField)) {
-      DexField field = encodedField.field;
+    DexField field = encodedField.field;
+    if (isLibrary || rootSet.noObfuscation.contains(field)) {
       state.reserveName(field.name, field.type);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index df11ba9..5f990ac 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -479,11 +479,12 @@
     return state;
   }
 
-  private void reserveNamesForMethod(DexEncodedMethod method,
-      boolean keepAll, NamingState<DexProto, ?> state) {
+  private void reserveNamesForMethod(
+      DexEncodedMethod encodedMethod, boolean keepAll, NamingState<DexProto, ?> state) {
+    DexMethod method = encodedMethod.method;
     if (keepAll || rootSet.noObfuscation.contains(method)) {
-      state.reserveName(method.method.name, method.method.proto);
-      globalState.reserveName(method.method.name, method.method.proto);
+      state.reserveName(method.name, method.proto);
+      globalState.reserveName(method.name, method.proto);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
index b28a1a7..07a62a8 100644
--- a/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
+++ b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.InternalOptions;
@@ -19,7 +20,7 @@
 
 public class DiscardedChecker {
 
-  private final Set<DexDefinition> checkDiscarded;
+  private final Set<DexReference> checkDiscarded;
   private final List<DexProgramClass> classes;
   private boolean fail = false;
   private final InternalOptions options;
@@ -55,7 +56,7 @@
   }
 
   private void checkItem(DexDefinition item) {
-    if (checkDiscarded.contains(item)) {
+    if (checkDiscarded.contains(item.toReference())) {
       options.reporter.info(
           new StringDiagnostic("Item " + item.toSourceString() + " was not discarded."));
       fail = true;
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 0761a71..13dd3ea 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.android.tools.r8.graph.GraphLense.rewriteReferenceKeys;
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentifier;
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
 import static com.android.tools.r8.shaking.AnnotationRemover.shouldKeepAnnotation;
@@ -63,7 +64,6 @@
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.Iterables;
@@ -297,22 +297,21 @@
     this.options = options;
   }
 
-  private void enqueueRootItems(Map<DexDefinition, Set<ProguardKeepRule>> items) {
+  private void enqueueRootItems(Map<DexReference, Set<ProguardKeepRule>> items) {
     items.entrySet().forEach(this::enqueueRootItem);
   }
 
-  private void enqueueRootItem(Entry<DexDefinition, Set<ProguardKeepRule>> root) {
-    enqueueRootItem(root.getKey(), root.getValue());
-  }
-
-  private void enqueueRootItem(DexDefinition item, ProguardKeepRule rule) {
-    enqueueRootItem(item, KeepReason.dueToKeepRule(rule));
+  private void enqueueRootItem(Entry<DexReference, Set<ProguardKeepRule>> root) {
+    DexDefinition item = appInfo.definitionFor(root.getKey());
+    if (item != null) {
+      enqueueRootItem(item, root.getValue());
+    }
   }
 
   private void enqueueRootItem(DexDefinition item, Set<ProguardKeepRule> rules) {
     assert !rules.isEmpty();
     if (keptGraphConsumer != null) {
-      GraphNode node = getGraphNode(item);
+      GraphNode node = getGraphNode(item.toReference());
       for (ProguardKeepRule rule : rules) {
         registerEdge(node, KeepReason.dueToKeepRule(rule));
       }
@@ -322,7 +321,7 @@
 
   private void enqueueRootItem(DexDefinition item, KeepReason reason) {
     if (keptGraphConsumer != null) {
-      registerEdge(getGraphNode(item), reason);
+      registerEdge(getGraphNode(item.toReference()), reason);
     }
     internalEnqueueRootItem(item, reason);
   }
@@ -368,16 +367,17 @@
   }
 
   private void enqueueHolderIfDependentNonStaticMember(
-      DexClass holder, Map<DexDefinition, Set<ProguardKeepRule>> dependentItems) {
+      DexClass holder, Map<DexReference, Set<ProguardKeepRule>> dependentItems) {
     // Check if any dependent members are not static, and in that case enqueue the class as well.
     // Having a dependent rule like -keepclassmembers with non static items indicates that class
     // instances will be present even if tracing do not find any instantiation. See b/115867670.
-    for (Entry<DexDefinition, Set<ProguardKeepRule>> entry : dependentItems.entrySet()) {
-      DexDefinition dependentItem = entry.getKey();
-      if (dependentItem.isDexClass()) {
+    for (Entry<DexReference, Set<ProguardKeepRule>> entry : dependentItems.entrySet()) {
+      DexReference dependentItem = entry.getKey();
+      if (dependentItem.isDexType()) {
         continue;
       }
-      if (!dependentItem.isStaticMember()) {
+      DexDefinition dependentDefinition = appInfo.definitionFor(dependentItem);
+      if (!dependentDefinition.isStaticMember()) {
         enqueueRootItem(holder, entry.getValue());
         // Enough to enqueue the known holder once.
         break;
@@ -810,7 +810,7 @@
         annotations.forEach(this::handleAnnotationOfLiveType);
       }
 
-      Map<DexDefinition, Set<ProguardKeepRule>> dependentItems = rootSet.getDependentItems(holder);
+      Map<DexReference, Set<ProguardKeepRule>> dependentItems = rootSet.getDependentItems(holder);
       enqueueHolderIfDependentNonStaticMember(holder, dependentItems);
       // Add all dependent members to the workqueue.
       enqueueRootItems(dependentItems);
@@ -1325,7 +1325,7 @@
       // TODO(sgjesse): Does this have to be enqueued as a root item? Right now it is done as the
       // marking of not renaming is in the root set.
       enqueueRootItem(valuesMethod, reason);
-      rootSet.noObfuscation.add(valuesMethod);
+      rootSet.noObfuscation.add(valuesMethod.toReference());
     }
   }
 
@@ -1465,17 +1465,14 @@
                   targetedMethods.getItems(),
                   executorService);
           ConsequentRootSet consequentRootSet = ifRuleEvaluator.run(liveTypes);
+          rootSet.addConsequentRootSet(consequentRootSet);
           enqueueRootItems(consequentRootSet.noShrinking);
-          rootSet.neverInline.addAll(consequentRootSet.neverInline);
-          rootSet.neverClassInline.addAll(consequentRootSet.neverClassInline);
-          rootSet.noOptimization.addAll(consequentRootSet.noOptimization);
-          rootSet.noObfuscation.addAll(consequentRootSet.noObfuscation);
-          rootSet.addDependentItems(consequentRootSet.dependentNoShrinking);
           // Check if any newly dependent members are not static, and in that case find the holder
           // and enqueue it as well. This is -if version of workaround for b/115867670.
           consequentRootSet.dependentNoShrinking.forEach((precondition, dependentItems) -> {
-            if (precondition.isDexClass()) {
-              enqueueHolderIfDependentNonStaticMember(precondition.asDexClass(), dependentItems);
+            if (precondition.isDexType()) {
+              DexClass preconditionHolder = appInfo.definitionFor(precondition.asDexType());
+              enqueueHolderIfDependentNonStaticMember(preconditionHolder, dependentItems);
             }
             // Add all dependent members to the workqueue.
             enqueueRootItems(dependentItems);
@@ -1948,11 +1945,11 @@
     /**
      * All items with assumenosideeffects rule.
      */
-    public final Map<DexDefinition, ProguardMemberRule> noSideEffects;
+    public final Map<DexReference, ProguardMemberRule> noSideEffects;
     /**
      * All items with assumevalues rule.
      */
-    public final Map<DexDefinition, ProguardMemberRule> assumedValues;
+    public final Map<DexReference, ProguardMemberRule> assumedValues;
     /**
      * All methods that should be inlined if possible due to a configuration directive.
      */
@@ -1965,9 +1962,13 @@
      * All methods that *must* never be inlined due to a configuration directive (testing only).
      */
     public final Set<DexMethod> neverInline;
-    /** All methods that may not have any parameters with a constant value removed. */
+    /**
+     * All methods that may not have any parameters with a constant value removed.
+     */
     public final Set<DexMethod> keepConstantArguments;
-    /** All methods that may not have any unused arguments removed. */
+    /**
+     * All methods that may not have any unused arguments removed.
+     */
     public final Set<DexMethod> keepUnusedArguments;
     /**
      * All types that *must* never be inlined due to a configuration directive (testing only).
@@ -2143,16 +2144,14 @@
       this.callSites = previous.callSites;
       this.brokenSuperInvokes = lense.rewriteMethodsConservatively(previous.brokenSuperInvokes);
       this.prunedTypes = rewriteItems(previous.prunedTypes, lense::lookupType);
-      assert lense.assertDefinitionsNotModified(previous.noSideEffects.keySet());
-      this.noSideEffects = previous.noSideEffects;
-      assert lense.assertDefinitionsNotModified(previous.assumedValues.keySet());
-      this.assumedValues = previous.assumedValues;
+      this.noSideEffects = rewriteReferenceKeys(previous.noSideEffects, lense::lookupReference);
+      this.assumedValues = rewriteReferenceKeys(previous.assumedValues, lense::lookupReference);
       assert lense.assertDefinitionsNotModified(
           previous.alwaysInline.stream()
               .map(this::definitionFor)
               .filter(Objects::nonNull)
               .collect(Collectors.toList()));
-      this.alwaysInline = previous.alwaysInline;
+      this.alwaysInline = lense.rewriteMethodsWithRenamedSignature(previous.alwaysInline);
       this.forceInline = lense.rewriteMethodsWithRenamedSignature(previous.forceInline);
       this.neverInline = lense.rewriteMethodsWithRenamedSignature(previous.neverInline);
       this.keepConstantArguments =
@@ -2165,7 +2164,7 @@
               .filter(Objects::nonNull)
               .collect(Collectors.toList()));
       this.neverClassInline = rewriteItems(previous.neverClassInline, lense::lookupType);
-      this.neverMerge = previous.neverMerge;
+      this.neverMerge = rewriteItems(previous.neverMerge, lense::lookupType);
       this.identifierNameStrings =
           lense.rewriteReferencesConservatively(previous.identifierNameStrings);
       // Switchmap classes should never be affected by renaming.
@@ -2174,8 +2173,8 @@
               .map(this::definitionFor)
               .filter(Objects::nonNull)
               .collect(Collectors.toList()));
-      this.switchMaps = previous.switchMaps;
-      this.ordinalsMaps = rewriteKeys(previous.ordinalsMaps, lense::lookupType);
+      this.switchMaps = rewriteReferenceKeys(previous.switchMaps, lense::lookupField);
+      this.ordinalsMaps = rewriteReferenceKeys(previous.ordinalsMaps, lense::lookupType);
       // Sanity check sets after rewriting.
       assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
       assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
@@ -2318,14 +2317,6 @@
       return builder.build();
     }
 
-    private static <T extends PresortedComparable<T>, S> ImmutableMap<T, S> rewriteKeys(
-        Map<T, S> original, Function<T, T> rewrite) {
-      ImmutableMap.Builder<T, S> builder = new ImmutableMap.Builder<>();
-      for (T item : original.keySet()) {
-        builder.put(rewrite.apply(item), original.get(item));
-      }
-      return builder.build();
-    }
 
     private static <T extends PresortedComparable<T>, S>
         Map<T, Set<S>> rewriteKeysWhileMergingValues(
@@ -2820,15 +2811,15 @@
     return reason.getSourceNode(this);
   }
 
-  public GraphNode getGraphNode(DexDefinition item) {
-    if (item instanceof DexClass) {
-      return getClassGraphNode(((DexClass) item).type);
+  public GraphNode getGraphNode(DexReference reference) {
+    if (reference.isDexType()) {
+      return getClassGraphNode(reference.asDexType());
     }
-    if (item instanceof DexEncodedMethod) {
-      return getMethodGraphNode(((DexEncodedMethod) item).method);
+    if (reference.isDexMethod()) {
+      return getMethodGraphNode(reference.asDexMethod());
     }
-    if (item instanceof DexEncodedField) {
-      return getFieldGraphNode(((DexEncodedField) item).field);
+    if (reference.isDexField()) {
+      return getFieldGraphNode(reference.asDexField());
     }
     throw new Unreachable();
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 203cf82..43c39a6 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -3,6 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.android.tools.r8.graph.GraphLense.rewriteMutableReferenceKeys;
+import static com.android.tools.r8.graph.GraphLense.rewriteReferenceKeys;
+
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
@@ -22,6 +25,7 @@
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
@@ -53,6 +57,7 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
+import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -61,14 +66,14 @@
   private final AppView<? extends AppInfo> appView;
   private final DirectMappedDexApplication application;
   private final Iterable<? extends ProguardConfigurationRule> rules;
-  private final Map<DexDefinition, Set<ProguardKeepRule>> noShrinking = new IdentityHashMap<>();
-  private final Set<DexDefinition> noOptimization = Sets.newIdentityHashSet();
-  private final Set<DexDefinition> noObfuscation = Sets.newIdentityHashSet();
-  private final LinkedHashMap<DexDefinition, DexDefinition> reasonAsked = new LinkedHashMap<>();
-  private final Set<DexDefinition> keepPackageName = Sets.newIdentityHashSet();
+  private final Map<DexReference, Set<ProguardKeepRule>> noShrinking = new IdentityHashMap<>();
+  private final Set<DexReference> noOptimization = Sets.newIdentityHashSet();
+  private final Set<DexReference> noObfuscation = Sets.newIdentityHashSet();
+  private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>();
+  private final Set<DexReference> keepPackageName = Sets.newIdentityHashSet();
   private final Set<ProguardConfigurationRule> rulesThatUseExtendsOrImplementsWrong =
       Sets.newIdentityHashSet();
-  private final Set<DexDefinition> checkDiscarded = Sets.newIdentityHashSet();
+  private final Set<DexReference> checkDiscarded = Sets.newIdentityHashSet();
   private final Set<DexMethod> alwaysInline = Sets.newIdentityHashSet();
   private final Set<DexMethod> forceInline = Sets.newIdentityHashSet();
   private final Set<DexMethod> neverInline = Sets.newIdentityHashSet();
@@ -76,10 +81,10 @@
   private final Set<DexMethod> keepUnusedArguments = Sets.newIdentityHashSet();
   private final Set<DexType> neverClassInline = Sets.newIdentityHashSet();
   private final Set<DexType> neverMerge = Sets.newIdentityHashSet();
-  private final Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentNoShrinking =
+  private final Map<DexReference, Map<DexReference, Set<ProguardKeepRule>>> dependentNoShrinking =
       new IdentityHashMap<>();
-  private final Map<DexDefinition, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
-  private final Map<DexDefinition, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
+  private final Map<DexReference, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
+  private final Map<DexReference, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
   private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
   private final InternalOptions options;
 
@@ -863,11 +868,11 @@
     }
     // Keep the type if the item is also kept.
     dependentNoShrinking
-        .computeIfAbsent(item, x -> new IdentityHashMap<>())
-        .computeIfAbsent(definition, k -> new HashSet<>())
+        .computeIfAbsent(item.toReference(), x -> new IdentityHashMap<>())
+        .computeIfAbsent(type, k -> new HashSet<>())
         .add(context);
     // Unconditionally add to no-obfuscation, as that is only checked for surviving items.
-    noObfuscation.add(definition);
+    noObfuscation.add(type);
   }
 
   private void includeDescriptorClasses(DexDefinition item, ProguardKeepRule context) {
@@ -903,32 +908,32 @@
       if (!modifiers.allowsShrinking) {
         if (precondition != null) {
           dependentNoShrinking
-              .computeIfAbsent(precondition, x -> new IdentityHashMap<>())
-              .computeIfAbsent(item, i -> new HashSet<>())
+              .computeIfAbsent(precondition.toReference(), x -> new IdentityHashMap<>())
+              .computeIfAbsent(item.toReference(), i -> new HashSet<>())
               .add(keepRule);
         } else {
-          noShrinking.computeIfAbsent(item, i -> new HashSet<>()).add(keepRule);
+          noShrinking.computeIfAbsent(item.toReference(), i -> new HashSet<>()).add(keepRule);
         }
       }
       if (!modifiers.allowsOptimization) {
-        noOptimization.add(item);
+        noOptimization.add(item.toReference());
       }
       if (!modifiers.allowsObfuscation) {
-        noObfuscation.add(item);
+        noObfuscation.add(item.toReference());
       }
       if (modifiers.includeDescriptorClasses) {
         includeDescriptorClasses(item, keepRule);
       }
     } else if (context instanceof ProguardAssumeNoSideEffectRule) {
-      noSideEffects.put(item, rule);
+      noSideEffects.put(item.toReference(), rule);
     } else if (context instanceof ProguardWhyAreYouKeepingRule) {
-      reasonAsked.computeIfAbsent(item, i -> i);
+      reasonAsked.computeIfAbsent(item.toReference(), i -> i);
     } else if (context instanceof ProguardKeepPackageNamesRule) {
-      keepPackageName.add(item);
+      keepPackageName.add(item.toReference());
     } else if (context instanceof ProguardAssumeValuesRule) {
-      assumedValues.put(item, rule);
+      assumedValues.put(item.toReference(), rule);
     } else if (context instanceof ProguardCheckDiscardRule) {
-      checkDiscarded.add(item);
+      checkDiscarded.add(item.toReference());
     } else if (context instanceof InlineRule) {
       if (item.isDexEncodedMethod()) {
         switch (((InlineRule) context).getType()) {
@@ -984,12 +989,12 @@
 
   public static class RootSet {
 
-    public final Map<DexDefinition, Set<ProguardKeepRule>> noShrinking;
-    public final Set<DexDefinition> noOptimization;
-    public final Set<DexDefinition> noObfuscation;
-    public final ImmutableList<DexDefinition> reasonAsked;
-    public final Set<DexDefinition> keepPackageName;
-    public final Set<DexDefinition> checkDiscarded;
+    public final Map<DexReference, Set<ProguardKeepRule>> noShrinking;
+    public final Set<DexReference> noOptimization;
+    public final Set<DexReference> noObfuscation;
+    public final ImmutableList<DexReference> reasonAsked;
+    public final Set<DexReference> keepPackageName;
+    public final Set<DexReference> checkDiscarded;
     public final Set<DexMethod> alwaysInline;
     public final Set<DexMethod> forceInline;
     public final Set<DexMethod> neverInline;
@@ -997,20 +1002,20 @@
     public final Set<DexMethod> keepUnusedArguments;
     public final Set<DexType> neverClassInline;
     public final Set<DexType> neverMerge;
-    public final Map<DexDefinition, ProguardMemberRule> noSideEffects;
-    public final Map<DexDefinition, ProguardMemberRule> assumedValues;
-    private final Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>>
+    public final Map<DexReference, ProguardMemberRule> noSideEffects;
+    public final Map<DexReference, ProguardMemberRule> assumedValues;
+    private final Map<DexReference, Map<DexReference, Set<ProguardKeepRule>>>
         dependentNoShrinking;
     public final Set<DexReference> identifierNameStrings;
     public final Set<ProguardIfRule> ifRules;
 
     private RootSet(
-        Map<DexDefinition, Set<ProguardKeepRule>> noShrinking,
-        Set<DexDefinition> noOptimization,
-        Set<DexDefinition> noObfuscation,
-        ImmutableList<DexDefinition> reasonAsked,
-        Set<DexDefinition> keepPackageName,
-        Set<DexDefinition> checkDiscarded,
+        Map<DexReference, Set<ProguardKeepRule>> noShrinking,
+        Set<DexReference> noOptimization,
+        Set<DexReference> noObfuscation,
+        ImmutableList<DexReference> reasonAsked,
+        Set<DexReference> keepPackageName,
+        Set<DexReference> checkDiscarded,
         Set<DexMethod> alwaysInline,
         Set<DexMethod> forceInline,
         Set<DexMethod> neverInline,
@@ -1018,12 +1023,12 @@
         Set<DexMethod> keepUnusedArguments,
         Set<DexType> neverClassInline,
         Set<DexType> neverMerge,
-        Map<DexDefinition, ProguardMemberRule> noSideEffects,
-        Map<DexDefinition, ProguardMemberRule> assumedValues,
-        Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentNoShrinking,
+        Map<DexReference, ProguardMemberRule> noSideEffects,
+        Map<DexReference, ProguardMemberRule> assumedValues,
+        Map<DexReference, Map<DexReference, Set<ProguardKeepRule>>> dependentNoShrinking,
         Set<DexReference> identifierNameStrings,
         Set<ProguardIfRule> ifRules) {
-      this.noShrinking = Collections.unmodifiableMap(noShrinking);
+      this.noShrinking = noShrinking;
       this.noOptimization = noOptimization;
       this.noObfuscation = noObfuscation;
       this.reasonAsked = reasonAsked;
@@ -1043,33 +1048,81 @@
       this.ifRules = Collections.unmodifiableSet(ifRules);
     }
 
+    private RootSet(RootSet previous, GraphLense lense) {
+      this.noShrinking = rewriteMutableReferenceKeys(previous.noShrinking, lense::lookupReference);
+      this.noOptimization = lense.rewriteMutableReferencesConservatively(previous.noOptimization);
+      this.noObfuscation = lense.rewriteMutableReferencesConservatively(previous.noObfuscation);
+      this.reasonAsked = lense.rewriteReferencesConservatively(previous.reasonAsked);
+      this.keepPackageName = lense.rewriteReferencesConservatively(previous.keepPackageName);
+      this.checkDiscarded = lense.rewriteReferencesConservatively(previous.checkDiscarded);
+      this.alwaysInline = lense.rewriteMethodsConservatively(previous.alwaysInline);
+      this.forceInline = lense.rewriteMethodsConservatively(previous.forceInline);
+      this.neverInline = lense.rewriteMutableMethodsConservatively(previous.neverInline);
+      this.keepConstantArguments =
+          lense.rewriteMutableMethodsConservatively(previous.keepConstantArguments);
+      this.keepUnusedArguments =
+          lense.rewriteMutableMethodsConservatively(previous.keepUnusedArguments);
+      this.neverClassInline = lense.rewriteMutableTypesConservatively(previous.neverClassInline);
+      this.neverMerge = lense.rewriteTypesConservatively(previous.neverMerge);
+      this.noSideEffects = rewriteReferenceKeys(previous.noSideEffects, lense::lookupReference);
+      this.assumedValues = rewriteReferenceKeys(previous.assumedValues, lense::lookupReference);
+      this.dependentNoShrinking =
+          rewriteDependentReferenceKeys(previous.dependentNoShrinking, lense::lookupReference);
+      this.identifierNameStrings =
+          lense.rewriteReferencesConservatively(previous.identifierNameStrings);
+      this.ifRules = Collections.unmodifiableSet(previous.ifRules);
+    }
+
+    public RootSet rewrittenWithLense(GraphLense lense) {
+      return new RootSet(this, lense);
+    }
+
+    private static <T extends DexReference, S> Map<T, Map<T, S>> rewriteDependentReferenceKeys(
+        Map<T, Map<T, S>> original, Function<T, T> rewrite) {
+      Map<T, Map<T, S>> result = new IdentityHashMap<>();
+      for (T item : original.keySet()) {
+        result.put(rewrite.apply(item), rewriteReferenceKeys(original.get(item), rewrite));
+      }
+      return result;
+    }
+
+    void addConsequentRootSet(ConsequentRootSet consequentRootSet) {
+      neverInline.addAll(consequentRootSet.neverInline);
+      neverClassInline.addAll(consequentRootSet.neverClassInline);
+      noOptimization.addAll(consequentRootSet.noOptimization);
+      noObfuscation.addAll(consequentRootSet.noObfuscation);
+      addDependentItems(consequentRootSet.dependentNoShrinking);
+    }
+
     // Add dependent items that depend on -if rules.
-    void addDependentItems(
-        Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentItems) {
+    private void addDependentItems(
+        Map<DexReference, Map<DexReference, Set<ProguardKeepRule>>> dependentItems) {
       dependentItems.forEach(
-          (def, dependence) ->
+          (reference, dependence) ->
               dependentNoShrinking
-                  .computeIfAbsent(def, x -> new IdentityHashMap<>())
+                  .computeIfAbsent(reference, x -> new IdentityHashMap<>())
                   .putAll(dependence));
     }
 
-    Map<DexDefinition, Set<ProguardKeepRule>> getDependentItems(DexDefinition item) {
-      return Collections
-          .unmodifiableMap(dependentNoShrinking.getOrDefault(item, Collections.emptyMap()));
+    Map<DexReference, Set<ProguardKeepRule>> getDependentItems(DexDefinition item) {
+      return Collections.unmodifiableMap(
+          dependentNoShrinking.getOrDefault(item.toReference(), Collections.emptyMap()));
     }
 
     public boolean verifyKeptFieldsAreAccessedAndLive(AppInfoWithLiveness appInfo) {
-      for (DexDefinition definition : noShrinking.keySet()) {
-        if (definition.isDexEncodedField()) {
-          DexEncodedField field = definition.asDexEncodedField();
-          if (field.isStatic() || isKeptDirectlyOrIndirectly(field.field.clazz, appInfo)) {
+      for (DexReference reference : noShrinking.keySet()) {
+        if (reference.isDexField()) {
+          DexField field = reference.asDexField();
+          DexEncodedField encodedField = appInfo.definitionFor(field);
+          if (encodedField != null
+              && (encodedField.isStatic() || isKeptDirectlyOrIndirectly(field.clazz, appInfo))) {
             // TODO(b/121354886): Enable asserts for reads and writes.
-            /*assert appInfo.fieldsRead.contains(field.field)
-                : "Expected kept field `" + field.field.toSourceString() + "` to be read";
-            assert appInfo.fieldsWritten.contains(field.field)
-                : "Expected kept field `" + field.field.toSourceString() + "` to be written";*/
-            assert appInfo.liveFields.contains(field.field)
-                : "Expected kept field `" + field.field.toSourceString() + "` to be live";
+            /*assert appInfo.fieldsRead.contains(field)
+                : "Expected kept field `" + field.toSourceString() + "` to be read";
+            assert appInfo.fieldsWritten.contains(field)
+                : "Expected kept field `" + field.toSourceString() + "` to be written";*/
+            assert appInfo.liveFields.contains(field)
+                : "Expected kept field `" + field.toSourceString() + "` to be live";
           }
         }
       }
@@ -1077,17 +1130,17 @@
     }
 
     public boolean verifyKeptMethodsAreTargetedAndLive(AppInfoWithLiveness appInfo) {
-      for (DexDefinition definition : noShrinking.keySet()) {
-        if (definition.isDexEncodedMethod()) {
-          DexEncodedMethod method = definition.asDexEncodedMethod();
-          assert appInfo.targetedMethods.contains(method.method)
-              : "Expected kept method `" + method.method.toSourceString() + "` to be targeted";
-
-          if (!method.accessFlags.isAbstract()
-              && isKeptDirectlyOrIndirectly(method.method.holder, appInfo)) {
-            assert appInfo.liveMethods.contains(method.method)
+      for (DexReference reference : noShrinking.keySet()) {
+        if (reference.isDexMethod()) {
+          DexMethod method = reference.asDexMethod();
+          assert appInfo.targetedMethods.contains(method)
+              : "Expected kept method `" + method.toSourceString() + "` to be targeted";
+          DexEncodedMethod encodedMethod = appInfo.definitionFor(method);
+          if (!encodedMethod.accessFlags.isAbstract()
+              && isKeptDirectlyOrIndirectly(method.holder, appInfo)) {
+            assert appInfo.liveMethods.contains(method)
                 : "Expected non-abstract kept method `"
-                    + method.method.toSourceString()
+                    + method.toSourceString()
                     + "` to be live";
           }
         }
@@ -1096,37 +1149,59 @@
     }
 
     public boolean verifyKeptTypesAreLive(AppInfoWithLiveness appInfo) {
-      for (DexDefinition definition : noShrinking.keySet()) {
-        if (definition.isDexClass()) {
-          DexClass clazz = definition.asDexClass();
-          assert appInfo.liveTypes.contains(clazz.type)
-              : "Expected kept type `" + clazz.type.toSourceString() + "` to be live";
+      for (DexReference reference : noShrinking.keySet()) {
+        if (reference.isDexType()) {
+          DexType type = reference.asDexType();
+          assert appInfo.liveTypes.contains(type)
+              : "Expected kept type `" + type.toSourceString() + "` to be live";
         }
       }
       return true;
     }
 
     private boolean isKeptDirectlyOrIndirectly(DexType type, AppInfoWithLiveness appInfo) {
+      if (noShrinking.containsKey(type)) {
+        return true;
+      }
       DexClass clazz = appInfo.definitionFor(type);
       if (clazz == null) {
         return false;
       }
-      if (noShrinking.containsKey(clazz)) {
-        return true;
-      }
       if (clazz.superType != null) {
         return isKeptDirectlyOrIndirectly(clazz.superType, appInfo);
       }
       return false;
     }
 
-    public boolean verifyKeptItemsAreKept(
-        DexApplication application, AppInfo appInfo, InternalOptions options) {
-      if (options.getProguardConfiguration().hasApplyMappingFile()) {
-        // TODO(b/121295633): Root set is obsolete after mapping has been applied.
-        return true;
+    // TODO(b/121240523): Ideally, these default interface methods should be delegated.
+    private boolean isDesugaredMethod(
+        AppInfo appInfo, GraphLense lense, DexClass clazz, DexMethod method) {
+      if (clazz == null) {
+        clazz = appInfo.definitionFor(method.getHolder());
       }
+      Set<DexMethod> lookupMethods = lense.lookupMethodInAllContexts(method);
+      if (lookupMethods.size() == 1) {
+        for (DexMethod lookupMethod : lookupMethods) {
+          if (lookupMethod == method) {
+            continue;
+          }
+          DexEncodedMethod encodedMethod = appInfo.definitionFor(lookupMethod);
+          if (clazz != null
+              && clazz.isInterface()
+              && encodedMethod != null
+              && encodedMethod.hasCode()) {
+            return true;
+          }
+        }
+      }
+      return false;
+    }
 
+    public boolean verifyKeptItemsAreKept(
+        DexApplication application,
+        AppInfo appInfo,
+        GraphLense lense,
+        InternalOptions options) {
       boolean isInterfaceMethodDesugaringEnabled =
           options.enableDesugaring
               && options.interfaceMethodDesugaring == OffOrAuto.Auto
@@ -1136,33 +1211,36 @@
           appInfo.hasLiveness() ? appInfo.withLiveness().pinnedItems : null;
 
       // Create a mapping from each required type to the set of required members on that type.
-      Map<DexType, Set<DexDefinition>> requiredDefinitionsPerType = new IdentityHashMap<>();
-      for (DexDefinition definition : noShrinking.keySet()) {
+      Map<DexType, Set<DexReference>> requiredReferencesPerType = new IdentityHashMap<>();
+      for (DexReference reference : noShrinking.keySet()) {
         // Check that `pinnedItems` is a super set of the root set.
-        assert pinnedItems == null || pinnedItems.contains(definition.toReference());
-        if (definition.isDexClass()) {
-          DexType type = definition.toReference().asDexType();
-          requiredDefinitionsPerType.putIfAbsent(type, Sets.newIdentityHashSet());
+        assert pinnedItems == null || pinnedItems.contains(reference)
+            || (reference.isDexMethod()
+                && isInterfaceMethodDesugaringEnabled
+                && isDesugaredMethod(appInfo, lense, null, reference.asDexMethod()));
+        if (reference.isDexType()) {
+          DexType type = reference.asDexType();
+          requiredReferencesPerType.putIfAbsent(type, Sets.newIdentityHashSet());
         } else {
-          assert definition.isDexEncodedField() || definition.isDexEncodedMethod();
-          Descriptor<?, ?> descriptor = definition.toReference().asDescriptor();
-          requiredDefinitionsPerType
+          assert reference.isDexField() || reference.isDexMethod();
+          Descriptor<?, ?> descriptor = reference.asDescriptor();
+          requiredReferencesPerType
               .computeIfAbsent(descriptor.getHolder(), key -> Sets.newIdentityHashSet())
-              .add(definition);
+              .add(reference);
         }
       }
 
       // Run through each class in the program and check that it has members it must have.
       for (DexProgramClass clazz : application.classes()) {
-        Set<DexDefinition> requiredDefinitions =
-            requiredDefinitionsPerType.getOrDefault(clazz.type, ImmutableSet.of());
+        Set<DexReference> requiredReferences =
+            requiredReferencesPerType.getOrDefault(clazz.type, ImmutableSet.of());
 
         Set<DexField> fields = null;
         Set<DexMethod> methods = null;
 
-        for (DexDefinition requiredDefinition : requiredDefinitions) {
-          if (requiredDefinition.isDexEncodedField()) {
-            DexEncodedField requiredField = requiredDefinition.asDexEncodedField();
+        for (DexReference requiredReference : requiredReferences) {
+          if (requiredReference.isDexField()) {
+            DexField requiredField = requiredReference.asDexField();
             if (fields == null) {
               // Create a Set of the fields to avoid quadratic behavior.
               fields =
@@ -1170,15 +1248,12 @@
                       .map(DexEncodedField::getKey)
                       .collect(Collectors.toSet());
             }
-            assert fields.contains(requiredField.field);
-          } else if (requiredDefinition.isDexEncodedMethod()) {
-            DexEncodedMethod requiredMethod = requiredDefinition.asDexEncodedMethod();
-            if (isInterfaceMethodDesugaringEnabled) {
-              if (clazz.isInterface() && requiredMethod.hasCode()) {
-                // TODO(b/121240523): Ideally, these default interface methods should not be in the
-                // root set.
-                continue;
-              }
+            assert fields.contains(requiredField);
+          } else if (requiredReference.isDexMethod()) {
+            DexMethod requiredMethod = requiredReference.asDexMethod();
+            if (isInterfaceMethodDesugaringEnabled
+                && isDesugaredMethod(appInfo, lense, clazz, requiredMethod)) {
+              continue;
             }
             if (methods == null) {
               // Create a Set of the methods to avoid quadratic behavior.
@@ -1187,21 +1262,21 @@
                       .map(DexEncodedMethod::getKey)
                       .collect(Collectors.toSet());
             }
-            assert methods.contains(requiredMethod.method);
+            assert methods.contains(requiredMethod);
           } else {
             assert false;
           }
         }
-        requiredDefinitionsPerType.remove(clazz.type);
+        requiredReferencesPerType.remove(clazz.type);
       }
 
       // If the map is non-empty, then a type in the root set was not in the application.
-      if (!requiredDefinitionsPerType.isEmpty()) {
-        DexType type = requiredDefinitionsPerType.keySet().iterator().next();
+      if (!requiredReferencesPerType.isEmpty()) {
+        DexType type = requiredReferencesPerType.keySet().iterator().next();
         DexClass clazz = application.definitionFor(type);
         assert clazz == null || clazz.isProgramClass()
             : "Unexpected library type in root set: `" + type + "`";
-        assert requiredDefinitionsPerType.isEmpty()
+        assert requiredReferencesPerType.isEmpty()
             : "Expected type `" + type.toSourceString() + "` to be present";
       }
 
@@ -1227,7 +1302,7 @@
 
       builder.append("\n\nNo Shrinking:");
       noShrinking.keySet().stream()
-          .sorted(Comparator.comparing(DexDefinition::toSourceString))
+          .sorted(Comparator.comparing(DexReference::toSourceString))
           .forEach(a -> builder
               .append("\n").append(a.toSourceString()).append(" ").append(noShrinking.get(a)));
       builder.append("\n");
@@ -1239,18 +1314,18 @@
   static class ConsequentRootSet {
     final Set<DexMethod> neverInline;
     final Set<DexType> neverClassInline;
-    final Map<DexDefinition, Set<ProguardKeepRule>> noShrinking;
-    final Set<DexDefinition> noOptimization;
-    final Set<DexDefinition> noObfuscation;
-    final Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentNoShrinking;
+    final Map<DexReference, Set<ProguardKeepRule>> noShrinking;
+    final Set<DexReference> noOptimization;
+    final Set<DexReference> noObfuscation;
+    final Map<DexReference, Map<DexReference, Set<ProguardKeepRule>>> dependentNoShrinking;
 
     private ConsequentRootSet(
         Set<DexMethod> neverInline,
         Set<DexType> neverClassInline,
-        Map<DexDefinition, Set<ProguardKeepRule>> noShrinking,
-        Set<DexDefinition> noOptimization,
-        Set<DexDefinition> noObfuscation,
-        Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentNoShrinking) {
+        Map<DexReference, Set<ProguardKeepRule>> noShrinking,
+        Set<DexReference> noOptimization,
+        Set<DexReference> noObfuscation,
+        Map<DexReference, Map<DexReference, Set<ProguardKeepRule>>> dependentNoShrinking) {
       this.neverInline = Collections.unmodifiableSet(neverInline);
       this.neverClassInline = Collections.unmodifiableSet(neverClassInline);
       this.noShrinking = Collections.unmodifiableMap(noShrinking);
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 15dcafa..fd90187 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
@@ -272,9 +271,7 @@
     // TODO(christofferqa): Remove the invariant that the graph lense should not modify any
     // methods from the sets alwaysInline and noSideEffects (see use of assertNotModified).
     extractPinnedItems(appInfo.alwaysInline, AbortReason.ALWAYS_INLINE);
-    extractPinnedItems(
-        DexDefinition.mapToReference(appInfo.noSideEffects.keySet().stream())::iterator,
-        AbortReason.NO_SIDE_EFFECTS);
+    extractPinnedItems(appInfo.noSideEffects.keySet(), AbortReason.NO_SIDE_EFFECTS);
 
     for (DexProgramClass clazz : classes) {
       for (DexEncodedMethod method : clazz.methods()) {
@@ -620,7 +617,7 @@
   }
 
   private boolean verifyGraphLense(GraphLense graphLense) {
-    assert graphLense.assertDefinitionsNotModified(appInfo.noSideEffects.keySet());
+    assert graphLense.assertReferencesNotModified(appInfo.noSideEffects.keySet());
 
     // Note that the method assertReferencesNotModified() relies on getRenamedFieldSignature() and
     // getRenamedMethodSignature() instead of lookupField() and lookupMethod(). This is important
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 1df6808..7493eba 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -903,18 +903,22 @@
     return extractor.getClassInternalType();
   }
 
-  protected Path writeToJar(List<byte[]> classes) throws IOException {
-    Path result = File.createTempFile("junit", ".jar", temp.getRoot()).toPath();
+  protected static void writeToJar(Path output, List<byte[]> classes) throws IOException {
     try (ZipOutputStream out =
         new ZipOutputStream(
             Files.newOutputStream(
-                result, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
+                output, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
       for (byte[] clazz : classes) {
         String name = extractClassName(clazz);
         ZipUtils.writeToZipStream(
             out, DescriptorUtils.getPathFromJavaType(name), clazz, ZipEntry.STORED);
       }
     }
+  }
+
+  protected Path writeToJar(List<byte[]> classes) throws IOException {
+    Path result = File.createTempFile("junit", ".jar", temp.getRoot()).toPath();
+    writeToJar(result, classes);
     return result;
   }
 
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/NameClashTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/NameClashTest.java
new file mode 100644
index 0000000..0da8519
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/NameClashTest.java
@@ -0,0 +1,404 @@
+// Copyright (c) 2019, 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.naming.applymapping;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+class LibraryClass {
+  static String LIB_MSG = "LibraryClass::foo";
+  void foo() {
+    System.out.println(LIB_MSG);
+  }
+}
+
+class AnotherLibraryClass {
+  static String ANOTHERLIB_MSG = "AnotherLibraryClass::foo";
+  void foo() {
+    System.out.println(ANOTHERLIB_MSG);
+  }
+}
+
+class ProgramClass extends LibraryClass {
+  static String PRG_MSG = "ProgramClass::bar";
+  void bar() {
+    System.out.println(PRG_MSG);
+  }
+
+  public static void main(String[] args) {
+    new AnotherLibraryClass().foo();
+    ProgramClass instance = new ProgramClass();
+    instance.foo();
+    instance.bar();
+  }
+}
+
+public class NameClashTest extends TestBase {
+
+  @ClassRule
+  public static TemporaryFolder temporaryFolder = ToolHelper.getTemporaryFolderForTest();
+
+  private static Class<?> MAIN = ProgramClass.class;
+  private static String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          AnotherLibraryClass.ANOTHERLIB_MSG, LibraryClass.LIB_MSG, ProgramClass.PRG_MSG);
+
+  private static Path prgJarThatUsesOriginalLib;
+  private static Path prgJarThatUsesMinifiedLib;
+  private static Path libJar;
+  private Path mappingFile;
+
+  @BeforeClass
+  public static void setUpJars() throws Exception {
+    prgJarThatUsesOriginalLib =
+        temporaryFolder.newFile("prgOrginalLib.jar").toPath().toAbsolutePath();
+    writeToJar(prgJarThatUsesOriginalLib, ImmutableList.of(ToolHelper.getClassAsBytes(MAIN)));
+    prgJarThatUsesMinifiedLib =
+        temporaryFolder.newFile("prgMinifiedLib.jar").toPath().toAbsolutePath();
+    writeToJar(prgJarThatUsesMinifiedLib, ImmutableList.of(ProgramClassDump.dump()));
+    libJar = temporaryFolder.newFile("lib.jar").toPath().toAbsolutePath();
+    writeToJar(libJar, ImmutableList.of(
+        ToolHelper.getClassAsBytes(LibraryClass.class),
+        ToolHelper.getClassAsBytes(AnotherLibraryClass.class)));
+  }
+
+  @Before
+  public void setUpMappingFile() throws Exception {
+    mappingFile = temp.newFile("mapping.txt").toPath().toAbsolutePath();
+  }
+
+  private String invertedMapping() {
+     return StringUtils.lines(
+        "A -> " + LibraryClass.class.getTypeName() + ":",
+        "  void a() -> foo",
+        "B -> " + AnotherLibraryClass.class.getTypeName() + ":",
+        "  void a() -> foo"
+    );
+  }
+
+  // Note that all the test mappings below still need identity mappings for classes/memebers that
+  // are not renamed, for some reasons:
+  // 1) to mimic how R8 generates the mapping, where identity mappings are used in the same way.
+  // 2) otherwise, those classes/members will be renamed if minification is enabled, resulting in
+  //   no name clash, which is definitely not intended.
+
+  private String mappingToExistingClassName() {
+    return StringUtils.lines(
+        LibraryClass.class.getTypeName()
+            + " -> " + AnotherLibraryClass.class.getTypeName() + ":",
+        AnotherLibraryClass.class.getTypeName()
+            + " -> " + AnotherLibraryClass.class.getTypeName() + ":"
+    );
+  }
+
+  private String mappingToTheSameClassName() {
+    return StringUtils.lines(
+        LibraryClass.class.getTypeName() + " -> Clash:",
+        AnotherLibraryClass.class.getTypeName() + " -> Clash:"
+    );
+  }
+
+  private String mappingToExistingMethodName() {
+    return StringUtils.lines(
+        LibraryClass.class.getTypeName() + " -> A:",
+        "  void foo() -> bar",
+        AnotherLibraryClass.class.getTypeName() + " -> B:",
+        ProgramClass.class.getTypeName() + " -> " + ProgramClass.class.getTypeName() + ":",
+        "  void bar() -> bar"
+    );
+  }
+
+  private String mappingToTheSameMethodName() {
+    return StringUtils.lines(
+        LibraryClass.class.getTypeName() + " -> A:",
+        "  void foo() -> clash",
+        AnotherLibraryClass.class.getTypeName() + " -> B:",
+        "  void foo() -> clash",
+        ProgramClass.class.getTypeName() + " -> " + ProgramClass.class.getTypeName() + ":",
+        "  void bar() -> clash"
+    );
+  }
+
+  private void testProguard_inputJar(Path mappingFile) throws Exception {
+    testForProguard()
+        .addProgramFiles(libJar)
+        .addProgramFiles(prgJarThatUsesOriginalLib)
+        .addKeepMainRule(MAIN)
+        .addKeepRules("-applymapping " + mappingFile)
+        .noTreeShaking()
+        .compile()
+        .run(MAIN)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  private void testR8_inputJar(Path mappingFile) throws Exception {
+    testForR8(Backend.DEX)
+        .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
+        .addProgramFiles(libJar)
+        .addProgramFiles(prgJarThatUsesOriginalLib)
+        .addKeepMainRule(MAIN)
+        .addKeepRules("-applymapping " + mappingFile)
+        .noTreeShaking()
+        .compile()
+        .run(MAIN)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  private void testProguard_originalLibraryJar(Path mappingFile) throws Exception {
+    testForProguard()
+        .addLibraryFiles(libJar)
+        .addProgramFiles(prgJarThatUsesOriginalLib)
+        .addKeepMainRule(MAIN)
+        .addKeepRules("-applymapping " + mappingFile)
+        .noTreeShaking()
+        .compile()
+        .run(MAIN)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  private void testR8_originalLibraryJar(Path mappingFile) throws Exception {
+    testForR8(Backend.DEX)
+        .addLibraryFiles(ToolHelper.getDefaultAndroidJar(), libJar)
+        .addProgramFiles(prgJarThatUsesOriginalLib)
+        .addKeepMainRule(MAIN)
+        .addKeepRules("-applymapping " + mappingFile)
+        .noTreeShaking()
+        .compile()
+        .run(MAIN)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  private void testProguard_minifiedLibraryJar(Path mappingFile) throws Exception {
+    testForProguard()
+        .addLibraryFiles(ToolHelper.getJava8RuntimeJar(), libJar)
+        .addProgramFiles(prgJarThatUsesMinifiedLib)
+        .addKeepMainRule(MAIN)
+        .addKeepRules("-applymapping " + mappingFile)
+        .noTreeShaking()
+        .compile()
+        .run(MAIN)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  private void testR8_minifiedLibraryJar(Path mappingFile) throws Exception {
+    testForR8(Backend.DEX)
+        .addLibraryFiles(ToolHelper.getDefaultAndroidJar(), libJar)
+        .addProgramFiles(prgJarThatUsesMinifiedLib)
+        .addKeepMainRule(MAIN)
+        .addKeepRules("-applymapping " + mappingFile)
+        .noTreeShaking()
+        .compile()
+        .run(MAIN)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testProguard_prgClassRenamedToExistingPrgClass() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToExistingClassName());
+    try {
+      testProguard_inputJar(mappingFile);
+      fail("Expect compilation failure.");
+    } catch (CompilationFailedException e) {
+      assertThat(e.getMessage(), containsString("Duplicate jar entry"));
+      assertThat(e.getMessage(), containsString("AnotherLibraryClass.class"));
+    }
+  }
+
+  @Test
+  public void testR8_prgClassRenamedToExistingPrgClass() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToExistingClassName());
+    try {
+      testR8_inputJar(mappingFile);
+      fail("Expect compilation failure.");
+    } catch (CompilationFailedException e) {
+      assertThat(e.getCause().getMessage(), containsString("Program type already present"));
+      assertThat(e.getCause().getMessage(), containsString("AnotherLibraryClass"));
+    }
+  }
+
+  @Test
+  public void testProguard_originalLibClassRenamedToExistingLibClass() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToExistingClassName());
+    try {
+      testProguard_originalLibraryJar(mappingFile);
+      fail("Expect compilation failure.");
+    } catch (CompilationFailedException e) {
+      assertThat(e.getMessage(), containsString("can't find referenced method"));
+      assertThat(e.getMessage(), containsString("ProgramClass"));
+    }
+  }
+
+  @Ignore("b/123092153")
+  @Test
+  public void testR8_originalLibClassRenamedToExistingLibClass() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToExistingClassName());
+    testR8_originalLibraryJar(mappingFile);
+  }
+
+  @Test
+  public void testProguard_prgClassesRenamedToSameName() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToTheSameClassName());
+    try {
+      testProguard_inputJar(mappingFile);
+      fail("Expect compilation failure.");
+    } catch (CompilationFailedException e) {
+      assertThat(e.getMessage(), containsString("Duplicate jar entry [Clash.class]"));
+    }
+  }
+
+  @Test
+  public void testR8_prgClassesRenamedToSameName() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToTheSameClassName());
+    try {
+      testR8_inputJar(mappingFile);
+      fail("Expect compilation failure.");
+    } catch (CompilationFailedException e) {
+      assertThat(e.getCause().getMessage(), containsString("Program type already present"));
+      assertThat(e.getCause().getMessage(), containsString("Clash"));
+    }
+  }
+
+  @Test
+  public void testProguard_originalLibClassesRenamedToSameName() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToTheSameClassName());
+    try {
+      testProguard_originalLibraryJar(mappingFile);
+      fail("Expect compilation failure.");
+    } catch (CompilationFailedException e) {
+      assertThat(e.getMessage(), containsString("can't find referenced method"));
+      assertThat(e.getMessage(), containsString("ProgramClass"));
+    }
+  }
+
+  @Ignore("b/123092153")
+  @Test
+  public void testR8_originalLibClassesRenamedToSameName() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToTheSameClassName());
+    testR8_originalLibraryJar(mappingFile);
+  }
+
+  @Test
+  public void testProguard_prgMethodRenamedToExistingName() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToExistingMethodName());
+    try {
+      testProguard_inputJar(mappingFile);
+      fail("Expect compilation failure.");
+    } catch (CompilationFailedException e) {
+      assertThat(e.getMessage(), containsString("method 'void bar()' can't be mapped to 'bar'"));
+      assertThat(e.getMessage(), containsString("it would conflict with method 'foo'"));
+      assertThat(e.getMessage(), containsString("which is already being mapped to 'bar'"));
+    }
+  }
+
+  @Ignore("b/123092153")
+  @Test
+  public void testR8_prgMethodRenamedToExistingName() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToExistingMethodName());
+    try {
+      testR8_inputJar(mappingFile);
+      fail("Expect compilation failure.");
+    } catch (CompilationFailedException e) {
+      assertThat(e.getMessage(), containsString("method 'void bar()' can't be mapped to 'bar'"));
+      assertThat(e.getMessage(), containsString("it would conflict with method 'foo'"));
+      assertThat(e.getMessage(), containsString("which is already being mapped to 'bar'"));
+    }
+  }
+
+  @Test
+  public void testProguard_originalLibMethodRenamedToExistingName() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToExistingMethodName());
+    try {
+      testProguard_originalLibraryJar(mappingFile);
+      fail("Expect compilation failure.");
+    } catch (CompilationFailedException e) {
+      assertThat(e.getMessage(), containsString("can't find referenced method"));
+      assertThat(e.getMessage(), containsString("ProgramClass"));
+    }
+  }
+
+  @Ignore("b/123092153")
+  @Test
+  public void testR8_originalLibMethodRenamedToExistingName() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToExistingMethodName());
+    testR8_originalLibraryJar(mappingFile);
+  }
+
+  @Test
+  public void testProguard_prgMethodRenamedToSameName() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToTheSameMethodName());
+    try {
+      testProguard_inputJar(mappingFile);
+      fail("Expect compilation failure.");
+    } catch (CompilationFailedException e) {
+      assertThat(e.getMessage(), containsString("method 'void bar()' can't be mapped to 'clash'"));
+      assertThat(e.getMessage(), containsString("it would conflict with method 'foo'"));
+      assertThat(e.getMessage(), containsString("which is already being mapped to 'clash'"));
+    }
+  }
+
+  @Ignore("b/123092153")
+  @Test
+  public void testR8_prgMethodRenamedToSameName() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToTheSameMethodName());
+    try {
+      testR8_inputJar(mappingFile);
+      fail("Expect compilation failure.");
+    } catch (CompilationFailedException e) {
+      assertThat(e.getMessage(), containsString("method 'void bar()' can't be mapped to 'bar'"));
+      assertThat(e.getMessage(), containsString("it would conflict with method 'foo'"));
+      assertThat(e.getMessage(), containsString("which is already being mapped to 'bar'"));
+    }
+  }
+
+  @Test
+  public void testProguard_originalLibMethodRenamedToSameName() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToTheSameMethodName());
+    try {
+      testProguard_originalLibraryJar(mappingFile);
+      fail("Expect compilation failure.");
+    } catch (CompilationFailedException e) {
+      assertThat(e.getMessage(), containsString("can't find referenced method"));
+      assertThat(e.getMessage(), containsString("ProgramClass"));
+    }
+  }
+
+  @Ignore("b/123092153")
+  @Test
+  public void testR8_originalLibMethodRenamedToSameName() throws Exception {
+    FileUtils.writeTextFile(mappingFile, mappingToTheSameMethodName());
+    testR8_originalLibraryJar(mappingFile);
+  }
+
+  @Test
+  public void testProguard_minifiedLib() throws Exception {
+    FileUtils.writeTextFile(mappingFile, invertedMapping());
+    try {
+      testProguard_minifiedLibraryJar(mappingFile);
+    } catch (CompilationFailedException e) {
+      assertThat(e.getMessage(), containsString("can't find superclass or interface A"));
+    }
+  }
+
+  @Ignore("b/121305642")
+  @Test
+  public void testR8_minifiedLib() throws Exception {
+    FileUtils.writeTextFile(mappingFile, invertedMapping());
+    testR8_minifiedLibraryJar(mappingFile);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/NameClashTestDump.java b/src/test/java/com/android/tools/r8/naming/applymapping/NameClashTestDump.java
new file mode 100644
index 0000000..7da3800
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/NameClashTestDump.java
@@ -0,0 +1,131 @@
+// Copyright (c) 2019, 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.naming.applymapping;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+// asmified the following:
+// class ProgramClass extends LibraryClass {
+//   static String PRG_MSG = "ProgramClass::bar";
+//   void bar() {
+//     System.out.println(PRG_MSG);
+//   }
+//   public static void main(String[] args) {
+//     new AnotherLibraryClass().foo();
+//     ProgramClass instance = new ProgramClass();
+//     instance.foo();
+//     instance.bar();
+//   }
+// }
+//
+// then replaced the use of LibraryClass and AnotherLibraryClass with A and B so that providing
+// LibraryClass and AnotherLibraryClass, along with mapping, as a minified library:
+//   A -> LibraryClass:
+//     void a() -> foo
+//   B -> AnotherLibraryClass:
+//     void a() -> foo
+class ProgramClassDump implements Opcodes {
+
+  public static byte[] dump () throws Exception {
+
+    ClassWriter classWriter = new ClassWriter(0);
+    FieldVisitor fieldVisitor;
+    MethodVisitor methodVisitor;
+
+    String pkg = LibraryClass.class.getPackage().getName();
+
+    classWriter.visit(V1_8, ACC_SUPER, pkg.replace('.', '/') + "/ProgramClass", null, "A", null);
+
+    classWriter.visitSource("Test.java", null);
+
+    {
+      fieldVisitor = classWriter.visitField(
+          ACC_STATIC, "PRG_MSG", "Ljava/lang/String;", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(15, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "A", "<init>", "()V", false);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(1, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(0, "bar", "()V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(18, label0);
+      methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      methodVisitor.visitFieldInsn(GETSTATIC, "ProgramClass", "PRG_MSG", "Ljava/lang/String;");
+      methodVisitor.visitMethodInsn(
+          INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLineNumber(19, label1);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(2, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(
+          ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(22, label0);
+      methodVisitor.visitTypeInsn(NEW, "B");
+      methodVisitor.visitInsn(DUP);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "B", "<init>", "()V", false);
+      methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "B", "a", "()V", false);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLineNumber(23, label1);
+      methodVisitor.visitTypeInsn(NEW, "ProgramClass");
+      methodVisitor.visitInsn(DUP);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "ProgramClass", "<init>", "()V", false);
+      methodVisitor.visitVarInsn(ASTORE, 1);
+      Label label2 = new Label();
+      methodVisitor.visitLabel(label2);
+      methodVisitor.visitLineNumber(24, label2);
+      methodVisitor.visitVarInsn(ALOAD, 1);
+      methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "ProgramClass", "a", "()V", false);
+      Label label3 = new Label();
+      methodVisitor.visitLabel(label3);
+      methodVisitor.visitLineNumber(25, label3);
+      methodVisitor.visitVarInsn(ALOAD, 1);
+      methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "ProgramClass", "bar", "()V", false);
+      Label label4 = new Label();
+      methodVisitor.visitLabel(label4);
+      methodVisitor.visitLineNumber(26, label4);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(2, 2);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(16, label0);
+      methodVisitor.visitLdcInsn("ProgramClass::bar");
+      methodVisitor.visitFieldInsn(PUTSTATIC, "ProgramClass", "PRG_MSG", "Ljava/lang/String;");
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(1, 0);
+      methodVisitor.visitEnd();
+    }
+    classWriter.visitEnd();
+
+    return classWriter.toByteArray();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/WhenToApplyTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/WhenToApplyTest.java
index e5371c5..25497e3 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/WhenToApplyTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/WhenToApplyTest.java
@@ -66,11 +66,11 @@
 
   @BeforeClass
   public static void setUpMappingFile() throws Exception {
-    mappingFile = temporaryFolder.newFile("mapping.txt").toPath();
+    mappingFile = temporaryFolder.newFile("mapping.txt").toPath().toAbsolutePath();
     FileUtils.writeTextFile(mappingFile, StringUtils.lines(
         ToBeRenamedClass.class.getTypeName() + " -> " + RENAMED_CLASS_NAME + ":",
         "  void toBeRenamedMethod() -> abc"));
-    configuration = temporaryFolder.newFile("pg.conf").toPath();
+    configuration = temporaryFolder.newFile("pg.conf").toPath().toAbsolutePath();
     FileUtils.writeTextFile(configuration, StringUtils.lines(
         "-dontoptimize",
         "-applymapping " + mappingFile