Merge "Mark field type as live."
diff --git a/build.gradle b/build.gradle
index 8ee6b41..cd831cc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -113,7 +113,6 @@
         java {
             srcDirs = ['src/test/apiUsageSample']
         }
-        output.resourcesDir = 'build/classes/apiUsageSample'
     }
     debugTestResources {
         java {
diff --git a/src/main/java/com/android/tools/r8/ApiLevelException.java b/src/main/java/com/android/tools/r8/ApiLevelException.java
index 14a530c..6d110a4 100644
--- a/src/main/java/com/android/tools/r8/ApiLevelException.java
+++ b/src/main/java/com/android/tools/r8/ApiLevelException.java
@@ -3,12 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.utils.AndroidApiLevel;
 
 /**
  * Exception to signal features that are not supported until a given API level.
  */
-public class ApiLevelException extends CompilationException {
+public class ApiLevelException extends CompilationError {
 
   public ApiLevelException(
       AndroidApiLevel minApiLevel, String unsupportedFeatures, String sourceString) {
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index e393f32..799d834 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -37,7 +37,7 @@
     programConsumer = null;
     mode = null;
     minApiLevel = 0;
-    reporter = new Reporter(new DefaultDiagnosticsHandler());
+    reporter = new Reporter(new DefaultDiagnosticsHandler(), this);
     enableDesugaring = true;
     optimizeMultidexForLinearAlloc = false;
   }
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index 6d7ad87..6c9a286 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.utils.ArchiveBuilder;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DirectoryBuilder;
-import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.OutputBuilder;
 import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.io.ByteStreams;
@@ -132,11 +131,7 @@
     @Override
     public void finished(DiagnosticsHandler handler) {
       super.finished(handler);
-      try {
-        outputBuilder.close();
-      } catch (IOException e) {
-        handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
-      }
+      outputBuilder.close(handler);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/CompatDxHelper.java b/src/main/java/com/android/tools/r8/CompatDxHelper.java
index 627f417..f540ed0 100644
--- a/src/main/java/com/android/tools/r8/CompatDxHelper.java
+++ b/src/main/java/com/android/tools/r8/CompatDxHelper.java
@@ -9,8 +9,7 @@
 import java.io.IOException;
 
 public class CompatDxHelper {
-  public static void run(D8Command command, Boolean minimalMainDex)
-      throws IOException, CompilationException {
+  public static void run(D8Command command, Boolean minimalMainDex) throws IOException {
     AndroidApp app = command.getInputApp();
     InternalOptions options = command.getInternalOptions();
     // DX does not desugar.
diff --git a/src/main/java/com/android/tools/r8/CompilationException.java b/src/main/java/com/android/tools/r8/CompilationException.java
deleted file mode 100644
index 8e1e56c..0000000
--- a/src/main/java/com/android/tools/r8/CompilationException.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2017, 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;
-
-/**
- * Exception to signal an compilation error.
- *
- * This is always an expected error and considered a user input issue.
- * A user-understandable message must be provided.
- */
-public class CompilationException extends Exception {
-  private static final long serialVersionUID = 1L;
-
-  /**
-   * Construct the exception with a {@link String} message.
-   * @param message the message
-   */
-  public CompilationException(String message) {
-    super(message);
-  }
-
-  /**
-   * Construct the exception with a {@link String} message and a {@link Throwable} cause.
-   * @param message the message
-   * @param cause the cause
-   */
-  public CompilationException(String message, Throwable cause) {
-    super(message, cause);
-  }
-
-  /**
-   * Construct the exception with a {@link Throwable} cause.
-   * @param cause the cause
-   */
-  public CompilationException(Throwable cause) {
-    super(cause.getMessage(), cause);
-  }
-
-  protected CompilationException() {
-    super();
-  }
-
-  public String getMessageForD8() {
-    return super.getMessage();
-  }
-
-  public String getMessageForR8() {
-    return super.getMessage();
-  }
-}
-
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 2d52c64..06cf0bb 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -121,8 +121,7 @@
     ExceptionUtils.withMainProgramHandler(() -> run(args));
   }
 
-  static void runForTesting(AndroidApp inputApp, InternalOptions options)
-      throws IOException, CompilationException {
+  static void runForTesting(AndroidApp inputApp, InternalOptions options) throws IOException {
     ExecutorService executor = ThreadUtils.getExecutorService(options);
     try {
       run(inputApp, options, executor);
@@ -149,7 +148,7 @@
   }
 
   private static void run(AndroidApp inputApp, InternalOptions options, ExecutorService executor)
-      throws IOException, CompilationException {
+      throws IOException {
     Timing timing = new Timing("D8");
     try {
       // Disable global optimizations.
@@ -196,7 +195,7 @@
       InternalOptions options,
       Timing timing,
       ExecutorService executor)
-      throws IOException, ExecutionException, ApiLevelException {
+      throws IOException, ExecutionException {
     final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
 
     IRConverter converter = new IRConverter(appInfo, options, timing, printer);
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 4c65134..6f956db 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -215,9 +216,10 @@
     Path outputPath = null;
     OutputMode outputMode = null;
     boolean hasDefinedApiLevel = false;
+    String[] expandedArgs = FlagFile.expandFlagFiles(args, builder.getReporter());
     try {
-      for (int i = 0; i < args.length; i++) {
-        String arg = args[i].trim();
+      for (int i = 0; i < expandedArgs.length; i++) {
+        String arg = expandedArgs[i].trim();
         if (arg.length() == 0) {
           continue;
         } else if (arg.equals("--help")) {
@@ -243,7 +245,7 @@
         } else if (arg.equals("--file-per-class")) {
           outputMode = OutputMode.DexFilePerClassFile;
         } else if (arg.equals("--output")) {
-          String output = args[++i];
+          String output = expandedArgs[++i];
           if (outputPath != null) {
             builder.getReporter().error(new StringDiagnostic(
                 "Cannot output both to '" + outputPath.toString() + "' and '" + output + "'",
@@ -252,15 +254,15 @@
           }
           outputPath = Paths.get(output);
         } else if (arg.equals("--lib")) {
-          builder.addLibraryFiles(Paths.get(args[++i]));
+          builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
         } else if (arg.equals("--classpath")) {
-          builder.addClasspathFiles(Paths.get(args[++i]));
+          builder.addClasspathFiles(Paths.get(expandedArgs[++i]));
         } else if (arg.equals("--main-dex-list")) {
-          builder.addMainDexListFiles(Paths.get(args[++i]));
+          builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
         } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
           builder.setOptimizeMultidexForLinearAlloc(true);
         } else if (arg.equals("--min-api")) {
-          hasDefinedApiLevel = parseMinApi(builder, args[++i], hasDefinedApiLevel, origin);
+          hasDefinedApiLevel = parseMinApi(builder, expandedArgs[++i], hasDefinedApiLevel, origin);
         } else if (arg.equals("--intermediate")) {
           builder.setIntermediate(true);
         } else if (arg.equals("--no-desugaring")) {
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index 9c76484..5987b99 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -67,7 +67,7 @@
       InternalOptions options,
       Boolean minimalMainDex,
       Map<String, Integer> inputOrdering)
-      throws IOException, CompilationException {
+      throws IOException {
     options.enableDesugaring = false;
     options.enableMainDexListCheck = false;
     options.minimalMainDex = minimalMainDex;
@@ -106,7 +106,7 @@
   }
 
   public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
-      throws IOException, CompilationException {
+      throws IOException {
     InternalOptions options = command.getInternalOptions();
     options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
     D8.runForTesting(command.getInputApp(), options);
diff --git a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
index ca86c70..f1c0fac 100644
--- a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.utils.ArchiveBuilder;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DirectoryBuilder;
-import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.OutputBuilder;
 import com.android.tools.r8.utils.ZipUtils;
@@ -153,11 +152,7 @@
     @Override
     public void finished(DiagnosticsHandler handler) {
       super.finished(handler);
-      try {
-        outputBuilder.close();
-      } catch (IOException e) {
-        handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
-      }
+      outputBuilder.close(handler);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index f26ab73..3bbab64 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -156,11 +156,7 @@
     @Override
     public void finished(DiagnosticsHandler handler) {
       super.finished(handler);
-      try {
-        outputBuilder.close();
-      } catch (IOException e) {
-        handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
-      }
+      outputBuilder.close(handler);
     }
 
     public static void writeResources(Path archive, List<ProgramResource> resources)
@@ -242,11 +238,7 @@
     @Override
     public void finished(DiagnosticsHandler handler) {
       super.finished(handler);
-      try {
-        outputBuilder.close();
-      } catch (IOException e) {
-        handler.error(new ExceptionDiagnostic(e, outputBuilder.getOrigin()));
-      }
+      outputBuilder.close(handler);
     }
 
     private synchronized void prepareDirectory() throws IOException {
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 1fcd70a..2d52ec9 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -40,8 +40,7 @@
     try {
       ExceptionUtils.withCompilationHandler(
           command.getReporter(),
-          () -> run(command, featureClassMapping, output, proguardMap, executor),
-          CompilationException::getMessage);
+          () -> run(command, featureClassMapping, output, proguardMap, executor));
     } finally {
       executor.shutdown();
     }
@@ -53,7 +52,7 @@
       String output,
       String proguardMap,
       ExecutorService executor)
-      throws IOException, CompilationException {
+      throws IOException {
     InternalOptions options = command.getInternalOptions();
     options.enableDesugaring = false;
     options.enableMainDexListCheck = false;
@@ -135,7 +134,7 @@
   }
 
   public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
-      throws IOException, CompilationException {
+      throws IOException {
     InternalOptions options = command.getInternalOptions();
     options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
     D8.runForTesting(command.getInputApp(), options);
diff --git a/src/main/java/com/android/tools/r8/ExtractMarker.java b/src/main/java/com/android/tools/r8/ExtractMarker.java
index ced0b4c..734dc4a 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarker.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarker.java
@@ -94,7 +94,7 @@
   }
 
   public static void main(String[] args)
-      throws IOException, CompilationException, ExecutionException, ResourceException {
+      throws IOException, ExecutionException, ResourceException {
     ExtractMarkerCommand.Builder builder = ExtractMarkerCommand.parse(args);
     ExtractMarkerCommand command = builder.build();
     if (command.isPrintHelp()) {
diff --git a/src/main/java/com/android/tools/r8/ExtractMarkerCommand.java b/src/main/java/com/android/tools/r8/ExtractMarkerCommand.java
index 9555d3b..598d8e0 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarkerCommand.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarkerCommand.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import com.android.tools.r8.errors.CompilationError;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Path;
@@ -54,7 +55,7 @@
       return this;
     }
 
-    public ExtractMarkerCommand build() throws CompilationException, IOException {
+    public ExtractMarkerCommand build() throws IOException {
       // If printing versions ignore everything else.
       if (isPrintHelp()) {
         return new ExtractMarkerCommand(isPrintHelp());
@@ -76,15 +77,13 @@
     return new Builder();
   }
 
-  public static Builder parse(String[] args)
-      throws CompilationException, IOException {
+  public static Builder parse(String[] args) throws IOException {
     Builder builder = builder();
     parse(args, builder);
     return builder;
   }
 
-  private static void parse(String[] args, Builder builder)
-      throws CompilationException, IOException {
+  private static void parse(String[] args, Builder builder) throws IOException {
     for (int i = 0; i < args.length; i++) {
       String arg = args[i].trim();
       if (arg.length() == 0) {
@@ -101,7 +100,7 @@
         builder.setPrintHelp(true);
       } else {
         if (arg.startsWith("--")) {
-          throw new CompilationException("Unknown option: " + arg);
+          throw new CompilationError("Unknown option: " + arg);
         }
         builder.addProgramFile(Paths.get(arg));
       }
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 53e2f87..5d29d90 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.ClassAndMemberPublicizer;
 import com.android.tools.r8.graph.DexApplication;
@@ -89,7 +88,7 @@
  * <p>R8 supports some configuration using configuration files mostly compatible with the format of
  * the <a href="https://www.guardsquare.com/en/proguard">ProGuard</a> optimizer.
  *
- * <p>The compiler is invoked by calling {@link #run(R8Command) R8.run} with an appropriate {@link
+ * <p>The compiler is invoked by calling {@link #run(R8Command) R8.run} with an appropriate {link
  * R8Command}. For example:
  *
  * <pre>
@@ -179,7 +178,7 @@
       String proguardSeedsData,
       InternalOptions options,
       ProguardMapSupplier proguardMapSupplier)
-      throws ExecutionException, DexOverflowException {
+      throws ExecutionException {
     try {
       Marker marker = getMarker(options);
       if (options.isGeneratingClassFiles()) {
@@ -214,8 +213,7 @@
     return result;
   }
 
-  static void runForTesting(AndroidApp app, InternalOptions options)
-      throws IOException, CompilationException {
+  static void runForTesting(AndroidApp app, InternalOptions options) throws IOException {
     ExecutorService executor = ThreadUtils.getExecutorService(options);
     try {
       run(app, options, executor);
@@ -224,16 +222,12 @@
     }
   }
 
-  private static void run(
-      AndroidApp app,
-      InternalOptions options,
-      ExecutorService executor)
-      throws IOException, CompilationException {
+  private static void run(AndroidApp app, InternalOptions options, ExecutorService executor)
+      throws IOException {
     new R8(options).run(app, executor);
   }
 
-  private void run(AndroidApp inputApp, ExecutorService executorService)
-      throws IOException, CompilationException {
+  private void run(AndroidApp inputApp, ExecutorService executorService) throws IOException {
     assert options.programConsumer != null;
     if (options.quiet) {
       System.setOut(new PrintStream(ByteStreams.nullOutputStream()));
@@ -503,31 +497,15 @@
     }
   }
 
-  static void unwrapExecutionException(ExecutionException executionException)
-      throws CompilationException {
+  static void unwrapExecutionException(ExecutionException executionException) {
     Throwable cause = executionException.getCause();
     if (cause instanceof CompilationError) {
       // add original exception as suppressed exception to provide the original stack trace
       cause.addSuppressed(executionException);
       throw (CompilationError) cause;
-    } else if (cause instanceof CompilationException) {
-      cause.addSuppressed(executionException);
-      throw (CompilationException) cause;
     } else if (cause instanceof RuntimeException) {
-      // ForkJoinPool wraps checked exceptions in RuntimeExceptions
-      if (cause.getCause() != null
-          && cause.getCause() instanceof CompilationException) {
-        cause.addSuppressed(executionException);
-        throw (CompilationException) cause.getCause();
-      // ForkJoinPool sometimes uses 2 levels of RuntimeExceptions, to provide accurate stack traces
-      } else if (cause.getCause() != null && cause.getCause().getCause() != null
-          && cause.getCause().getCause() instanceof CompilationException) {
-        cause.addSuppressed(executionException);
-        throw (CompilationException) cause.getCause().getCause();
-      } else {
-        cause.addSuppressed(executionException);
-        throw (RuntimeException) cause;
-      }
+      cause.addSuppressed(executionException);
+      throw (RuntimeException) cause;
     } else {
       throw new RuntimeException(executionException.getMessage(), cause);
     }
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index dafa35a..573f6df 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -18,12 +18,12 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableList;
-import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -66,6 +66,8 @@
     // Internal compatibility mode for use from CompatProguard tool.
     Path proguardCompatibilityRulesOutput = null;
 
+    private boolean allowPartiallyImplementedProguardOptions = false;
+
     private StringConsumer mainDexListConsumer = null;
 
     // TODO(zerny): Consider refactoring CompatProguardCommandBuilder to avoid subclassing.
@@ -286,7 +288,8 @@
         mainDexKeepRules = parser.getConfig().getRules();
       }
 
-      ProguardConfigurationParser parser = new ProguardConfigurationParser(factory, reporter);
+      ProguardConfigurationParser parser = new ProguardConfigurationParser(
+          factory, reporter, !allowPartiallyImplementedProguardOptions);
       if (!proguardConfigs.isEmpty()) {
         parser.parse(proguardConfigs);
       }
@@ -384,6 +387,11 @@
             c.accept(builder);
           };
     }
+
+    // Internal for-testing method to add post-processors of the proguard configuration.
+    void allowPartiallyImplementedProguardOptions() {
+      allowPartiallyImplementedProguardOptions = true;
+    }
   }
 
   // Wrapper class to ensure that R8 does not allow DEX as program inputs.
@@ -514,8 +522,9 @@
       Origin argsOrigin,
       Builder builder,
       ParseState state) {
-    for (int i = 0; i < args.length; i++) {
-      String arg = args[i].trim();
+    String[] expandedArgs = FlagFile.expandFlagFiles(args, builder.getReporter());
+    for (int i = 0; i < expandedArgs.length; i++) {
+      String arg = expandedArgs[i].trim();
       if (arg.length() == 0) {
         continue;
       } else if (arg.equals("--help")) {
@@ -547,7 +556,7 @@
         }
         state.outputMode = OutputMode.ClassFile;
       } else if (arg.equals("--output")) {
-        String outputPath = args[++i];
+        String outputPath = expandedArgs[++i];
         if (state.outputPath != null) {
           builder.getReporter().error(new StringDiagnostic(
               "Cannot output both to '"
@@ -559,10 +568,10 @@
         }
         state.outputPath = Paths.get(outputPath);
       } else if (arg.equals("--lib")) {
-        builder.addLibraryFiles(Paths.get(args[++i]));
+        builder.addLibraryFiles(Paths.get(expandedArgs[++i]));
       } else if (arg.equals("--min-api")) {
         state.hasDefinedApiLevel =
-            parseMinApi(builder, args[++i], state.hasDefinedApiLevel, argsOrigin);
+            parseMinApi(builder, expandedArgs[++i], state.hasDefinedApiLevel, argsOrigin);
       } else if (arg.equals("--no-tree-shaking")) {
         builder.setDisableTreeShaking(true);
       } else if (arg.equals("--no-minification")) {
@@ -570,40 +579,17 @@
       } else if (arg.equals("--no-desugaring")) {
         builder.setDisableDesugaring(true);
       } else if (arg.equals("--main-dex-rules")) {
-        builder.addMainDexRulesFiles(Paths.get(args[++i]));
+        builder.addMainDexRulesFiles(Paths.get(expandedArgs[++i]));
       } else if (arg.equals("--main-dex-list")) {
-        builder.addMainDexListFiles(Paths.get(args[++i]));
+        builder.addMainDexListFiles(Paths.get(expandedArgs[++i]));
       } else if (arg.equals("--main-dex-list-output")) {
-        builder.setMainDexListOutputPath(Paths.get(args[++i]));
+        builder.setMainDexListOutputPath(Paths.get(expandedArgs[++i]));
       } else if (arg.equals("--optimize-multidex-for-linearalloc")) {
         builder.setOptimizeMultidexForLinearAlloc(true);
       } else if (arg.equals("--pg-conf")) {
-        builder.addProguardConfigurationFiles(Paths.get(args[++i]));
+        builder.addProguardConfigurationFiles(Paths.get(expandedArgs[++i]));
       } else if (arg.equals("--pg-map-output")) {
-        builder.setProguardMapOutputPath(Paths.get(args[++i]));
-      } else if (arg.startsWith("@")) {
-        // TODO(zerny): Replace this with pipe reading.
-        Path argsFile = Paths.get(arg.substring(1));
-        Origin argsFileOrigin = new PathOrigin(argsFile);
-        try {
-          List<String> linesInFile = FileUtils.readAllLines(argsFile);
-          List<String> argsInFile = new ArrayList<>();
-          for (String line : linesInFile) {
-            for (String word : line.split("\\s")) {
-              String trimmed = word.trim();
-              if (!trimmed.isEmpty()) {
-                argsInFile.add(trimmed);
-              }
-            }
-          }
-          // TODO(zerny): We need to define what CWD should be for files referenced in an args file.
-          state = parse(argsInFile.toArray(new String[argsInFile.size()]),
-              argsFileOrigin, builder, state);
-        } catch (IOException e) {
-          builder.getReporter().error(new StringDiagnostic(
-              "Failed to read arguments from file " + argsFile + ": " + e.getMessage(),
-              argsFileOrigin));
-        }
+        builder.setProguardMapOutputPath(Paths.get(expandedArgs[++i]));
       } else {
         if (arg.startsWith("--")) {
           builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 98ed5e9..24b2cc9 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.2.17-dev";
+  public static final String LABEL = "1.2.19-dev";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
index 250ca3b..1336f95 100644
--- a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
+++ b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.benchmarks.BenchmarkUtils.printRuntimeNanoseconds;
 
 import com.android.tools.r8.ClassFileResourceProvider;
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -112,7 +111,7 @@
       boolean desugar,
       Map<String, ProgramResource> outputs,
       ExecutorService executor)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
 
     ProgramConsumer consumer =
         new DexFilePerClassFileConsumer.ForwardingConsumer(null) {
@@ -155,7 +154,7 @@
       boolean desugar,
       Map<String, ProgramResource> outputs,
       ExecutorService executor)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     ProgramConsumer consumer =
         new ForwardingConsumer(null) {
           @Override
@@ -199,7 +198,7 @@
 
   private static void merge(
       boolean desugar, Map<String, ProgramResource> outputs, ExecutorService executor)
-      throws IOException, CompilationException, CompilationFailedException, ResourceException {
+      throws IOException, CompilationFailedException, ResourceException {
     Builder builder =
         D8Command.builder()
             .setMinApiLevel(API)
@@ -221,7 +220,7 @@
   }
 
   public static void main(String[] args)
-      throws IOException, CompilationException, CompilationFailedException, ResourceException {
+      throws IOException, CompilationFailedException, ResourceException {
     boolean desugar = Arrays.asList(args).contains("--desugar");
     Path input = desugar ? JAR_NOT_DESUGARED : JAR_DESUGARED;
     InMemoryClassPathProvider provider = new InMemoryClassPathProvider(input);
diff --git a/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
index 0aaefd9..3f241a0 100644
--- a/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
+++ b/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.benchmarks;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -20,7 +19,7 @@
   private static final int ITERATIONS = 1000;
 
   public static void compile(ExecutorService executor)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     D8.run(
         D8Command.builder()
             .addProgramFiles(Paths.get("build/test/examples/arithmetic.jar"))
@@ -43,8 +42,7 @@
         executor);
   }
 
-  public static void main(String[] args)
-      throws IOException, CompilationException, CompilationFailedException {
+  public static void main(String[] args) throws IOException, CompilationFailedException {
     int threads = Integer.min(Runtime.getRuntime().availableProcessors(), 16) / 2;
     ExecutorService executor = ThreadUtils.getExecutorService(threads);
     try {
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index dcc89c6..7ef43a3 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.utils.AndroidApp;
@@ -175,7 +174,7 @@
   }
 
   private static void writeApp(DexApplication app, Path output, ExecutorService executor)
-      throws IOException, ExecutionException, DexOverflowException {
+      throws IOException, ExecutionException {
     InternalOptions options = new InternalOptions();
     AndroidAppConsumers compatSink = new AndroidAppConsumers(options);
     ApplicationWriter writer = new ApplicationWriter(app, options, null, null, null, null, null);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index 0a98cb3..3122404 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexType;
@@ -48,8 +47,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder, CfState state, CfSourceCode code)
-      throws ApiLevelException {
+  public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
     builder.addConstMethodHandle(
         state.push(builder.getFactory().methodHandleType).register, handle);
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
index e933c11..ebbaf21 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
@@ -49,8 +48,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder, CfState state, CfSourceCode code)
-      throws ApiLevelException {
+  public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
     builder.addConstMethodType(state.push(builder.getFactory().methodTypeType).register, type);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index eaa7b2a..6d2a3d5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.UseRegistry;
@@ -49,8 +48,7 @@
     return false;
   }
 
-  public abstract void buildIR(IRBuilder builder, CfState state, CfSourceCode code)
-      throws ApiLevelException;
+  public abstract void buildIR(IRBuilder builder, CfState state, CfSourceCode code);
 
   /** Return true if this instruction directly emits IR instructions. */
   public boolean emitsIR() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 32c7022..5dfef0c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
@@ -91,8 +90,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder, CfState state, CfSourceCode code)
-      throws ApiLevelException {
+  public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
     Invoke.Type type;
     DexMethod canonicalMethod;
     DexProto callSiteProto = null;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index 2dfc8fa..015b8a3 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.cf.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.UseRegistry;
@@ -52,8 +51,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder, CfState state, CfSourceCode code)
-      throws ApiLevelException {
+  public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
     int[] dimensions = state.popReverse(this.dimensions);
     builder.addMultiNewArray(type, state.push(type).register, dimensions);
   }
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
index a346020..bad4e47 100644
--- a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
@@ -71,7 +70,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addConstMethodHandle(AA, (DexMethodHandle) BBBB);
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
index 6a1bef5..84c090a 100644
--- a/src/main/java/com/android/tools/r8/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.ObjectToOffsetMapping;
@@ -71,7 +70,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addConstMethodType(AA, (DexProto) BBBB);
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArray.java b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
index 8aaf5b9..4f2ebb6 100644
--- a/src/main/java/com/android/tools/r8/code/FilledNewArray.java
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -42,7 +41,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeNewArray(getType(), A, new int[]{C, D, E, F, G});
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
index 2d39d50..0ee63b0 100644
--- a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -42,7 +41,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRangeNewArray(getType(), AA, CCCC);
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
index 09d5671b..c8b5154 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.graph.DexCallSite;
@@ -184,7 +183,7 @@
     return NO_TARGETS;
   }
 
-  public abstract void buildIR(IRBuilder builder) throws ApiLevelException;
+  public abstract void buildIR(IRBuilder builder);
 
   public DexCallSite getCallSite() {
     return null;
diff --git a/src/main/java/com/android/tools/r8/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/code/InvokeDirect.java
index 06b919b..08d6826 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeDirect.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -51,7 +50,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRegisters(Type.DIRECT, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
index e2b129d..8f72a2b 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRange(Type.DIRECT, getMethod(), getProto(), AA, CCCC);
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/code/InvokeInterface.java
index c0d912a..b98af41 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeInterface.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -51,7 +50,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRegisters(
         Type.INTERFACE, getMethod(), getProto(), A, new int[] {C, D, E, F, G});
   }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
index c63c41b..e4d63b4 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRange(Type.INTERFACE, getMethod(), getProto(), AA, CCCC);
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java
index 891dcca..d5ecd11 100644
--- a/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -30,7 +29,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRegisters(
         Type.POLYMORPHIC, getMethod(), getProto(), A, new int[] {C, D, E, F, G});
   }
diff --git a/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java b/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
index 3969a8c..45eb6e8 100644
--- a/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -53,7 +52,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRange(Type.POLYMORPHIC, getMethod(), getProto(), AA, CCCC);
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/code/InvokeStatic.java
index 6c0a724..73cb887 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeStatic.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRegisters(Type.STATIC, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
index 912cd4b..6fd3f42 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRange(Type.STATIC, getMethod(), getProto(), AA, CCCC);
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/code/InvokeSuper.java
index 10ed497..9575ed9 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeSuper.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.IndexedDexItem;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -51,7 +50,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRegisters(Type.SUPER, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
index 8933527..63c6318 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRange(Type.SUPER, getMethod(), getProto(), AA, CCCC);
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
index 132c564..003debf 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRegisters(Type.VIRTUAL, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
   }
 
diff --git a/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
index 00cb861..64e9b6d 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.code;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.graph.UseRegistry;
@@ -50,7 +49,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) throws ApiLevelException {
+  public void buildIR(IRBuilder builder) {
     builder.addInvokeRange(Type.VIRTUAL, getMethod(), getProto(), AA, CCCC);
   }
 
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
index b270200..6599591 100644
--- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.compatdexbuilder;
 
 import com.android.tools.r8.CompatDxHelper;
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -154,7 +153,7 @@
   }
 
   private DexConsumer dexEntry(ZipFile zipFile, ZipEntry classEntry, ExecutorService executor)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     DexConsumer consumer = new DexConsumer();
     D8Command.Builder builder = D8Command.builder();
     CompatDxHelper.ignoreDexInArchive(builder);
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index b9131e5..f512a34 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -11,7 +11,6 @@
 import static com.android.tools.r8.utils.FileUtils.isZipFile;
 
 import com.android.tools.r8.CompatDxHelper;
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8Command;
@@ -312,9 +311,6 @@
   public static void main(String[] args) throws IOException {
     try {
       run(args);
-    } catch (CompilationException e) {
-      System.err.println(e.getMessage());
-      System.exit(1);
     } catch (DxUsageMessage e) {
       System.err.println(USAGE_HEADER);
       e.printHelpOn(System.err);
@@ -325,7 +321,7 @@
   }
 
   private static void run(String[] args)
-      throws DxUsageMessage, IOException, CompilationException, CompilationFailedException {
+      throws DxUsageMessage, IOException, CompilationFailedException {
     DxCompatOptions dexArgs = DxCompatOptions.parse(args);
     if (dexArgs.help) {
       printHelpOn(System.out);
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index 66931e3..615a3e2 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -5,12 +5,12 @@
 package com.android.tools.r8.compatproguard;
 
 import com.android.tools.r8.CompatProguardCommandBuilder;
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.Version;
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.utils.AbortException;
 import com.google.common.collect.ImmutableList;
@@ -58,7 +58,7 @@
       this.printHelpAndExit = printHelpAndExit;
     }
 
-    public static CompatProguardOptions parse(String[] args) throws CompilationException {
+    public static CompatProguardOptions parse(String[] args) {
       String output = null;
       int minApi = 1;
       boolean forceProguardCompatibility = false;
@@ -97,7 +97,7 @@
             } else if (arg.equals("--no-locals")) {
               noLocals = true;
             } else if (arg.equals("-outjars")) {
-              throw new CompilationException(
+              throw new CompilationError(
                   "Proguard argument -outjar is not supported. Use R8 compatible --output flag");
             } else {
               if (currentLine.length() > 0) {
@@ -150,8 +150,7 @@
     CompatProguardOptions.print();
   }
 
-  private static void run(String[] args)
-      throws IOException, CompilationException, CompilationFailedException {
+  private static void run(String[] args) throws IOException, CompilationFailedException {
     // Run R8 passing all the options from the command line as a Proguard configuration.
     CompatProguardOptions options = CompatProguardOptions.parse(args);
     if (options.printHelpAndExit || options.output == null) {
@@ -175,9 +174,6 @@
   public static void main(String[] args) throws IOException {
     try {
       run(args);
-    } catch (CompilationException e) {
-      System.err.println(e.getMessage());
-      System.exit(1);
     } catch (CompilationFailedException | AbortException e) {
       // Detail of the errors were already reported
       System.err.println("Compilation failed");
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 382bc66..e1905ff 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.dex;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.DataDirectoryResource;
 import com.android.tools.r8.DataEntryResource;
 import com.android.tools.r8.DataResourceConsumer;
@@ -13,7 +12,6 @@
 import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationDirectory;
 import com.android.tools.r8.graph.DexAnnotationSet;
@@ -163,7 +161,7 @@
   }
 
   private Iterable<VirtualFile> distribute(ExecutorService executorService)
-      throws ExecutionException, IOException, DexOverflowException {
+      throws ExecutionException, IOException {
     // Distribute classes into dex files.
     VirtualFile.Distributor distributor;
     if (options.isGeneratingDexFilePerClassFile()) {
@@ -180,8 +178,7 @@
     return distributor.run();
   }
 
-  public void write(ExecutorService executorService)
-      throws IOException, ExecutionException, DexOverflowException {
+  public void write(ExecutorService executorService) throws IOException, ExecutionException {
     application.timing.begin("DexApplication.write");
     try {
       insertAttributeAnnotations();
@@ -200,7 +197,6 @@
       // Use a linked hash map as the order matters when addDexProgramData is called below.
       Map<VirtualFile, Future<ObjectToOffsetMapping>> offsetMappingFutures = new LinkedHashMap<>();
       for (VirtualFile newFile : distribute(executorService)) {
-        assert !newFile.isEmpty();
         if (!newFile.isEmpty()) {
           offsetMappingFutures
               .put(newFile, executorService.submit(() -> {
@@ -429,8 +425,7 @@
     }
   }
 
-  private byte[] writeDexFile(ObjectToOffsetMapping mapping)
-      throws ApiLevelException {
+  private byte[] writeDexFile(ObjectToOffsetMapping mapping) {
     FileWriter fileWriter = new FileWriter(mapping, application, options, namingLens);
     // Collect the non-fixed sections.
     fileWriter.collect();
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 4767c64..b73f8a4 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -51,7 +51,6 @@
 import com.android.tools.r8.utils.DexVersion;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.LebUtils;
-import com.android.tools.r8.utils.ThrowingConsumer;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Object2IntMap;
@@ -135,7 +134,7 @@
     return this;
   }
 
-  public byte[] generate() throws ApiLevelException {
+  public byte[] generate() {
     // Check restrictions on interface methods.
     checkInterfaceMethods();
 
@@ -203,7 +202,7 @@
     return Arrays.copyOf(dest.asArray(), layout.getEndOfFile());
   }
 
-  private void checkInterfaceMethods() throws ApiLevelException {
+  private void checkInterfaceMethods() {
     for (DexProgramClass clazz : mapping.getClasses()) {
       if (clazz.isInterface()) {
         for (DexEncodedMethod method : clazz.directMethods()) {
@@ -222,7 +221,7 @@
   //  -- starting with N interfaces may also have public or private
   //     static methods, as well as public non-abstract (default)
   //     and private instance methods.
-  private void checkInterfaceMethod(DexEncodedMethod method) throws ApiLevelException {
+  private void checkInterfaceMethod(DexEncodedMethod method) {
     if (application.dexItemFactory.isClassConstructor(method.method)) {
       return; // Class constructor is always OK.
     }
@@ -295,16 +294,16 @@
     }
   }
 
-  private <T extends IndexedDexItem> void writeFixedSectionItems(Collection<T> items, int offset,
-      ThrowingConsumer<T, ApiLevelException> writer) throws ApiLevelException {
+  private <T extends IndexedDexItem> void writeFixedSectionItems(
+      Collection<T> items, int offset, Consumer<T> writer) {
     assert dest.position() == offset;
     for (T item : items) {
       writer.accept(item);
     }
   }
 
-  private void writeFixedSectionItems(DexProgramClass[] items, int offset,
-      ThrowingConsumer<DexProgramClass, ApiLevelException> writer) throws ApiLevelException {
+  private void writeFixedSectionItems(
+      DexProgramClass[] items, int offset, Consumer<DexProgramClass> writer) {
     assert dest.position() == offset;
     for (DexProgramClass item : items) {
       writer.accept(item);
@@ -610,7 +609,7 @@
     }
   }
 
-  private void writeMethodHandle(DexMethodHandle methodHandle) throws ApiLevelException {
+  private void writeMethodHandle(DexMethodHandle methodHandle) {
     checkThatInvokeCustomIsAllowed();
     MethodHandleType methodHandleDexType;
     switch (methodHandle.type) {
@@ -636,7 +635,7 @@
     dest.putShort((short) 0); // unused
   }
 
-  private void writeCallSite(DexCallSite callSite) throws ApiLevelException {
+  private void writeCallSite(DexCallSite callSite) {
     checkThatInvokeCustomIsAllowed();
     assert dest.isAligned(4);
     dest.putInt(mixedSectionOffsets.getOffsetFor(callSite.getEncodedArray()));
@@ -1296,7 +1295,7 @@
     }
   }
 
-  private void checkThatInvokeCustomIsAllowed() throws ApiLevelException {
+  private void checkThatInvokeCustomIsAllowed() {
     if (!options.canUseInvokeCustom()) {
       throw new ApiLevelException(
           AndroidApiLevel.O,
diff --git a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
index 3d0b01c..9e75e45 100644
--- a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
+++ b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
@@ -5,7 +5,7 @@
 package com.android.tools.r8.dex;
 
 import com.android.tools.r8.dex.VirtualFile.VirtualFileCycler;
-import com.android.tools.r8.errors.DexOverflowException;
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -306,7 +306,7 @@
     directSubClasses = new DirectSubClassesInfo(app, classes);
   }
 
-  public void distribute() throws DexOverflowException {
+  public void distribute() {
     List<ClassGroup> remainingInheritanceGroups = collectInheritanceGroups();
     // Sort to ensure reproducible allocation
     remainingInheritanceGroups.sort(null);
@@ -369,8 +369,7 @@
     return groupClassNumber;
   }
 
-  private Collection<VirtualFile> assignGroup(ClassGroup group,
-      List<VirtualFile> dexBlackList) throws DexOverflowException {
+  private Collection<VirtualFile> assignGroup(ClassGroup group, List<VirtualFile> dexBlackList) {
     VirtualFileCycler cycler = new VirtualFileCycler(dexes, namingLens, dexIndexOffset);
     if (group.members.isEmpty()) {
       return Collections.emptyList();
@@ -411,8 +410,8 @@
    * They will fail to link during DexOpt but they will be loaded only once.
    * @param classes set of classes to assign, the set will be destroyed during assignment.
    */
-  private Collection<VirtualFile> assignClassesWithLinkingError(Set<DexProgramClass> classes,
-      Collection<VirtualFile> dexBlackList) throws DexOverflowException {
+  private Collection<VirtualFile> assignClassesWithLinkingError(
+      Set<DexProgramClass> classes, Collection<VirtualFile> dexBlackList) {
 
     List<ClassGroup> layers = collectNoDirectInheritanceGroups(classes);
 
@@ -440,7 +439,7 @@
             dexForLayer.abortTransaction();
             if (dexForLayer.isEmpty()) {
               // The class is too big to fit in one dex
-              throw new DexOverflowException("Class '" + dexProgramClass.toSourceString()
+              throw new CompilationError("Class '" + dexProgramClass.toSourceString()
                   + "' from " + dexProgramClass.getOrigin().toString()
                   + " is too big to fit in a dex.");
             }
@@ -614,8 +613,8 @@
    * Assign as many classes as possible by layer starting by roots.
    * @return the list of classes that were not assigned.
    */
-  private Set<DexProgramClass> assignFromRoot(VirtualFile dex,
-      Collection<DexProgramClass> classes) throws DexOverflowException {
+  private Set<DexProgramClass> assignFromRoot(
+      VirtualFile dex, Collection<DexProgramClass> classes) {
 
     int totalClasses = classes.size();
     int assignedClasses = 0;
@@ -635,7 +634,7 @@
             dex.abortTransaction();
             if (dex.isEmpty()) {
               // The class is too big to fit in one dex
-              throw new DexOverflowException("Class '" + clazz.toSourceString() + "' from "
+              throw new CompilationError("Class '" + clazz.toSourceString() + "' from "
                   + clazz.getOrigin().toString() + " is too big to fit in a dex.");
             }
             isLayerFullyAssigned = false;
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 7f55b19..9ac752a 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -3,9 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.dex;
 
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.errors.InternalCompilerError;
-import com.android.tools.r8.errors.MainDexOverflowException;
+import com.android.tools.r8.errors.MainDexOverflow;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
@@ -24,6 +23,7 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Sets;
@@ -185,15 +185,16 @@
     return transaction.getNumberOfFields();
   }
 
-  void throwIfFull(boolean hasMainDexList) throws DexOverflowException {
+  void throwIfFull(boolean hasMainDexList, Reporter reporter) {
     if (!isFull()) {
       return;
     }
-    throw new MainDexOverflowException(
-        hasMainDexList,
-        transaction.getNumberOfMethods(),
-        transaction.getNumberOfFields(),
-        MAX_ENTRIES);
+    throw reporter.fatalError(
+        new MainDexOverflow(
+            hasMainDexList,
+            transaction.getNumberOfMethods(),
+            transaction.getNumberOfFields(),
+            MAX_ENTRIES));
   }
 
   private boolean isFilledEnough(FillStrategy fillStrategy) {
@@ -226,8 +227,7 @@
       this.writer = writer;
     }
 
-    public abstract Collection<VirtualFile> run()
-        throws ExecutionException, IOException, DexOverflowException;
+    public abstract Collection<VirtualFile> run() throws ExecutionException, IOException;
   }
 
   /**
@@ -296,7 +296,7 @@
       originalNames = computeOriginalNameMapping(classes, application.getProguardMap());
     }
 
-    protected void fillForMainDexList(Set<DexProgramClass> classes) throws DexOverflowException {
+    protected void fillForMainDexList(Set<DexProgramClass> classes) {
       if (!application.mainDexList.isEmpty()) {
         VirtualFile mainDexFile = virtualFiles.get(0);
         for (DexType type : application.mainDexList) {
@@ -314,7 +314,7 @@
           }
           mainDexFile.commitTransaction();
         }
-        mainDexFile.throwIfFull(true);
+        mainDexFile.throwIfFull(true, options.reporter);
       }
     }
 
@@ -363,7 +363,7 @@
     }
 
     @Override
-    public Collection<VirtualFile> run() throws IOException, DexOverflowException {
+    public Collection<VirtualFile> run() throws IOException {
       int totalClassNumber = classes.size();
       // First fill required classes into the main dex file.
       fillForMainDexList(classes);
@@ -408,14 +408,13 @@
     }
 
     @Override
-    public Collection<VirtualFile> run()
-        throws ExecutionException, IOException, DexOverflowException {
+    public Collection<VirtualFile> run() throws ExecutionException, IOException {
       // Add all classes to the main dex file.
       for (DexProgramClass programClass : classes) {
         mainDexFile.addClass(programClass);
       }
       mainDexFile.commitTransaction();
-      mainDexFile.throwIfFull(false);
+      mainDexFile.throwIfFull(false, options.reporter);
       return virtualFiles;
     }
   }
diff --git a/src/main/java/com/android/tools/r8/errors/DexOverflowException.java b/src/main/java/com/android/tools/r8/errors/DexOverflowException.java
deleted file mode 100644
index a392080..0000000
--- a/src/main/java/com/android/tools/r8/errors/DexOverflowException.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2017, 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.errors;
-
-import com.android.tools.r8.CompilationException;
-
-/**
- * Signals when there were too many items to fit in a given dex file.
- */
-public class DexOverflowException extends CompilationException {
-
-  protected DexOverflowException() {
-    super();
-  }
-
-  public DexOverflowException(String message) {
-    super(message);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/errors/MainDexOverflowException.java b/src/main/java/com/android/tools/r8/errors/MainDexOverflow.java
similarity index 89%
rename from src/main/java/com/android/tools/r8/errors/MainDexOverflowException.java
rename to src/main/java/com/android/tools/r8/errors/MainDexOverflow.java
index e570c81..e33cf59 100644
--- a/src/main/java/com/android/tools/r8/errors/MainDexOverflowException.java
+++ b/src/main/java/com/android/tools/r8/errors/MainDexOverflow.java
@@ -4,16 +4,16 @@
 package com.android.tools.r8.errors;
 
 /**
- * Thrown when running mono dex and not all classes can fit in a dex or when running for multidex
- * legacy and there are too many classes to fit in the main dex.
+ * Info about error when running mono dex and not all classes can fit in a dex or when running for
+ * multidex legacy and there are too many classes to fit in the main dex.
  */
-public class MainDexOverflowException extends DexOverflowException {
+public class MainDexOverflow {
   private final boolean hasMainDexList;
   private final long numOfMethods;
   private final long numOfFields;
   private final long maxNumOfEntries;
 
-  public MainDexOverflowException(
+  public MainDexOverflow(
       boolean hasMainDexList, long numOfMethods, long numOfFields, long maxNumOfEntries) {
     super();
     this.hasMainDexList = hasMainDexList;
@@ -52,7 +52,6 @@
     return messageBuilder.toString();
   }
 
-  @Override
   public String getMessage() {
     // Default message
     return getGeneralMessage()
@@ -62,7 +61,6 @@
         .toString();
   }
 
-  @Override
   public String getMessageForD8() {
     StringBuilder messageBuilder = getGeneralMessage();
     if (!hasMainDexList) {
@@ -74,7 +72,6 @@
     return messageBuilder.toString();
   }
 
-  @Override
   public String getMessageForR8() {
     StringBuilder messageBuilder = getGeneralMessage();
     if (!hasMainDexList) {
@@ -85,5 +82,4 @@
     messageBuilder.append(getNumberRelatedMessage());
     return messageBuilder.toString();
   }
-
 }
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index 324362a..626e5cc 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -68,6 +68,25 @@
     return flags;
   }
 
+  public boolean isMoreVisibleThan(AccessFlags other) {
+    return visibilityOrdinal() > other.visibilityOrdinal();
+  }
+
+  private int visibilityOrdinal() {
+    // public > protected > package > private
+    if (isPublic()) {
+      return 3;
+    }
+    if (isProtected()) {
+      return 2;
+    }
+    if (isPrivate()) {
+      return 0;
+    }
+    // Package-private
+    return 1;
+  }
+
   public boolean isPublic() {
     return isSet(Constants.ACC_PUBLIC);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 6e93a90..1fd4a73 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfLabel;
@@ -201,11 +200,7 @@
 
   @Override
   public IRCode buildIR(
-      DexEncodedMethod encodedMethod,
-      AppInfo appInfo,
-      InternalOptions options,
-      Origin origin)
-      throws ApiLevelException {
+      DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
     return internalBuild(encodedMethod, appInfo, options, null, null, origin);
   }
 
@@ -216,8 +211,7 @@
       InternalOptions options,
       ValueNumberGenerator valueNumberGenerator,
       Position callerPosition,
-      Origin origin)
-      throws ApiLevelException {
+      Origin origin) {
     assert valueNumberGenerator != null;
     assert callerPosition != null;
     return internalBuild(
@@ -230,8 +224,7 @@
       InternalOptions options,
       ValueNumberGenerator generator,
       Position callerPosition,
-      Origin origin)
-      throws ApiLevelException {
+      Origin origin) {
     assert !options.isGeneratingDex() || !encodedMethod.accessFlags.isSynchronized()
         : "Converting CfCode to IR not supported for DEX output of synchronized methods.";
     CfSourceCode source = new CfSourceCode(this, encodedMethod, callerPosition, origin);
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 6f86412..60a5ef5 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
@@ -18,11 +17,7 @@
 public abstract class Code extends CachedHashValueDexItem {
 
   public abstract IRCode buildIR(
-      DexEncodedMethod encodedMethod,
-      AppInfo appInfo,
-      InternalOptions options,
-      Origin origin)
-      throws ApiLevelException;
+      DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin);
 
   public IRCode buildInliningIR(
       DexEncodedMethod encodedMethod,
@@ -30,8 +25,7 @@
       InternalOptions options,
       ValueNumberGenerator valueNumberGenerator,
       Position callerPosition,
-      Origin origin)
-      throws ApiLevelException {
+      Origin origin) {
     throw new Unreachable("Unexpected attempt to build IR graph for inlining from: "
         + getClass().getCanonicalName());
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 685f32e..b1b5f8a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.ReturnVoid;
 import com.android.tools.r8.code.SwitchPayload;
@@ -164,9 +163,8 @@
   }
 
   @Override
-  public IRCode buildIR(DexEncodedMethod encodedMethod, AppInfo appInfo,
-      InternalOptions options, Origin origin)
-      throws ApiLevelException {
+  public IRCode buildIR(
+      DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
     DexSourceCode source =
         new DexSourceCode(
             this, encodedMethod, null, options.lineNumberOptimization == LineNumberOptimization.ON);
@@ -177,11 +175,11 @@
   @Override
   public IRCode buildInliningIR(
       DexEncodedMethod encodedMethod,
-      AppInfo appInfo, InternalOptions options,
+      AppInfo appInfo,
+      InternalOptions options,
       ValueNumberGenerator valueNumberGenerator,
       Position callerPosition,
-      Origin origin)
-      throws ApiLevelException {
+      Origin origin) {
     DexSourceCode source =
         new DexSourceCode(
             this,
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index ee29ff3..9f97f88 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -9,7 +9,6 @@
 import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SUBCLASS;
 import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.cf.code.CfConstNull;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfThrow;
@@ -100,6 +99,7 @@
   private Code code;
   private CompilationState compilationState = CompilationState.NOT_PROCESSED;
   private OptimizationInfo optimizationInfo = DefaultOptimizationInfo.DEFAULT;
+  private int classFileVersion = -1;
 
   public DexEncodedMethod(
       DexMethod method,
@@ -115,6 +115,17 @@
     assert code == null || !accessFlags.isAbstract();
   }
 
+  public DexEncodedMethod(
+      DexMethod method,
+      MethodAccessFlags flags,
+      DexAnnotationSet annotationSet,
+      ParameterAnnotationsList annotationsList,
+      Code code,
+      int classFileVersion) {
+    this(method, flags, annotationSet, annotationsList, code);
+    this.classFileVersion = classFileVersion;
+  }
+
   public boolean isProcessed() {
     return compilationState != CompilationState.NOT_PROCESSED;
   }
@@ -231,23 +242,21 @@
     compilationState = CompilationState.NOT_PROCESSED;
   }
 
-  public IRCode buildIR(
-      AppInfo appInfo, InternalOptions options, Origin origin) throws ApiLevelException {
+  public IRCode buildIR(AppInfo appInfo, InternalOptions options, Origin origin) {
     return code == null ? null : code.buildIR(this, appInfo, options, origin);
   }
 
   public IRCode buildInliningIRForTesting(
-      InternalOptions options, ValueNumberGenerator valueNumberGenerator)
-      throws ApiLevelException {
+      InternalOptions options, ValueNumberGenerator valueNumberGenerator) {
     return buildInliningIR(null, options, valueNumberGenerator, null, Origin.unknown());
   }
 
   public IRCode buildInliningIR(
-      AppInfo appInfo, InternalOptions options,
+      AppInfo appInfo,
+      InternalOptions options,
       ValueNumberGenerator valueNumberGenerator,
       Position callerPosition,
-      Origin origin)
-      throws ApiLevelException {
+      Origin origin) {
     return code.buildInliningIR(
         this, appInfo, options, valueNumberGenerator, callerPosition, origin);
   }
@@ -306,6 +315,21 @@
     return code.asDexCode().hasDebugPositions();
   }
 
+  public int getClassFileVersion() {
+    assert classFileVersion >= 0;
+    return classFileVersion;
+  }
+
+  public boolean hasClassFileVersion() {
+    return classFileVersion >= 0;
+  }
+
+  public void upgradeClassFileVersion(int version) {
+    assert version >= 0;
+    assert !hasClassFileVersion() || version >= getClassFileVersion();
+    classFileVersion = version;
+  }
+
   public String qualifiedName() {
     return method.qualifiedName();
   }
@@ -756,6 +780,16 @@
     return optimizationInfo;
   }
 
+  public void copyMetadataFromInlinee(DexEncodedMethod inlinee) {
+    // Record that the current method uses identifier name string if the inlinee did so.
+    if (inlinee.getOptimizationInfo().useIdentifierNameString()) {
+      markUseIdentifierNameString();
+    }
+    if (inlinee.classFileVersion > classFileVersion) {
+      upgradeClassFileVersion(inlinee.getClassFileVersion());
+    }
+  }
+
   private static Builder builder(DexEncodedMethod from) {
     return new Builder(from);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 86ca12a..4e4a78f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -230,6 +230,10 @@
   public final DexType annotationThrows = createType("Ldalvik/annotation/Throws;");
   public final DexType annotationSynthesizedClassMap =
       createType("Lcom/android/tools/r8/annotations/SynthesizedClassMap;");
+  public final DexType annotationCovariantReturnType =
+      createType("Ldalvik/annotation/codegen/CovariantReturnType;");
+  public final DexType annotationCovariantReturnTypes =
+      createType("Ldalvik/annotation/codegen/CovariantReturnType$CovariantReturnTypes;");
 
   private static final String METAFACTORY_METHOD_NAME = "metafactory";
   private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index c1b843f..0a33000 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -356,9 +356,14 @@
   }
 
   public void setClassFileVersion(int classFileVersion) {
+    assert classFileVersion >= 0;
     this.classFileVersion = classFileVersion;
   }
 
+  public boolean hasClassFileVersion() {
+    return classFileVersion >= 0;
+  }
+
   public int getClassFileVersion() {
     assert classFileVersion != -1;
     return classFileVersion;
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 02f0801..64ba508 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import static org.objectweb.asm.ClassReader.SKIP_CODE;
 import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
 import static org.objectweb.asm.Opcodes.ACC_DEPRECATED;
 import static org.objectweb.asm.Opcodes.ASM6;
@@ -90,7 +91,7 @@
     ClassReader reader = new ClassReader(input);
     reader.accept(
         new CreateDexClassVisitor(origin, classKind, reader.b, application, classConsumer),
-        SKIP_FRAMES);
+        SKIP_FRAMES | SKIP_CODE);
   }
 
   private static int cleanAccessFlags(int access) {
@@ -560,6 +561,11 @@
 
     @Override
     public void visitCode() {
+      throw new Unreachable("visitCode() should not be called when SKIP_CODE is set");
+    }
+
+    @Override
+    public void visitEnd() {
       if (!flags.isAbstract() && !flags.isNative() && parent.classKind == ClassKind.PROGRAM) {
         if (parent.application.options.enableCfFrontend) {
           code = new LazyCfCode(method, parent.origin, parent.context, parent.application);
@@ -567,12 +573,6 @@
           code = new JarCode(method, parent.origin, parent.context, parent.application);
         }
       }
-    }
-
-    @Override
-    public void visitEnd() {
-      assert flags.isAbstract() || flags.isNative() || parent.classKind != ClassKind.PROGRAM
-          || code != null;
       ParameterAnnotationsList annotationsList;
       if (parameterAnnotationsLists == null) {
         annotationsList = ParameterAnnotationsList.empty();
@@ -595,8 +595,14 @@
             parameterFlags.toArray(new DexValue[parameterFlags.size()]),
             parent.application.getFactory()));
       }
-      DexEncodedMethod dexMethod = new DexEncodedMethod(method, flags,
-          createAnnotationSet(annotations), annotationsList, code);
+      DexEncodedMethod dexMethod =
+          new DexEncodedMethod(
+              method,
+              flags,
+              createAnnotationSet(annotations),
+              annotationsList,
+              code,
+              parent.version);
       if (flags.isStatic() || flags.isConstructor() || flags.isPrivate()) {
         parent.directMethods.add(dexMethod);
       } else {
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 3bd4cde..640172f 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.errors.InvalidDebugInfoException;
 import com.android.tools.r8.graph.JarClassFileReader.ReparseContext;
 import com.android.tools.r8.ir.code.IRCode;
@@ -104,9 +103,8 @@
   }
 
   @Override
-  public IRCode buildIR(DexEncodedMethod encodedMethod, AppInfo appInfo,
-      InternalOptions options, Origin origin)
-      throws ApiLevelException {
+  public IRCode buildIR(
+      DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
     triggerDelayedParsingIfNeccessary();
     return options.debug
         ? internalBuildWithLocals(encodedMethod, appInfo, options, null, null)
@@ -116,11 +114,11 @@
   @Override
   public IRCode buildInliningIR(
       DexEncodedMethod encodedMethod,
-      AppInfo appInfo, InternalOptions options,
+      AppInfo appInfo,
+      InternalOptions options,
       ValueNumberGenerator generator,
       Position callerPosition,
-      Origin origin)
-      throws ApiLevelException {
+      Origin origin) {
     assert generator != null;
     triggerDelayedParsingIfNeccessary();
     return options.debug
@@ -130,10 +128,10 @@
 
   private IRCode internalBuildWithLocals(
       DexEncodedMethod encodedMethod,
-      AppInfo appInfo, InternalOptions options,
+      AppInfo appInfo,
+      InternalOptions options,
       ValueNumberGenerator generator,
-      Position callerPosition)
-      throws ApiLevelException {
+      Position callerPosition) {
     try {
       return internalBuild(encodedMethod, appInfo, options, generator, callerPosition);
     } catch (InvalidDebugInfoException e) {
@@ -145,10 +143,10 @@
 
   private IRCode internalBuild(
       DexEncodedMethod encodedMethod,
-      AppInfo appInfo, InternalOptions options,
+      AppInfo appInfo,
+      InternalOptions options,
       ValueNumberGenerator generator,
-      Position callerPosition)
-      throws ApiLevelException {
+      Position callerPosition) {
     if (!options.debug) {
       node.localVariables.clear();
     }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index bd6d3f7..8ecc1ab 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.cf.code.CfArrayLength;
 import com.android.tools.r8.cf.code.CfArrayLoad;
@@ -175,9 +174,8 @@
   }
 
   @Override
-  public IRCode buildIR(DexEncodedMethod encodedMethod, AppInfo appInfo,
-      InternalOptions options, Origin origin)
-      throws ApiLevelException {
+  public IRCode buildIR(
+      DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
     return asCfCode().buildIR(encodedMethod, appInfo, options, origin);
   }
 
@@ -188,8 +186,7 @@
       InternalOptions options,
       ValueNumberGenerator valueNumberGenerator,
       Position callerPosition,
-      Origin origin)
-      throws ApiLevelException {
+      Origin origin) {
     return asCfCode().buildInliningIR(
         encodedMethod, appInfo, options, valueNumberGenerator, callerPosition, origin);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 382a0a9..e68e3d5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -5,7 +5,6 @@
 
 import static it.unimi.dsi.fastutil.ints.Int2ObjectSortedMaps.emptyMap;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.cf.code.CfFrame;
 import com.android.tools.r8.cf.code.CfFrame.FrameType;
 import com.android.tools.r8.cf.code.CfGoto;
@@ -359,8 +358,7 @@
 
   @Override
   public void buildInstruction(
-      IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
-      throws ApiLevelException {
+      IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
     CfInstruction instruction = code.getInstructions().get(instructionIndex);
     currentInstructionIndex = instructionIndex;
     if (firstBlockInstruction) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 0d87723..31ee706 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.conversion;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.code.FillArrayData;
 import com.android.tools.r8.code.FillArrayDataPayload;
 import com.android.tools.r8.code.FilledNewArray;
@@ -173,8 +172,7 @@
 
   @Override
   public void buildInstruction(
-      IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
-      throws ApiLevelException {
+      IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
     updateCurrentCatchHandlers(instructionIndex);
     updateDebugPosition(instructionIndex, builder);
     currentDexInstruction = code.instructions[instructionIndex];
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index c697105..702218e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -357,7 +357,7 @@
    *
    * @return The list of basic blocks. First block is the main entry.
    */
-  public IRCode build() throws ApiLevelException {
+  public IRCode build() {
     assert source != null;
     source.setUp();
 
@@ -516,7 +516,7 @@
     return true;
   }
 
-  private void processWorklist() throws ApiLevelException {
+  private void processWorklist() {
     for (WorklistItem item = ssaWorklist.poll(); item != null; item = ssaWorklist.poll()) {
       if (item.block.isFilled()) {
         continue;
@@ -835,8 +835,7 @@
     add(instruction);
   }
 
-  public void addConstMethodHandle(int dest, DexMethodHandle methodHandle)
-      throws ApiLevelException {
+  public void addConstMethodHandle(int dest, DexMethodHandle methodHandle) {
     if (!options.canUseConstantMethodHandle()) {
       throw new ApiLevelException(
           AndroidApiLevel.P,
@@ -848,8 +847,7 @@
     add(instruction);
   }
 
-  public void addConstMethodType(int dest, DexProto methodType)
-      throws ApiLevelException {
+  public void addConstMethodType(int dest, DexProto methodType) {
     if (!options.canUseConstantMethodType()) {
       throw new ApiLevelException(
           AndroidApiLevel.P,
@@ -1040,8 +1038,7 @@
   }
 
   public void addInvoke(
-      Type type, DexItem item, DexProto callSiteProto, List<Value> arguments, boolean itf)
-      throws ApiLevelException {
+      Type type, DexItem item, DexProto callSiteProto, List<Value> arguments, boolean itf) {
     if (type == Type.POLYMORPHIC) {
       assert item instanceof DexMethod;
       if (!options.canUseInvokePolymorphic()) {
@@ -1073,8 +1070,7 @@
     add(Invoke.create(type, item, callSiteProto, null, arguments, itf));
   }
 
-  public void addInvoke(Type type, DexItem item, DexProto callSiteProto, List<Value> arguments)
-      throws ApiLevelException {
+  public void addInvoke(Type type, DexItem item, DexProto callSiteProto, List<Value> arguments) {
     addInvoke(type, item, callSiteProto, arguments, false);
   }
 
@@ -1083,8 +1079,7 @@
       DexItem item,
       DexProto callSiteProto,
       List<ValueType> types,
-      List<Integer> registers)
-      throws ApiLevelException {
+      List<Integer> registers) {
     addInvoke(type, item, callSiteProto, types, registers, false);
   }
 
@@ -1094,8 +1089,7 @@
       DexProto callSiteProto,
       List<ValueType> types,
       List<Integer> registers,
-      boolean itf)
-      throws ApiLevelException {
+      boolean itf) {
     assert types.size() == registers.size();
     List<Value> arguments = new ArrayList<>(types.size());
     for (int i = 0; i < types.size(); i++) {
@@ -1163,8 +1157,7 @@
       DexMethod method,
       DexProto callSiteProto,
       int argumentRegisterCount,
-      int[] argumentRegisters)
-      throws ApiLevelException {
+      int[] argumentRegisters) {
     // The value of argumentRegisterCount is the number of registers - not the number of values,
     // but it is an upper bound on the number of arguments.
     List<Value> arguments = new ArrayList<>(argumentRegisterCount);
@@ -1191,8 +1184,7 @@
     addInvoke(type, method, callSiteProto, arguments);
   }
 
-  public void addInvokeNewArray(DexType type, int argumentCount, int[] argumentRegisters)
-      throws ApiLevelException {
+  public void addInvokeNewArray(DexType type, int argumentCount, int[] argumentRegisters) {
     String descriptor = type.descriptor.toString();
     assert descriptor.charAt(0) == '[';
     assert descriptor.length() >= 2;
@@ -1211,7 +1203,7 @@
     addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments);
   }
 
-  public void addMultiNewArray(DexType type, int dest, int[] dimensions) throws ApiLevelException {
+  public void addMultiNewArray(DexType type, int dest, int[] dimensions) {
     assert isGeneratingClassFiles();
     List<Value> arguments = new ArrayList<>(dimensions.length);
     for (int dimension : dimensions) {
@@ -1226,8 +1218,7 @@
       DexMethod method,
       DexProto callSiteProto,
       int argumentCount,
-      int firstArgumentRegister)
-      throws ApiLevelException {
+      int firstArgumentRegister) {
     // The value of argumentCount is the number of registers - not the number of values, but it
     // is an upper bound on the number of arguments.
     List<Value> arguments = new ArrayList<>(argumentCount);
@@ -1254,8 +1245,7 @@
     addInvoke(type, method, callSiteProto, arguments);
   }
 
-  public void addInvokeRangeNewArray(DexType type, int argumentCount, int firstArgumentRegister)
-      throws ApiLevelException {
+  public void addInvokeRangeNewArray(DexType type, int argumentCount, int firstArgumentRegister) {
     String descriptor = type.descriptor.toString();
     assert descriptor.charAt(0) == '[';
     assert descriptor.length() >= 2;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 8477d9b..90a9898 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -33,6 +32,7 @@
 import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.desugar.LambdaRewriter;
 import com.android.tools.r8.ir.desugar.StringConcatRewriter;
@@ -100,6 +100,7 @@
   private final ProtoLitePruner protoLiteRewriter;
   private final IdentifierNameStringMarker identifierNameStringMarker;
   private final Devirtualizer devirtualizer;
+  private final CovariantReturnTypeAnnotationTransformer covariantReturnTypeAnnotationTransformer;
 
   private final OptimizationFeedback ignoreOptimizationFeedback = new OptimizationFeedbackIgnore();
   private DexString highestSortingString;
@@ -127,6 +128,10 @@
             ? new InterfaceMethodRewriter(this, options) : null;
     this.lambdaMerger = options.enableLambdaMerging
         ? new LambdaMerger(appInfo.dexItemFactory, options.reporter) : null;
+    this.covariantReturnTypeAnnotationTransformer =
+        options.processCovariantReturnTypeAnnotations
+            ? new CovariantReturnTypeAnnotationTransformer(this, appInfo.dexItemFactory)
+            : null;
     if (enableWholeProgramOptimizations) {
       assert appInfo.hasLiveness();
       this.nonNullTracker = new NonNullTracker();
@@ -233,7 +238,7 @@
     }
   }
 
-  private void synthesizeLambdaClasses(Builder<?> builder) throws ApiLevelException {
+  private void synthesizeLambdaClasses(Builder<?> builder) {
     if (lambdaRewriter != null) {
       lambdaRewriter.adjustAccessibility();
       lambdaRewriter.synthesizeLambdaClasses(builder);
@@ -241,15 +246,20 @@
   }
 
   private void desugarInterfaceMethods(
-      Builder<?> builder, InterfaceMethodRewriter.Flavor includeAllResources)
-      throws ApiLevelException {
+      Builder<?> builder, InterfaceMethodRewriter.Flavor includeAllResources) {
     if (interfaceMethodRewriter != null) {
       interfaceMethodRewriter.desugarInterfaceMethods(builder, includeAllResources);
     }
   }
 
+  private void processCovariantReturnTypeAnnotations(Builder<?> builder) {
+    if (covariantReturnTypeAnnotationTransformer != null) {
+      covariantReturnTypeAnnotationTransformer.process(builder);
+    }
+  }
+
   public DexApplication convertToDex(DexApplication application, ExecutorService executor)
-      throws ExecutionException, ApiLevelException {
+      throws ExecutionException {
     removeLambdaDeserializationMethods();
 
     timing.begin("IR conversion");
@@ -261,6 +271,7 @@
 
     synthesizeLambdaClasses(builder);
     desugarInterfaceMethods(builder, ExcludeDexResources);
+    processCovariantReturnTypeAnnotations(builder);
 
     handleSynthesizedClassMapping(builder);
     timing.end();
@@ -347,7 +358,7 @@
     ThreadUtils.awaitFutures(futures);
   }
 
-  void convertMethodToDex(DexEncodedMethod method) throws ApiLevelException {
+  void convertMethodToDex(DexEncodedMethod method) {
     assert options.isGeneratingDex();
     if (method.getCode() != null) {
       boolean matchesMethodFilter = options.methodMatchesFilter(method);
@@ -362,8 +373,7 @@
     }
   }
 
-  public DexApplication optimize(DexApplication application)
-      throws ExecutionException, ApiLevelException {
+  public DexApplication optimize(DexApplication application) throws ExecutionException {
     ExecutorService executor = Executors.newSingleThreadExecutor();
     try {
       return optimize(application, executor);
@@ -372,9 +382,8 @@
     }
   }
 
-  public DexApplication optimize(DexApplication application,
-      ExecutorService executorService)
-      throws ExecutionException, ApiLevelException {
+  public DexApplication optimize(DexApplication application, ExecutorService executorService)
+      throws ExecutionException {
     removeLambdaDeserializationMethods();
     collectLambdaMergingCandidates(application);
 
@@ -392,10 +401,14 @@
           .build(application, appInfo.withLiveness(), graphLense, options);
       timing.end();
       timing.begin("IR conversion phase 1");
-      callGraph.forEachMethod((method, isProcessedConcurrently) -> {
-        processMethod(method, directFeedback, isProcessedConcurrently, callGraph,
-            outliner == null ? Outliner::noProcessing : outliner::identifyCandidates);
-      }, executorService);
+      BiConsumer<IRCode, DexEncodedMethod> outlineHandler =
+          outliner == null ? Outliner::noProcessing : outliner.identifyCandidateMethods();
+      callGraph.forEachMethod(
+          (method, isProcessedConcurrently) -> {
+            processMethod(
+                method, directFeedback, isProcessedConcurrently, callGraph, outlineHandler);
+          },
+          executorService);
       timing.end();
     }
 
@@ -418,25 +431,23 @@
 
     if (outliner != null) {
       timing.begin("IR conversion phase 2");
-      // Compile all classes flagged for outlining and
-      // add the outline support class IF needed.
-      DexProgramClass outlineClass = prepareOutlining();
-      if (outlineClass != null) {
-        // We need a new call graph to ensure deterministic order and also processing inside out
-        // to get maximal inlining. Use a identity lense, as the code has been rewritten.
-        CallGraph callGraph = CallGraph
-            .build(application, appInfo.withLiveness(), GraphLense.getIdentityLense(), options);
-        Set<DexEncodedMethod> outlineMethods = outliner.getMethodsSelectedForOutlining();
-        callGraph.forEachMethod((method, isProcessedConcurrently) -> {
-          if (!outlineMethods.contains(method)) {
-            return;
-          }
-          // This is the second time we compile this method first mark it not processed.
-          assert !method.getCode().isOutlineCode();
-          processMethod(method, ignoreOptimizationFeedback, isProcessedConcurrently, callGraph,
-              outliner::applyOutliningCandidate);
-          assert method.isProcessed();
-        }, executorService);
+      if (outliner.selectMethodsForOutlining()) {
+        forEachSelectedOutliningMethod(
+            executorService,
+            (code, method) -> {
+              printMethod(code, "IR before outlining (SSA)");
+              outliner.identifyOutlineSites(code, method);
+            });
+        DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType());
+        optimizeSynthesizedClass(outlineClass);
+        forEachSelectedOutliningMethod(
+            executorService,
+            (code, method) -> {
+              outliner.applyOutliningCandidate(code, method);
+              printMethod(code, "IR after outlining (SSA)");
+              finalizeIR(method, code, ignoreOptimizationFeedback);
+            });
+        assert outliner.checkAllOutlineSitesFoundAgain();
         builder.addSynthesizedClass(outlineClass, true);
         clearDexMethodCompilationState(outlineClass);
       }
@@ -451,15 +462,45 @@
     return builder.build();
   }
 
+  private void forEachSelectedOutliningMethod(
+      ExecutorService executorService, BiConsumer<IRCode, DexEncodedMethod> consumer)
+      throws ExecutionException {
+    assert !options.skipIR;
+    Set<DexEncodedMethod> methods = outliner.getMethodsSelectedForOutlining();
+    List<Future<?>> futures = new ArrayList<>();
+    for (DexEncodedMethod method : methods) {
+      futures.add(
+          executorService.submit(
+              () -> {
+                IRCode code =
+                    method.buildIR(appInfo, options, appInfo.originFor(method.method.holder));
+                assert code != null;
+                assert !method.getCode().isOutlineCode();
+                // Instead of repeating all the optimizations of rewriteCode(), only run the
+                // optimizations needed for outlining: rewriteMoveResult() to remove out-values on
+                // StringBuilder/StringBuffer method invocations, and removeDeadCode() to remove
+                // unused out-values.
+                codeRewriter.rewriteMoveResult(code);
+                DeadCodeRemover.removeDeadCode(code, codeRewriter, options);
+                consumer.accept(code, method);
+                return null;
+              }));
+    }
+    ThreadUtils.awaitFutures(futures);
+  }
+
   private void collectLambdaMergingCandidates(DexApplication application) {
     if (lambdaMerger != null) {
       lambdaMerger.collectGroupCandidates(application, appInfo.withLiveness(), options);
     }
   }
 
-  private void finalizeLambdaMerging(DexApplication application,
-      OptimizationFeedback directFeedback, Builder<?> builder, ExecutorService executorService)
-      throws ExecutionException, ApiLevelException {
+  private void finalizeLambdaMerging(
+      DexApplication application,
+      OptimizationFeedback directFeedback,
+      Builder<?> builder,
+      ExecutorService executorService)
+      throws ExecutionException {
     if (lambdaMerger != null) {
       lambdaMerger.applyLambdaClassMapping(
           application, this, directFeedback, builder, executorService);
@@ -512,16 +553,7 @@
     return result;
   }
 
-  private DexProgramClass prepareOutlining() throws ApiLevelException {
-    if (!outliner.selectMethodsForOutlining()) {
-      return null;
-    }
-    DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType());
-    optimizeSynthesizedClass(outlineClass);
-    return outlineClass;
-  }
-
-  public void optimizeSynthesizedClass(DexProgramClass clazz) throws ApiLevelException {
+  public void optimizeSynthesizedClass(DexProgramClass clazz) {
     try {
       codeRewriter.enterCachedClass(clazz);
       // Process the generated class, but don't apply any outlining.
@@ -531,7 +563,7 @@
     }
   }
 
-  public void optimizeSynthesizedMethod(DexEncodedMethod method) throws ApiLevelException {
+  public void optimizeSynthesizedMethod(DexEncodedMethod method) {
     // Process the generated method, but don't apply any outlining.
     processMethod(method, ignoreOptimizationFeedback, x -> false, CallSiteInformation.empty(),
         Outliner::noProcessing);
@@ -541,12 +573,12 @@
     return options.useSmaliSyntax ? method.toSmaliString(null) : method.codeToString();
   }
 
-  public void processMethod(DexEncodedMethod method,
+  public void processMethod(
+      DexEncodedMethod method,
       OptimizationFeedback feedback,
       Predicate<DexEncodedMethod> isProcessedConcurrently,
       CallSiteInformation callSiteInformation,
-      BiConsumer<IRCode, DexEncodedMethod> outlineHandler)
-      throws ApiLevelException {
+      BiConsumer<IRCode, DexEncodedMethod> outlineHandler) {
     Code code = method.getCode();
     boolean matchesMethodFilter = options.methodMatchesFilter(method);
     if (code != null && matchesMethodFilter) {
@@ -565,12 +597,12 @@
     }
   }
 
-  private void rewriteCode(DexEncodedMethod method,
+  private void rewriteCode(
+      DexEncodedMethod method,
       OptimizationFeedback feedback,
       Predicate<DexEncodedMethod> isProcessedConcurrently,
       CallSiteInformation callSiteInformation,
-      BiConsumer<IRCode, DexEncodedMethod> outlineHandler)
-      throws ApiLevelException {
+      BiConsumer<IRCode, DexEncodedMethod> outlineHandler) {
     if (options.verbose) {
       options.reporter.info(
           new StringDiagnostic("Processing: " + method.toSourceString()));
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 46cc8e9..b5f0a05 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.conversion;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
@@ -29,7 +28,6 @@
 import com.android.tools.r8.ir.conversion.JarState.Local;
 import com.android.tools.r8.ir.conversion.JarState.Slot;
 import com.android.tools.r8.logging.Log;
-import com.android.tools.r8.utils.ThrowingBiConsumer;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
@@ -44,6 +42,7 @@
 import java.util.List;
 import java.util.Queue;
 import java.util.Set;
+import java.util.function.BiConsumer;
 import org.objectweb.asm.Handle;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
@@ -478,8 +477,7 @@
 
   @Override
   public void buildInstruction(
-      IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
-      throws ApiLevelException {
+      IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
     if (instructionIndex == EXCEPTIONAL_SYNC_EXIT_OFFSET) {
       buildExceptionalPostlude(builder);
       return;
@@ -1816,7 +1814,7 @@
 
   // IR instruction building procedures.
 
-  private void build(AbstractInsnNode insn, IRBuilder builder) throws ApiLevelException {
+  private void build(AbstractInsnNode insn, IRBuilder builder) {
     switch (insn.getType()) {
       case AbstractInsnNode.INSN:
         build((InsnNode) insn, builder);
@@ -2540,7 +2538,7 @@
     }
   }
 
-  private void build(MethodInsnNode insn, IRBuilder builder) throws ApiLevelException {
+  private void build(MethodInsnNode insn, IRBuilder builder) {
     // Resolve the target method of the invoke.
     DexMethod method = application.getMethod(insn.owner, insn.name, insn.desc);
 
@@ -2619,8 +2617,7 @@
       Type methodOwner,
       boolean addImplicitReceiver,
       IRBuilder builder,
-      ThrowingBiConsumer<List<ValueType>, List<Integer>, ApiLevelException> creator)
-      throws ApiLevelException {
+      BiConsumer<List<ValueType>, List<Integer>> creator) {
 
     // Build the argument list of the form [owner, param1, ..., paramN].
     // The arguments are in reverse order on the stack, so we pop off the parameters here.
@@ -2657,7 +2654,7 @@
     registers.add(slot.register);
   }
 
-  private void build(InvokeDynamicInsnNode insn, IRBuilder builder) throws ApiLevelException {
+  private void build(InvokeDynamicInsnNode insn, IRBuilder builder) {
     DexCallSite callSite = DexCallSite.fromAsmInvokeDynamic(insn, application, clazz);
 
     buildInvoke(insn.desc, null /* Not needed */,
@@ -2716,7 +2713,7 @@
     // Intentionally empty.
   }
 
-  private void build(LdcInsnNode insn, IRBuilder builder) throws ApiLevelException {
+  private void build(LdcInsnNode insn, IRBuilder builder) {
     if (insn.cst instanceof Type) {
       Type type = (Type) insn.cst;
       if (type.getSort() == Type.METHOD) {
@@ -2781,7 +2778,7 @@
     builder.addSwitch(index, keys, fallthroughOffset, labelOffsets);
   }
 
-  private void build(MultiANewArrayInsnNode insn, IRBuilder builder) throws ApiLevelException {
+  private void build(MultiANewArrayInsnNode insn, IRBuilder builder) {
     // Type of the full array.
     Type arrayType = application.getAsmObjectType(insn.desc);
     DexType dexArrayType = application.getType(arrayType);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
index cd8efa7..e95790a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.conversion;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.Position;
@@ -48,8 +47,8 @@
   // Delegates for IR building.
   void buildPrelude(IRBuilder builder);
 
-  void buildInstruction(IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
-      throws ApiLevelException;
+  void buildInstruction(IRBuilder builder, int instructionIndex, boolean firstBlockInstruction);
+
   void buildPostlude(IRBuilder builder);
 
   // Helper to resolve switch payloads and build switch instructions (dex code only).
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
new file mode 100644
index 0000000..5b397e0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -0,0 +1,266 @@
+// Copyright (c) 2018, 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.ir.desugar;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
+import com.android.tools.r8.ir.synthetic.SynthesizedCode;
+import com.google.common.base.Predicates;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+// Responsible for processing the annotations dalvik.annotation.codegen.CovariantReturnType and
+// dalvik.annotation.codegen.CovariantReturnType$CovariantReturnTypes.
+//
+// Consider the following class:
+//   public class B extends A {
+//     @CovariantReturnType(returnType = B.class, presentAfter = 25)
+//     @Override
+//     public A m(...) { ... return new B(); }
+//   }
+//
+// The annotation is used to indicate that the compiler should insert a synthetic method that is
+// equivalent to method m, but has return type B instead of A. Thus, for this example, this
+// component is responsible for inserting the following method in class B (in addition to the
+// existing method m):
+//   public B m(...) { A result = "invoke B.m(...)A;"; return (B) result; }
+//
+// Note that a method may be annotated with more than one CovariantReturnType annotation. In this
+// case there will be a CovariantReturnType$CovariantReturnTypes annotation on the method that wraps
+// several CovariantReturnType annotations. In this case, a new method is synthesized for each of
+// the contained CovariantReturnType annotations.
+public final class CovariantReturnTypeAnnotationTransformer {
+  private final IRConverter converter;
+  private final DexItemFactory factory;
+
+  public CovariantReturnTypeAnnotationTransformer(IRConverter converter, DexItemFactory factory) {
+    this.converter = converter;
+    this.factory = factory;
+  }
+
+  public void process(DexApplication.Builder<?> builder) {
+    // List of methods that should be added to the next class.
+    List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation = new LinkedList<>();
+    List<DexEncodedMethod> covariantReturnTypeMethods = new LinkedList<>();
+    for (DexClass clazz : builder.getProgramClasses()) {
+      // Construct the methods that should be added to clazz.
+      buildCovariantReturnTypeMethodsForClass(
+          clazz, methodsWithCovariantReturnTypeAnnotation, covariantReturnTypeMethods);
+      if (covariantReturnTypeMethods.isEmpty()) {
+        continue;
+      }
+      updateClass(clazz, methodsWithCovariantReturnTypeAnnotation, covariantReturnTypeMethods);
+      // Reset lists for the next class that will have a CovariantReturnType or
+      // CovariantReturnType$CovariantReturnTypes annotation.
+      methodsWithCovariantReturnTypeAnnotation.clear();
+      covariantReturnTypeMethods.clear();
+    }
+  }
+
+  private void updateClass(
+      DexClass clazz,
+      List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation,
+      List<DexEncodedMethod> covariantReturnTypeMethods) {
+    // It is a compilation error if the class already has a method with a signature similar to one
+    // of the methods in covariantReturnTypeMethods.
+    for (DexEncodedMethod syntheticMethod : covariantReturnTypeMethods) {
+      if (hasVirtualMethodWithSignature(clazz, syntheticMethod)) {
+        throw new CompilationError(
+            String.format(
+                "Cannot process CovariantReturnType annotation: Class %s already "
+                    + "has a method \"%s\"",
+                clazz.getType(), syntheticMethod.toSourceString()));
+      }
+    }
+    // Remove the CovariantReturnType annotations.
+    for (DexEncodedMethod method : methodsWithCovariantReturnTypeAnnotation) {
+      method.annotations =
+          method.annotations.keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation));
+    }
+    // Add the newly constructed methods to the class.
+    DexEncodedMethod[] oldVirtualMethods = clazz.virtualMethods();
+    DexEncodedMethod[] newVirtualMethods =
+        new DexEncodedMethod[oldVirtualMethods.length + covariantReturnTypeMethods.size()];
+    System.arraycopy(oldVirtualMethods, 0, newVirtualMethods, 0, oldVirtualMethods.length);
+    int i = oldVirtualMethods.length;
+    for (DexEncodedMethod syntheticMethod : covariantReturnTypeMethods) {
+      newVirtualMethods[i] = syntheticMethod;
+      i++;
+    }
+    clazz.setVirtualMethods(newVirtualMethods);
+  }
+
+  // Processes all the dalvik.annotation.codegen.CovariantReturnType and dalvik.annotation.codegen.
+  // CovariantReturnTypes annotations in the given DexClass. Adds the newly constructed, synthetic
+  // methods to the list covariantReturnTypeMethods.
+  private void buildCovariantReturnTypeMethodsForClass(
+      DexClass clazz,
+      List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation,
+      List<DexEncodedMethod> covariantReturnTypeMethods) {
+    for (DexEncodedMethod method : clazz.virtualMethods()) {
+      if (methodHasCovariantReturnTypeAnnotation(method)) {
+        methodsWithCovariantReturnTypeAnnotation.add(method);
+        buildCovariantReturnTypeMethodsForMethod(clazz, method, covariantReturnTypeMethods);
+      }
+    }
+  }
+
+  private boolean methodHasCovariantReturnTypeAnnotation(DexEncodedMethod method) {
+    for (DexAnnotation annotation : method.annotations.annotations) {
+      if (isCovariantReturnTypeAnnotation(annotation.annotation)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // Processes all the dalvik.annotation.codegen.CovariantReturnType and dalvik.annotation.Co-
+  // variantReturnTypes annotations on the given method. Adds the newly constructed, synthetic
+  // methods to the list covariantReturnTypeMethods.
+  private void buildCovariantReturnTypeMethodsForMethod(
+      DexClass clazz, DexEncodedMethod method, List<DexEncodedMethod> covariantReturnTypeMethods) {
+    assert methodHasCovariantReturnTypeAnnotation(method);
+    for (DexType covariantReturnType : getCovariantReturnTypes(clazz, method)) {
+      DexEncodedMethod covariantReturnTypeMethod =
+          buildCovariantReturnTypeMethod(clazz, method, covariantReturnType);
+      covariantReturnTypeMethods.add(covariantReturnTypeMethod);
+    }
+  }
+
+  // Builds a synthetic method that invokes the given method, casts the result to
+  // covariantReturnType, and then returns the result. The newly created method will have return
+  // type covariantReturnType.
+  //
+  // Note: any "synchronized" or "strictfp" modifier could be dropped safely.
+  private DexEncodedMethod buildCovariantReturnTypeMethod(
+      DexClass clazz, DexEncodedMethod method, DexType covariantReturnType) {
+    DexProto newProto =
+        factory.createProto(
+            covariantReturnType, method.method.proto.shorty, method.method.proto.parameters);
+    MethodAccessFlags newAccessFlags = method.accessFlags.copy();
+    newAccessFlags.setBridge();
+    newAccessFlags.setSynthetic();
+    DexEncodedMethod newVirtualMethod =
+        new DexEncodedMethod(
+            factory.createMethod(method.method.holder, newProto, method.method.name),
+            newAccessFlags,
+            method.annotations.keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation)),
+            method.parameterAnnotationsList.keepIf(Predicates.alwaysTrue()),
+            new SynthesizedCode(
+                new ForwardMethodSourceCode(
+                    clazz.type,
+                    newProto,
+                    method.method.holder,
+                    method.method,
+                    Invoke.Type.VIRTUAL,
+                    true)));
+    // Optimize to generate DexCode instead of SynthesizedCode.
+    converter.optimizeSynthesizedMethod(newVirtualMethod);
+    return newVirtualMethod;
+  }
+
+  // Returns the set of covariant return types for method.
+  //
+  // If the method is:
+  //   @dalvik.annotation.codegen.CovariantReturnType(returnType=SubOfFoo, presentAfter=25)
+  //   @dalvik.annotation.codegen.CovariantReturnType(returnType=SubOfSubOfFoo, presentAfter=28)
+  //   @Override
+  //   public Foo foo() { ... return new SubOfSubOfFoo(); }
+  // then this method returns the set { SubOfFoo, SubOfSubOfFoo }.
+  private Set<DexType> getCovariantReturnTypes(DexClass clazz, DexEncodedMethod method) {
+    Set<DexType> covariantReturnTypes = new HashSet<>();
+    for (DexAnnotation annotation : method.annotations.annotations) {
+      if (isCovariantReturnTypeAnnotation(annotation.annotation)) {
+        getCovariantReturnTypesFromAnnotation(
+            clazz, method, annotation.annotation, covariantReturnTypes);
+      }
+    }
+    return covariantReturnTypes;
+  }
+
+  private void getCovariantReturnTypesFromAnnotation(
+      DexClass clazz,
+      DexEncodedMethod method,
+      DexEncodedAnnotation annotation,
+      Set<DexType> covariantReturnTypes) {
+    assert isCovariantReturnTypeAnnotation(annotation);
+    boolean hasPresentAfterElement = false;
+    for (DexAnnotationElement element : annotation.elements) {
+      String name = element.name.toString();
+      if (annotation.type == factory.annotationCovariantReturnType) {
+        if (name.equals("returnType")) {
+          if (!(element.value instanceof DexValue.DexValueType)) {
+            throw new CompilationError(
+                String.format(
+                    "Expected element \"returnType\" of CovariantReturnType annotation to "
+                        + "reference a type (method: \"%s\", was: %s)",
+                    method.toSourceString(), element.value.getClass().getCanonicalName()));
+          }
+
+          DexValue.DexValueType dexValueType = (DexValue.DexValueType) element.value;
+          covariantReturnTypes.add(dexValueType.value);
+        } else if (name.equals("presentAfter")) {
+          hasPresentAfterElement = true;
+        }
+      } else {
+        if (name.equals("value")) {
+          if (!(element.value instanceof DexValue.DexValueArray)) {
+            throw new CompilationError(
+                String.format(
+                    "Expected element \"value\" of CovariantReturnTypes annotation to "
+                        + "be an array (method: \"%s\", was: %s)",
+                    method.toSourceString(), element.value.getClass().getCanonicalName()));
+          }
+
+          DexValue.DexValueArray array = (DexValue.DexValueArray) element.value;
+          // Handle the inner dalvik.annotation.codegen.CovariantReturnType annotations recursively.
+          for (DexValue value : array.getValues()) {
+            assert value instanceof DexValue.DexValueAnnotation;
+            DexValue.DexValueAnnotation innerAnnotation = (DexValue.DexValueAnnotation) value;
+            getCovariantReturnTypesFromAnnotation(
+                clazz, method, innerAnnotation.value, covariantReturnTypes);
+          }
+        }
+      }
+    }
+
+    if (annotation.type == factory.annotationCovariantReturnType && !hasPresentAfterElement) {
+      throw new CompilationError(
+          String.format(
+              "CovariantReturnType annotation for method \"%s\" is missing mandatory element "
+                  + "\"presentAfter\" (class %s)",
+              clazz.getType(), method.toSourceString()));
+    }
+  }
+
+  private boolean isCovariantReturnTypeAnnotation(DexEncodedAnnotation annotation) {
+    return annotation.type == factory.annotationCovariantReturnType
+        || annotation.type == factory.annotationCovariantReturnTypes;
+  }
+
+  private static boolean hasVirtualMethodWithSignature(DexClass clazz, DexEncodedMethod method) {
+    for (DexEncodedMethod existingMethod : clazz.virtualMethods()) {
+      if (existingMethod.method.equals(method.method)) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 8add960..73c5118 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.desugar;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.DexApplication.Builder;
@@ -325,7 +324,7 @@
    * Move static and default interface methods to companion classes,
    * add missing methods to forward to moved default methods implementation.
    */
-  public void desugarInterfaceMethods(Builder<?> builder, Flavor flavour) throws ApiLevelException {
+  public void desugarInterfaceMethods(Builder<?> builder, Flavor flavour) {
     // Process all classes first. Add missing forwarding methods to
     // replace desugared default interface methods.
     forwardingMethods.addAll(processClasses(builder, flavour));
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 d9bd4bd..81407bd 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
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.desugar;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
@@ -425,7 +424,7 @@
     }
 
     // Ensure access of the referenced symbol(s).
-    abstract boolean ensureAccessibility() throws ApiLevelException;
+    abstract boolean ensureAccessibility();
 
     DexClass definitionFor(DexType type) {
       return rewriter.converter.appInfo.app.definitionFor(type);
@@ -520,7 +519,7 @@
     }
 
     @Override
-    boolean ensureAccessibility() throws ApiLevelException {
+    boolean ensureAccessibility() {
       // Create a static accessor with proper accessibility.
       DexProgramClass accessorClass = programDefinitionFor(callTarget.holder);
       assert accessorClass != null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 11ff65b..d6b99a7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.desugar;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication.Builder;
@@ -162,7 +161,7 @@
    * Adjust accessibility of referenced application symbols or
    * creates necessary accessors.
    */
-  public void adjustAccessibility() throws ApiLevelException {
+  public void adjustAccessibility() {
     // For each lambda class perform necessary adjustment of the
     // referenced symbols to make them accessible. This can result in
     // method access relaxation or creation of accessor method.
@@ -172,7 +171,7 @@
   }
 
   /** Generates lambda classes and adds them to the builder. */
-  public void synthesizeLambdaClasses(Builder<?> builder) throws ApiLevelException {
+  public void synthesizeLambdaClasses(Builder<?> builder) {
     for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
       DexProgramClass synthesizedClass = lambdaClass.synthesizeLambdaClass();
       converter.optimizeSynthesizedClass(synthesizedClass);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 90081d1..845e17b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -305,8 +304,7 @@
   }
 
   @Override
-  public void ensureMethodProcessed(
-      DexEncodedMethod target, IRCode inlinee) throws ApiLevelException {
+  public void ensureMethodProcessed(DexEncodedMethod target, IRCode inlinee) {
     if (!target.isProcessed()) {
       if (Log.ENABLED) {
         Log.verbose(getClass(), "Forcing extra inline on " + target.toSourceString());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/EnumOrdinalMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/EnumOrdinalMapCollector.java
index 59ddc36..1b68f7d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/EnumOrdinalMapCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/EnumOrdinalMapCollector.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -40,7 +39,7 @@
     this.options = options;
   }
 
-  public AppInfoWithLiveness run() throws ApiLevelException {
+  public AppInfoWithLiveness run() {
     for (DexProgramClass clazz : appInfo.classes()) {
       processClasses(clazz);
     }
@@ -50,7 +49,7 @@
     return appInfo;
   }
 
-  private void processClasses(DexProgramClass clazz) throws ApiLevelException {
+  private void processClasses(DexProgramClass clazz) {
     // Enum classes are flagged as such. Also, for library classes, the ordinals are not known.
     if (!clazz.accessFlags.isEnum() || clazz.isLibraryClass() || !clazz.hasClassInitializer()) {
       return;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 860cc21..b75597d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.AccessFlags;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexClass;
@@ -151,8 +150,8 @@
     return target;
   }
 
-  public synchronized void processDoubleInlineCallers(IRConverter converter,
-      OptimizationFeedback feedback) throws ApiLevelException {
+  public synchronized void processDoubleInlineCallers(
+      IRConverter converter, OptimizationFeedback feedback) {
     if (doubleInlineCallers.size() > 0) {
       applyDoubleInlining = true;
       List<DexEncodedMethod> methods = doubleInlineCallers
@@ -267,8 +266,7 @@
         AppInfoWithSubtyping appInfo,
         GraphLense graphLense,
         InternalOptions options,
-        Position callerPosition)
-        throws ApiLevelException {
+        Position callerPosition) {
       // Build the IR for a yet not processed method, and perform minimal IR processing.
       Origin origin = appInfo.originFor(target.method.holder);
       IRCode code = target.buildInliningIR(appInfo, options, generator, callerPosition, origin);
@@ -375,8 +373,10 @@
     }
   }
 
-  public void performForcedInlining(DexEncodedMethod method, IRCode code,
-      Map<InvokeMethodWithReceiver, InliningInfo> invokesToInline) throws ApiLevelException {
+  public void performForcedInlining(
+      DexEncodedMethod method,
+      IRCode code,
+      Map<InvokeMethodWithReceiver, InliningInfo> invokesToInline) {
 
     ForcedInliningOracle oracle = new ForcedInliningOracle(method, invokesToInline);
     performInliningImpl(oracle, oracle, method, code);
@@ -387,8 +387,7 @@
       IRCode code,
       TypeEnvironment typeEnvironment,
       Predicate<DexEncodedMethod> isProcessedConcurrently,
-      CallSiteInformation callSiteInformation)
-      throws ApiLevelException {
+      CallSiteInformation callSiteInformation) {
 
     DefaultInliningOracle oracle =
         new DefaultInliningOracle(
@@ -405,11 +404,7 @@
   }
 
   private void performInliningImpl(
-      InliningStrategy strategy,
-      InliningOracle oracle,
-      DexEncodedMethod method,
-      IRCode code)
-      throws ApiLevelException {
+      InliningStrategy strategy, InliningOracle oracle, DexEncodedMethod method, IRCode code) {
     if (strategy.exceededAllowance()) {
       return;
     }
@@ -473,10 +468,7 @@
                   method.accessFlags.unsetBridge();
                 }
 
-                // Record that the current method uses identifier name string if the inlinee did so.
-                if (target.getOptimizationInfo().useIdentifierNameString()) {
-                  method.markUseIdentifierNameString();
-                }
+                method.copyMetadataFromInlinee(target);
               }
             }
           }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
index 2b0c7e5..7b88b38 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.optimize;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -17,8 +16,7 @@
 
   void markInlined(IRCode inlinee);
 
-  void ensureMethodProcessed(
-      DexEncodedMethod target, IRCode inlinee) throws ApiLevelException;
+  void ensureMethodProcessed(DexEncodedMethod target, IRCode inlinee);
 
   ListIterator<BasicBlock> updateTypeInformationIfNeeded(IRCode inlinee,
       ListIterator<BasicBlock> blockIterator, BasicBlock block, BasicBlock invokeSuccessor);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 9462354..6cb8f6e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.optimize;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
@@ -64,13 +63,45 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.function.BiConsumer;
+import org.objectweb.asm.Opcodes;
 
+/**
+ * Support class for implementing outlining (i.e. extracting common code patterns as methods).
+ *
+ * <p>Outlining happens in three steps.
+ *
+ * <ul>
+ *   <li>First, all methods are converted to IR and passed to {@link
+ *       Outliner#identifyCandidateMethods()} to identify outlining candidates and the methods
+ *       containing each candidate. IR is converted to the output format (DEX or CF) and thrown away
+ *       along with the outlining candidates; only a list of lists of methods is kept, where each
+ *       list of methods corresponds to methods containing an outlining candidate.
+ *   <li>Second, {@link Outliner#selectMethodsForOutlining()} is called to retain the lists of
+ *       methods found in the first step that are large enough (see {@link InternalOptions#outline}
+ *       {@link OutlineOptions#threshold}), and the methods to be further analyzed for outlining is
+ *       returned by {@link Outliner#getMethodsSelectedForOutlining}. Each selected method is then
+ *       converted back to IR and passed to {@link Outliner#identifyOutlineSites(IRCode,
+ *       DexEncodedMethod)}, which then stores concrete outlining candidates in {@link
+ *       Outliner#outlineSites}.
+ *   <li>Third, {@link Outliner#buildOutlinerClass(DexType)} is called to construct the <em>outline
+ *       support class</em> containing a static helper method for each outline candidate that occurs
+ *       frequently enough. Each selected method is then converted to IR, passed to {@link
+ *       Outliner#applyOutliningCandidate(IRCode, DexEncodedMethod)} to perform the outlining, and
+ *       converted back to the output format (DEX or CF).
+ * </ul>
+ */
 public class Outliner {
 
   private final InternalOptions options;
-  private final Map<Outline, List<DexEncodedMethod>> candidates = new HashMap<>();
-  private final Map<Outline, DexMethod> generatedOutlines = new HashMap<>();
+  /** Result of first step (see {@link Outliner#identifyCandidateMethods()}. */
+  private final List<List<DexEncodedMethod>> candidateMethodLists = new ArrayList<>();
+  /** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */
   private final Set<DexEncodedMethod> methodsSelectedForOutlining = Sets.newIdentityHashSet();
+  /** Result of second step (see {@link Outliner#selectMethodsForOutlining()}. */
+  private final Map<Outline, List<DexEncodedMethod>> outlineSites = new HashMap<>();
+  /** Result of third step (see {@link Outliner#buildOutlinerClass(DexType)}. */
+  private final Map<Outline, DexMethod> generatedOutlines = new HashMap<>();
 
   static final int MAX_IN_SIZE = 5;  // Avoid using ranged calls for outlined code.
 
@@ -652,16 +683,42 @@
 
   // Collect outlining candidates with the methods that can use them.
   // TODO(sgjesse): This does not take several usages in the same method into account.
-  private class OutlineIdentifier extends OutlineSpotter {
+  private class OutlineMethodIdentifier extends OutlineSpotter {
 
-    OutlineIdentifier(DexEncodedMethod method, BasicBlock block) {
+    private final Map<Outline, List<DexEncodedMethod>> candidateMap;
+
+    OutlineMethodIdentifier(
+        DexEncodedMethod method,
+        BasicBlock block,
+        Map<Outline, List<DexEncodedMethod>> candidateMap) {
+      super(method, block);
+      this.candidateMap = candidateMap;
+    }
+
+    @Override
+    protected void handle(int start, int end, Outline outline) {
+      synchronized (candidateMap) {
+        candidateMap.computeIfAbsent(outline, this::addOutlineMethodList).add(method);
+      }
+    }
+
+    private List<DexEncodedMethod> addOutlineMethodList(Outline outline) {
+      List<DexEncodedMethod> result = new ArrayList<>();
+      candidateMethodLists.add(result);
+      return result;
+    }
+  }
+
+  private class OutlineSiteIdentifier extends OutlineSpotter {
+
+    OutlineSiteIdentifier(DexEncodedMethod method, BasicBlock block) {
       super(method, block);
     }
 
     @Override
     protected void handle(int start, int end, Outline outline) {
-      synchronized (candidates) {
-        candidates.computeIfAbsent(outline, k -> new ArrayList<>()).add(method);
+      synchronized (outlineSites) {
+        outlineSites.computeIfAbsent(outline, k -> new ArrayList<>()).add(method);
       }
     }
   }
@@ -685,9 +742,9 @@
 
     @Override
     protected void handle(int start, int end, Outline outline) {
-      if (candidates.containsKey(outline)) {
-        DexMethod m = generatedOutlines.get(outline);
-        assert m != null;
+      DexMethod m = generatedOutlines.get(outline);
+      if (m != null) {
+        assert removeMethodFromOutlineList(outline);
         List<Value> in = new ArrayList<>();
         Value returnValue = null;
         argumentsMapIndex = 0;
@@ -748,6 +805,14 @@
         }
       }
     }
+
+    /** When assertions are enabled, remove method from the outline's list. */
+    private boolean removeMethodFromOutlineList(Outline outline) {
+      synchronized (outlineSites) {
+        assert outlineSites.get(outline).remove(method);
+      }
+      return true;
+    }
   }
 
   public Outliner(AppInfoWithLiveness appInfo, InternalOptions options) {
@@ -756,26 +821,37 @@
     this.options = options;
   }
 
-  public void identifyCandidates(IRCode code, DexEncodedMethod method) {
+  public BiConsumer<IRCode, DexEncodedMethod> identifyCandidateMethods() {
+    // Since optimizations may change the map identity of Outline objects (e.g. by setting the
+    // out-value of invokes to null), this map must not be used except for identifying methods
+    // potentially relevant to outlining. OutlineMethodIdentifier will add method lists to
+    // candidateMethodLists whenever it adds an entry to candidateMap.
+    Map<Outline, List<DexEncodedMethod>> candidateMap = new HashMap<>();
+    assert candidateMethodLists.isEmpty();
+    return (code, method) -> {
+      assert !(method.getCode() instanceof OutlineCode);
+      for (BasicBlock block : code.blocks) {
+        new OutlineMethodIdentifier(method, block, candidateMap).process();
+      }
+    };
+  }
+
+  public void identifyOutlineSites(IRCode code, DexEncodedMethod method) {
     assert !(method.getCode() instanceof OutlineCode);
     for (BasicBlock block : code.blocks) {
-      new OutlineIdentifier(method, block).process();
+      new OutlineSiteIdentifier(method, block).process();
     }
   }
 
   public boolean selectMethodsForOutlining() {
     assert methodsSelectedForOutlining.size() == 0;
-    List<Outline> toRemove = new ArrayList<>();
-    for (Entry<Outline, List<DexEncodedMethod>> entry : candidates.entrySet()) {
-      if (entry.getValue().size() < options.outline.threshold) {
-        toRemove.add(entry.getKey());
-      } else {
-        methodsSelectedForOutlining.addAll(entry.getValue());
+    assert outlineSites.size() == 0;
+    for (List<DexEncodedMethod> outlineMethods : candidateMethodLists) {
+      if (outlineMethods.size() >= options.outline.threshold) {
+        methodsSelectedForOutlining.addAll(outlineMethods);
       }
     }
-    for (Outline outline : toRemove) {
-      candidates.remove(outline);
-    }
+    candidateMethodLists.clear();
     return methodsSelectedForOutlining.size() > 0;
   }
 
@@ -783,6 +859,79 @@
     return methodsSelectedForOutlining;
   }
 
+  public DexProgramClass buildOutlinerClass(DexType type) {
+    // Build the outlined methods.
+    // By now the candidates are the actual selected outlines. Name the generated methods in a
+    // consistent order, to provide deterministic output.
+    List<Outline> outlines = selectOutlines();
+    outlines.sort(Comparator.naturalOrder());
+    DexEncodedMethod[] direct = new DexEncodedMethod[outlines.size()];
+    int count = 0;
+    for (Outline outline : outlines) {
+      MethodAccessFlags methodAccess =
+          MethodAccessFlags.fromSharedAccessFlags(
+              Constants.ACC_PUBLIC | Constants.ACC_STATIC, false);
+      DexString methodName = dexItemFactory.createString(OutlineOptions.METHOD_PREFIX + count);
+      DexMethod method = outline.buildMethod(type, methodName);
+      List<DexEncodedMethod> sites = outlineSites.get(outline);
+      assert !sites.isEmpty();
+      direct[count] =
+          new DexEncodedMethod(
+              method,
+              methodAccess,
+              DexAnnotationSet.empty(),
+              ParameterAnnotationsList.empty(),
+              new OutlineCode(outline));
+      if (options.isGeneratingClassFiles()) {
+        direct[count].upgradeClassFileVersion(sites.get(0).getClassFileVersion());
+      }
+      generatedOutlines.put(outline, method);
+      count++;
+    }
+    // No need to sort the direct methods as they are generated in sorted order.
+
+    // Build the outliner class.
+    DexType superType = dexItemFactory.createType("Ljava/lang/Object;");
+    DexTypeList interfaces = DexTypeList.empty();
+    DexString sourceFile = dexItemFactory.createString("outline");
+    ClassAccessFlags accessFlags = ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC);
+    DexProgramClass clazz =
+        new DexProgramClass(
+            type,
+            null,
+            new SynthesizedOrigin("outlining", getClass()),
+            accessFlags,
+            superType,
+            interfaces,
+            sourceFile,
+            null,
+            Collections.emptyList(),
+            // TODO: Build dex annotations structure.
+            DexAnnotationSet.empty(),
+            DexEncodedField.EMPTY_ARRAY, // Static fields.
+            DexEncodedField.EMPTY_ARRAY, // Instance fields.
+            direct,
+            DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
+            options.itemFactory.getSkipNameValidationForTesting());
+    if (options.isGeneratingClassFiles()) {
+      // All program classes must have a class-file version. Use Java 6.
+      clazz.setClassFileVersion(Opcodes.V1_6);
+    }
+    return clazz;
+  }
+
+  private List<Outline> selectOutlines() {
+    assert outlineSites.size() > 0;
+    assert candidateMethodLists.isEmpty();
+    List<Outline> result = new ArrayList<>();
+    for (Entry<Outline, List<DexEncodedMethod>> entry : outlineSites.entrySet()) {
+      if (entry.getValue().size() >= options.outline.threshold) {
+        result.add(entry.getKey());
+      }
+    }
+    return result;
+  }
+
   public void applyOutliningCandidate(IRCode code, DexEncodedMethod method) {
     assert !(method.getCode() instanceof OutlineCode);
     ListIterator<BasicBlock> blocksIterator = code.blocks.listIterator();
@@ -794,6 +943,13 @@
     }
   }
 
+  public boolean checkAllOutlineSitesFoundAgain() {
+    for (Outline outline : generatedOutlines.keySet()) {
+      assert outlineSites.get(outline).isEmpty() : outlineSites.get(outline);
+    }
+    return true;
+  }
+
   static public void noProcessing(IRCode code, DexEncodedMethod method) {
     // No operation.
   }
@@ -1004,9 +1160,8 @@
     }
 
     @Override
-    public IRCode buildIR(DexEncodedMethod encodedMethod,
-        AppInfo appInfo, InternalOptions options, Origin origin)
-        throws ApiLevelException {
+    public IRCode buildIR(
+        DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
       OutlineSourceCode source = new OutlineSourceCode(outline);
       IRBuilder builder = new IRBuilder(encodedMethod, appInfo, source, options);
       return builder.build();
@@ -1037,81 +1192,4 @@
       return null;
     }
   }
-
-
-  public DexProgramClass buildOutlinerClass(DexType type) {
-    if (candidates.size() == 0) {
-      return null;
-    }
-
-    // Build the outlined methods.
-    DexEncodedMethod[] direct = new DexEncodedMethod[candidates.size()];
-    int count = 0;
-
-    // By now the candidates are the actual selected outlines. Name the generated methods in a
-    // consistent order, to provide deterministic output.
-    List<Outline> outlines = new ArrayList<>(candidates.keySet());
-    outlines.sort(Comparator.naturalOrder());
-    for (Outline outline : outlines) {
-      MethodAccessFlags methodAccess =
-          MethodAccessFlags.fromSharedAccessFlags(
-              Constants.ACC_PUBLIC | Constants.ACC_STATIC, false);
-      DexString methodName = dexItemFactory.createString(OutlineOptions.METHOD_PREFIX + count);
-      DexMethod method = outline.buildMethod(type, methodName);
-      direct[count] = new DexEncodedMethod(method, methodAccess, DexAnnotationSet.empty(),
-          ParameterAnnotationsList.empty(), new OutlineCode(outline));
-      generatedOutlines.put(outline, method);
-      count++;
-    }
-    // No need to sort the direct methods as they are generated in sorted order.
-
-    // Build the outliner class.
-    DexType superType = dexItemFactory.createType("Ljava/lang/Object;");
-    DexTypeList interfaces = DexTypeList.empty();
-    DexString sourceFile = dexItemFactory.createString("outline");
-    ClassAccessFlags accessFlags = ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC);
-    DexProgramClass clazz =
-        new DexProgramClass(
-            type,
-            null,
-            new SynthesizedOrigin("outlining", getClass()),
-            accessFlags,
-            superType,
-            interfaces,
-            sourceFile,
-            null,
-            Collections.emptyList(),
-            // TODO: Build dex annotations structure.
-            DexAnnotationSet.empty(),
-            DexEncodedField.EMPTY_ARRAY, // Static fields.
-            DexEncodedField.EMPTY_ARRAY, // Instance fields.
-            direct,
-            DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
-            options.itemFactory.getSkipNameValidationForTesting());
-    if (options.isGeneratingClassFiles()) {
-      // Don't set class file version below 50.0 (JDK release 1.6).
-      clazz.setClassFileVersion(Math.max(50, getClassFileVersion()));
-    }
-
-    return clazz;
-  }
-
-  private int getClassFileVersion() {
-    assert options.isGeneratingClassFiles();
-    int classFileVersion = -1;
-    Set<DexType> seen = Sets.newIdentityHashSet();
-    for (List<DexEncodedMethod> methods : candidates.values()) {
-      for (DexEncodedMethod method : methods) {
-        DexType holder = method.method.holder;
-        if (seen.add(holder)) {
-          DexProgramClass programClass = appInfo.definitionFor(holder).asProgramClass();
-          assert programClass != null : "Attempt to outline from library class";
-          assert programClass.originatesFromClassResource()
-              : "Attempt to outline from non-classfile input to classfile output";
-          classFileVersion = Math.max(classFileVersion, programClass.getClassFileVersion());
-        }
-      }
-    }
-    return classFileVersion;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
index 1284671..a586355 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.optimize;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
@@ -75,7 +74,7 @@
     intArrayType = appInfo.dexItemFactory.createType("[I");
   }
 
-  public AppInfoWithLiveness run() throws ApiLevelException {
+  public AppInfoWithLiveness run() {
     for (DexProgramClass clazz : appInfo.classes()) {
       processClasses(clazz);
     }
@@ -85,7 +84,7 @@
     return appInfo;
   }
 
-  private void processClasses(DexProgramClass clazz) throws ApiLevelException {
+  private void processClasses(DexProgramClass clazz) {
     // Switchmap classes are synthetic and have a class initializer.
     if (!clazz.accessFlags.isSynthetic() && !clazz.hasClassInitializer()) {
       return;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 7d13b1a..9ff44eb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.optimize.classinliner;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -40,7 +39,7 @@
   private static final Map<DexField, Integer> NO_MAPPING = new IdentityHashMap<>();
 
   public interface InlinerAction {
-    void inline(Map<InvokeMethodWithReceiver, InliningInfo> methods) throws ApiLevelException;
+    void inline(Map<InvokeMethodWithReceiver, InliningInfo> methods);
   }
 
   public ClassInliner(DexItemFactory factory) {
@@ -110,7 +109,7 @@
       DexEncodedMethod method,
       IRCode code,
       Predicate<DexEncodedMethod> isProcessedConcurrently,
-      InlinerAction inliner) throws ApiLevelException {
+      InlinerAction inliner) {
 
     // Collect all the new-instance instructions in the code before inlining.
     List<NewInstance> newInstances = Streams.stream(code.instructionIterator())
@@ -246,8 +245,8 @@
     }
   }
 
-  private void inlineAllCalls(InlinerAction inliner,
-      Map<InvokeMethodWithReceiver, InliningInfo> methodCalls) throws ApiLevelException {
+  private void inlineAllCalls(
+      InlinerAction inliner, Map<InvokeMethodWithReceiver, InliningInfo> methodCalls) {
     if (!methodCalls.isEmpty()) {
       inliner.inline(methodCalls);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 466117f..d1b56f2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.optimize.lambda;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
@@ -187,9 +186,13 @@
     }
   }
 
-  public final void applyLambdaClassMapping(DexApplication app,
-      IRConverter converter, OptimizationFeedback feedback, Builder<?> builder,
-      ExecutorService executorService) throws ExecutionException, ApiLevelException {
+  public final void applyLambdaClassMapping(
+      DexApplication app,
+      IRConverter converter,
+      OptimizationFeedback feedback,
+      Builder<?> builder,
+      ExecutorService executorService)
+      throws ExecutionException {
     if (lambdas.isEmpty()) {
       return;
     }
@@ -294,8 +297,7 @@
     }
   }
 
-  private void rewriteLambdaReferences(
-      IRConverter converter, OptimizationFeedback feedback) throws ApiLevelException {
+  private void rewriteLambdaReferences(IRConverter converter, OptimizationFeedback feedback) {
     List<DexEncodedMethod> methods =
         methodsToReprocess
             .stream()
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 2f055ce..df30c18 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -636,13 +636,21 @@
       LiveIntervals intervals = current.getLiveIntervals();
       assert intervals.getRegisterLimit() == Constants.U16BIT_MAX;
       boolean canUseArgumentRegister = true;
+      boolean couldUseArgumentRegister = true;
       for (LiveIntervals child : intervals.getSplitChildren()) {
-        if (child.getRegisterLimit() < highestUsedRegister()) {
-          canUseArgumentRegister = false;
-          break;
+        int registerConstraint = child.getRegisterLimit();
+        if (registerConstraint < Constants.U16BIT_MAX) {
+          couldUseArgumentRegister = false;
+
+          if (registerConstraint < highestUsedRegister()) {
+            canUseArgumentRegister = false;
+            break;
+          }
         }
       }
-      if (canUseArgumentRegister) {
+      if (canUseArgumentRegister && !couldUseArgumentRegister) {
+        // Only return true if there is a constrained use where it turns out that we can use the
+        // original argument register. This way we will not unnecessarily redo move insertion.
         argumentRegisterUnsplit = true;
         for (LiveIntervals child : intervals.getSplitChildren()) {
           child.clearRegisterAssignment();
@@ -805,8 +813,8 @@
       if (overlappingMoveExceptionIntervals) {
         for (LiveIntervals intervals : moveExceptionIntervals) {
           if (intervals.getUses().size() > 1) {
-            LiveIntervalsUse secondUse = intervals.getUses().stream().skip(1).findFirst().get();
-            LiveIntervals split = intervals.splitBefore(secondUse.getPosition());
+            LiveIntervals split =
+                intervals.splitBefore(intervals.getFirstUse() + INSTRUCTION_NUMBER_DELTA);
             unhandled.add(split);
           }
         }
@@ -1346,6 +1354,12 @@
     if (!hasDedicatedMoveExceptionRegister()) {
       return false;
     }
+    // If there are that many move exception intervals we don't spent the time
+    // going through them all. In that case it is unlikely that we can reuse the move exception
+    // register in any case.
+    if (moveExceptionIntervals.size() > 1000) {
+      return true;
+    }
     for (LiveIntervals moveExceptionInterval : moveExceptionIntervals) {
       if (intervals.anySplitOverlaps(moveExceptionInterval)) {
         return true;
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
index 9259485..0ba49d3 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
@@ -21,15 +21,27 @@
   private final DexType targetReceiver;
   private final DexMethod target;
   private final Invoke.Type invokeType;
+  private final boolean castResult;
 
   public ForwardMethodSourceCode(DexType receiver, DexProto proto,
       DexType targetReceiver, DexMethod target, Invoke.Type invokeType) {
+    this(receiver, proto, targetReceiver, target, invokeType, false);
+  }
+
+  public ForwardMethodSourceCode(
+      DexType receiver,
+      DexProto proto,
+      DexType targetReceiver,
+      DexMethod target,
+      Invoke.Type invokeType,
+      boolean castResult) {
     super(receiver, proto);
     assert (targetReceiver == null) == (invokeType == Invoke.Type.STATIC);
 
     this.target = target;
     this.targetReceiver = targetReceiver;
     this.invokeType = invokeType;
+    this.castResult = castResult;
     assert checkSignatures();
 
     switch (invokeType) {
@@ -67,7 +79,7 @@
       assert (source.isClassType() && target.isClassType()) || source == target;
     }
 
-    assert this.proto.returnType == target.proto.returnType;
+    assert this.proto.returnType == target.proto.returnType || castResult;
     return true;
   }
 
@@ -99,6 +111,9 @@
       ValueType valueType = ValueType.fromDexType(proto.returnType);
       int tempValue = nextRegister(valueType);
       add(builder -> builder.addMoveResult(tempValue));
+      if (this.proto.returnType != target.proto.returnType) {
+        add(builder -> builder.addCheckCast(tempValue, this.proto.returnType));
+      }
       add(builder -> builder.addReturn(valueType, tempValue));
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
index f8134f4..a5807c3 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.ir.synthetic;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.Code;
@@ -40,9 +39,7 @@
 
   @Override
   public final IRCode buildIR(
-      DexEncodedMethod encodedMethod, AppInfo appInfo,
-      InternalOptions options, Origin origin)
-      throws ApiLevelException {
+      DexEncodedMethod encodedMethod, AppInfo appInfo, InternalOptions options, Origin origin) {
     return new IRBuilder(encodedMethod, appInfo, sourceCode, options).build();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
index 03206b5..80a439a 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -6,7 +6,6 @@
 
 import static com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo.NO_THROW;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexProto;
@@ -18,9 +17,9 @@
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.ir.conversion.SourceCode;
-import com.android.tools.r8.utils.ThrowingConsumer;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 public abstract class SyntheticSourceCode implements SourceCode {
@@ -43,7 +42,7 @@
   private Value[] paramValues;
 
   // Instruction constructors
-  private List<ThrowingConsumer<IRBuilder, ApiLevelException>> constructors = new ArrayList<>();
+  private List<Consumer<IRBuilder>> constructors = new ArrayList<>();
   private List<Predicate<IRBuilder>> traceEvents = new ArrayList<>();
 
   protected SyntheticSourceCode(DexType receiver, DexProto proto) {
@@ -63,12 +62,11 @@
     }
   }
 
-  protected final void add(ThrowingConsumer<IRBuilder, ApiLevelException> constructor) {
+  protected final void add(Consumer<IRBuilder> constructor) {
     add(constructor, doesNotEndBlock);
   }
 
-  protected final void add(
-      ThrowingConsumer<IRBuilder, ApiLevelException> constructor, Predicate<IRBuilder> traceEvent) {
+  protected final void add(Consumer<IRBuilder> constructor, Predicate<IRBuilder> traceEvent) {
     constructors.add(constructor);
     traceEvents.add(traceEvent);
   }
@@ -189,8 +187,7 @@
 
   @Override
   public final void buildInstruction(
-      IRBuilder builder, int instructionIndex, boolean firstBlockInstruction)
-      throws ApiLevelException {
+      IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
     constructors.get(instructionIndex).accept(builder);
   }
 
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 2970fb4..7a25e1a 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -108,7 +108,7 @@
   private void writeClass(DexProgramClass clazz, ClassFileConsumer consumer) throws IOException {
     ClassWriter writer = new ClassWriter(0);
     writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, null);
-    int version = clazz.getClassFileVersion();
+    int version = getClassFileVersion(clazz);
     int access = clazz.accessFlags.getAsCfAccessFlags();
     String desc = namingLens.lookupDescriptor(clazz.type).toString();
     String name = namingLens.lookupInternalName(clazz.type);
@@ -161,6 +161,17 @@
         options.reporter, handler -> consumer.accept(result, desc, handler));
   }
 
+  private int getClassFileVersion(DexProgramClass clazz) {
+    int version = clazz.getClassFileVersion();
+    for (DexEncodedMethod method : clazz.directMethods()) {
+      version = Math.max(version, method.getClassFileVersion());
+    }
+    for (DexEncodedMethod method : clazz.virtualMethods()) {
+      version = Math.max(version, method.getClassFileVersion());
+    }
+    return version;
+  }
+
   private DexValue getSystemAnnotationValue(DexAnnotationSet annotations, DexType type) {
     DexAnnotation annotation = annotations.getFirstMatching(type);
     if (annotation == null) {
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 4f49fbe..56e381b 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -34,6 +34,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 class ClassNameMinifier {
 
@@ -50,6 +51,7 @@
   private final ImmutableList<String> packageDictionary;
   private final ImmutableList<String> classDictionary;
   private final boolean keepInnerClassStructure;
+  private final Set<DexType> keepPackageName;
 
   private final Namespace topLevelState;
 
@@ -69,6 +71,12 @@
     this.packageDictionary = options.proguardConfiguration.getPackageObfuscationDictionary();
     this.classDictionary = options.proguardConfiguration.getClassObfuscationDictionary();
     this.keepInnerClassStructure = options.proguardConfiguration.getKeepAttributes().signature;
+    this.keepPackageName =
+        rootSet.keepPackageName.stream()
+            .filter(DexClass.class::isInstance)
+            .map(DexClass.class::cast)
+            .map(DexClass::getType)
+            .collect(Collectors.toSet());
 
     // Initialize top-level naming state.
     topLevelState = new Namespace(
@@ -92,7 +100,7 @@
     timing.begin("rename-classes");
     for (DexClass clazz : classes) {
       if (!renaming.containsKey(clazz.type)) {
-        DexString renamed = computeName(clazz);
+        DexString renamed = computeName(clazz.type);
         renaming.put(clazz.type, renamed);
       }
     }
@@ -231,27 +239,27 @@
     return null;
   }
 
-  private DexString computeName(DexClass clazz) {
+  private DexString computeName(DexType type) {
     Namespace state = null;
     if (keepInnerClassStructure) {
       // When keeping the nesting structure of inner classes, we have to insert the name
       // of the outer class for the $ prefix.
-      DexType outerClass = getOutClassForType(clazz.type);
+      DexType outerClass = getOutClassForType(type);
       if (outerClass != null) {
         state = getStateForOuterClass(outerClass);
       }
     }
     if (state == null) {
-      state = getStateForClass(clazz);
+      state = getStateForClass(type);
     }
     return state.nextTypeName();
   }
 
-  private Namespace getStateForClass(DexClass clazz) {
-    String packageName = getPackageBinaryNameFromJavaType(clazz.type.getPackageDescriptor());
+  private Namespace getStateForClass(DexType type) {
+    String packageName = getPackageBinaryNameFromJavaType(type.getPackageDescriptor());
     // Check whether the given class should be kept.
     // or check whether the given class belongs to a package that is kept for another class.
-    if (rootSet.keepPackageName.contains(clazz)
+    if (keepPackageName.contains(type)
         || noObfuscationPrefixes.contains(packageName)) {
       return states.computeIfAbsent(packageName, Namespace::new);
     }
@@ -307,13 +315,12 @@
       DexString renamed = renaming.get(outer);
       if (renamed == null) {
         // The outer class has not been renamed yet, so rename the outer class first.
-        DexClass outerClass = appInfo.definitionFor(outer);
-        if (outerClass == null) {
-          renamed = outer.descriptor;
-        } else {
-          renamed = computeName(outerClass);
-          renaming.put(outer, renamed);
-        }
+        // Note that here we proceed unconditionally---w/o regards to the existence of the outer
+        // class: it could be the case that the outer class is not renamed because it's shrunk.
+        // Even though that's the case, we can _implicitly_ assign a new name to the outer class
+        // and then use that renamed name as a base prefix for the current inner class.
+        renamed = computeName(outer);
+        renaming.put(outer, renamed);
       }
       String binaryName = getClassBinaryNameFromDescriptor(renamed.toString());
       state = new Namespace(binaryName, "$");
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 2c85270..c766c37 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -299,5 +299,16 @@
       }
       writer.append(')');
     }
+
+    public String toDescriptor() {
+      StringBuilder sb = new StringBuilder();
+      sb.append('(');
+      for (String parameterType : parameters) {
+        sb.append(javaTypeToDescriptor(parameterType));
+      }
+      sb.append(')');
+      sb.append(javaTypeToDescriptor(type));
+      return sb.toString();
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
index 9a5baaf..0cc0d53 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.naming.signature;
 
 import java.lang.reflect.GenericSignatureFormatError;
+import java.nio.CharBuffer;
 
 /**
  * Implements a parser for the generics signature attribute as defined by JVMS 7 $ 4.3.4.
@@ -177,7 +178,7 @@
         updateTypeVariableSignature();
         break;
       default:
-        throw new GenericSignatureFormatError();
+        parseError();
     }
   }
 
@@ -340,7 +341,7 @@
         eof = true;
       }
     } else {
-      throw new GenericSignatureFormatError();
+      parseError("Unexpected end of signature");
     }
   }
 
@@ -348,7 +349,7 @@
     if (symbol == c) {
       scanSymbol();
     } else {
-      throw new GenericSignatureFormatError();
+      parseError("Expected " + c);
     }
   }
 
@@ -395,10 +396,20 @@
         // Ident starts with incorrect char.
         symbol = 0;
         eof = true;
-        throw new GenericSignatureFormatError();
+        parseError();
       }
     } else {
-      throw new GenericSignatureFormatError();
+      parseError("Unexpected end of signature");
     }
   }
+
+  private void parseError() {
+    parseError("Unexpected");
+  }
+
+  private void parseError(String message) {
+    String arrow = CharBuffer.allocate(pos).toString().replace('\0', ' ') + '^';
+    throw new GenericSignatureFormatError(
+        message + " at position " + (pos + 1) + "\n" + String.valueOf(buffer) + "\n" + arrow);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/position/TextRange.java b/src/main/java/com/android/tools/r8/position/TextRange.java
index d7c0f43..5f35aad 100644
--- a/src/main/java/com/android/tools/r8/position/TextRange.java
+++ b/src/main/java/com/android/tools/r8/position/TextRange.java
@@ -45,6 +45,11 @@
   }
 
   @Override
+  public String toString() {
+      return "Text range from: '" + getStart() + "', to: '" + getEnd() + "'";
+  }
+
+  @Override
   public String getDescription() {
     return start.getDescription();
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
index e4e2ed7..610bfd3 100644
--- a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
@@ -52,7 +52,7 @@
     List<DexEncodedMethod> methods = null;
     for (int i = 0; i < virtualMethods.length; i++) {
       DexEncodedMethod method = virtualMethods[i];
-      if (scope.addMethod(method.method) || !method.accessFlags.isAbstract()) {
+      if (scope.addMethodIfMoreVisible(method) || !method.accessFlags.isAbstract()) {
         if (methods != null) {
           methods.add(method);
         }
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 7e483a7..085d9b3 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -7,7 +7,6 @@
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
 import static com.android.tools.r8.shaking.ProguardConfigurationUtils.buildIdentifierNameStringRule;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.errors.Unreachable;
@@ -461,10 +460,11 @@
         DexType baseType = type.toBaseType(appInfo.dexItemFactory);
         if (baseType.isClassType()) {
           DexClass baseClass = appInfo.definitionFor(baseType);
-          if (baseClass != null && !baseClass.isLibraryClass()) {
+          if (baseClass != null && baseClass.isProgramClass()
+              && baseClass.hasDefaultInitializer()) {
             markClassAsInstantiatedWithCompatRule(baseClass);
           } else {
-            // This handles reporting of missing classes.
+            // This also handles reporting of missing classes.
             markTypeAsLive(baseType);
           }
           return true;
@@ -740,7 +740,6 @@
       transitionNonAbstractMethodsToLiveAndShadow(
           reachableMethods.getItems(), instantiatedType, seen.newNestedScope());
     }
-    seen = seen.newNestedScope();
     for (DexType subInterface : clazz.interfaces.values) {
       transitionDefaultMethodsForInstantiatedClass(subInterface, instantiatedType, seen);
     }
@@ -749,7 +748,7 @@
   private void transitionNonAbstractMethodsToLiveAndShadow(Iterable<DexEncodedMethod> reachable,
       DexType instantiatedType, ScopedDexMethodSet seen) {
     for (DexEncodedMethod encodedMethod : reachable) {
-      if (seen.addMethod(encodedMethod.method)) {
+      if (seen.addMethod(encodedMethod)) {
         // Abstract methods do shadow implementations but they cannot be live, as they have no
         // code.
         if (!encodedMethod.accessFlags.isAbstract()) {
@@ -1349,13 +1348,8 @@
   }
 
   private void handleProguardReflectiveBehavior(DexEncodedMethod method) {
-    try {
-      IRCode code = method.buildIR(appInfo, options, appInfo.originFor(method.method.holder));
-      code.instructionIterator().forEachRemaining(this::handleProguardReflectiveBehavior);
-    } catch (ApiLevelException e) {
-      // Ignore this exception here. It will be hit again further in the pipeline when
-      // generating code.
-    }
+    IRCode code = method.buildIR(appInfo, options, appInfo.originFor(method.method.holder));
+    code.instructionIterator().forEachRemaining(this::handleProguardReflectiveBehavior);
   }
 
   private void handleProguardReflectiveBehavior(Instruction instruction) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index dd82dfd..d4d4bcb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -305,8 +305,8 @@
         // disableOptimization();
       }
 
-      if ((keepAttributePatterns.isEmpty()
-          && (rulesWasEmpty || (forceProguardCompatibility && !isObfuscating())))
+      if ((keepAttributePatterns.isEmpty() && rulesWasEmpty)
+          || (forceProguardCompatibility && !isObfuscating())
           || !isShrinking()) {
         keepAttributePatterns.addAll(ProguardKeepAttributes.KEEP_ALL);
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 4a25e42..ed9cbdb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -47,6 +47,7 @@
   private final DexItemFactory dexItemFactory;
 
   private final Reporter reporter;
+  private final boolean failOnPartiallyImplementedOptions;
 
   private static final List<String> IGNORED_SINGLE_ARG_OPTIONS = ImmutableList.of(
       "protomapping",
@@ -96,10 +97,16 @@
 
   public ProguardConfigurationParser(
       DexItemFactory dexItemFactory, Reporter reporter) {
+    this(dexItemFactory, reporter, true);
+  }
+
+  public ProguardConfigurationParser(
+      DexItemFactory dexItemFactory, Reporter reporter, boolean failOnPartiallyImplementedOptions) {
     this.dexItemFactory = dexItemFactory;
     configurationBuilder = ProguardConfiguration.builder(dexItemFactory, reporter);
 
     this.reporter = reporter;
+    this.failOnPartiallyImplementedOptions = failOnPartiallyImplementedOptions;
   }
 
   public ProguardConfiguration.Builder getConfigurationBuilder() {
@@ -219,6 +226,10 @@
         ProguardCheckDiscardRule rule = parseCheckDiscardRule();
         configurationBuilder.addRule(rule);
       } else if (acceptString("keepdirectories")) {
+        // TODO(74279367): Report an error until it's fully supported.
+        if (failOnPartiallyImplementedOptions) {
+          failPartiallyImplementedOption("-keepdirectories", optionStart);
+        }
         parsePathFilter(configurationBuilder::addKeepDirectories);
       } else if (acceptString("keep")) {
         ProguardKeepRule rule = parseKeepRule();
@@ -339,11 +350,17 @@
       } else if (acceptString("adaptclassstrings")) {
         parseClassFilter(configurationBuilder::addAdaptClassStringsPattern);
       } else if (acceptString("adaptresourcefilenames")) {
-        // TODO(b/76377381): should be report an error until it's fully supported.
+        // TODO(76377381): Report an error until it's fully supported.
+        if (failOnPartiallyImplementedOptions) {
+          failPartiallyImplementedOption("-adaptresourcefilenames", optionStart);
+        }
         parsePathFilter(configurationBuilder::addAdaptResourceFilenames);
       } else if (acceptString("adaptresourcefilecontents")) {
+        // TODO(36847655): Report an error until it's fully supported.
+        if (failOnPartiallyImplementedOptions) {
+          failPartiallyImplementedOption("-adaptresourcefilecontents", optionStart);
+        }
         parsePathFilter(configurationBuilder::addAdaptResourceFilecontents);
-        // TODO(b/37139570): should be report an error until it's fully supported.
       } else if (acceptString("identifiernamestring")) {
         configurationBuilder.addRule(parseIdentifierNameStringRule());
       } else if (acceptString("if")) {
@@ -1473,6 +1490,11 @@
           "Option -" + optionName + " overrides -" + victim, origin, getPosition(start)));
     }
 
+    private void failPartiallyImplementedOption(String optionName, TextPosition start) {
+      throw reporter.fatalError(new StringDiagnostic(
+          "Option " + optionName + " currently not supported", origin, getPosition(start)));
+    }
+
     private Position getPosition(TextPosition start) {
       if (start.getOffset() == position) {
         return start;
diff --git a/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java b/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
index 39f07ee..3587cf4 100644
--- a/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
+++ b/src/main/java/com/android/tools/r8/shaking/ScopedDexMethodSet.java
@@ -3,19 +3,20 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.google.common.base.Equivalence;
 import com.google.common.base.Equivalence.Wrapper;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
 
 class ScopedDexMethodSet {
 
   private static final Equivalence<DexMethod> METHOD_EQUIVALENCE = MethodSignatureEquivalence.get();
 
   private final ScopedDexMethodSet parent;
-  private final Set<Wrapper<DexMethod>> items = new HashSet<>();
+  private final Map<Wrapper<DexMethod>, DexEncodedMethod> items = new HashMap<>();
 
   public ScopedDexMethodSet() {
     this(null);
@@ -29,14 +30,32 @@
     return new ScopedDexMethodSet(this);
   }
 
-  private boolean contains(Wrapper<DexMethod> item) {
-    return items.contains(item)
-        || ((parent != null) && parent.contains(item));
+  private DexEncodedMethod lookup(Wrapper<DexMethod> item) {
+    DexEncodedMethod ownMethod = items.get(item);
+    return ownMethod != null ? ownMethod : (parent != null ? parent.lookup(item) : null);
   }
 
-  public boolean addMethod(DexMethod method) {
-    Wrapper<DexMethod> wrapped = METHOD_EQUIVALENCE.wrap(method);
-    return !contains(wrapped) && items.add(wrapped);
+  private boolean contains(Wrapper<DexMethod> item) {
+    return lookup(item) != null;
+  }
+
+  public boolean addMethod(DexEncodedMethod method) {
+    Wrapper<DexMethod> wrapped = METHOD_EQUIVALENCE.wrap(method.method);
+    if (contains(wrapped)) {
+      return false;
+    }
+    items.put(wrapped, method);
+    return true;
+  }
+
+  public boolean addMethodIfMoreVisible(DexEncodedMethod method) {
+    Wrapper<DexMethod> wrapped = METHOD_EQUIVALENCE.wrap(method.method);
+    DexEncodedMethod existing = lookup(wrapped);
+    if (existing == null || method.accessFlags.isMoreVisibleThan(existing.accessFlags)) {
+      items.put(wrapped, method);
+      return true;
+    }
+    return false;
   }
 
   public ScopedDexMethodSet getParent() {
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
index cd21d17..1a64ab0 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
@@ -35,34 +35,40 @@
   @Override
   public synchronized void open() {
     assert !closed;
-    openCount ++;
+    openCount++;
   }
 
   @Override
-  public synchronized void close() throws IOException {
+  public synchronized void close(DiagnosticsHandler handler)  {
     assert !closed;
     openCount--;
     if (openCount == 0) {
       closed = true;
-      if (stream != null) {
-        stream.close();
+      try {
+        getStreamRaw().close();
         stream = null;
+      } catch (IOException e) {
+        handler.error(new ExceptionDiagnostic(e, origin));
       }
     }
   }
 
+  private ZipOutputStream getStreamRaw() throws IOException {
+    if (stream != null) {
+      return stream;
+    }
+    stream = new ZipOutputStream(Files.newOutputStream(
+        archive, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
+    return stream;
+  }
+
   /** Get or open the zip output stream. */
   private synchronized ZipOutputStream getStream(DiagnosticsHandler handler) {
     assert !closed;
-    if (stream == null) {
-      try {
-        stream =
-            new ZipOutputStream(
-                Files.newOutputStream(
-                    archive, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
-      } catch (IOException e) {
-        handler.error(new ExceptionDiagnostic(e, origin));
-      }
+    try {
+      getStreamRaw();
+    } catch (IOException e) {
+      handler.error(new ExceptionDiagnostic(e, origin));
     }
     return stream;
   }
diff --git a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
index 2dc4a2d..2058d39 100644
--- a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
@@ -30,7 +30,7 @@
   }
 
   @Override
-  public void close() {
+  public void close(DiagnosticsHandler handler) {
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index 9c7f525..fe8adfa 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.ResourceException;
@@ -15,7 +14,6 @@
 import java.nio.file.FileSystemException;
 import java.nio.file.Paths;
 import java.util.function.Consumer;
-import java.util.function.Function;
 
 public abstract class ExceptionUtils {
 
@@ -37,31 +35,26 @@
   }
 
   public interface CompileAction {
-    void run() throws IOException, CompilationException, CompilationError, ResourceException;
+    void run() throws IOException, CompilationError, ResourceException;
   }
 
   public static void withD8CompilationHandler(Reporter reporter, CompileAction action)
       throws CompilationFailedException {
-    withCompilationHandler(reporter, action, CompilationException::getMessageForD8);
+    withCompilationHandler(reporter, action);
   }
 
   public static void withR8CompilationHandler(Reporter reporter, CompileAction action)
       throws CompilationFailedException {
-    withCompilationHandler(reporter, action, CompilationException::getMessageForR8);
+    withCompilationHandler(reporter, action);
   }
 
-  public static void withCompilationHandler(
-      Reporter reporter,
-      CompileAction action,
-      Function<CompilationException, String> compilerMessage)
+  public static void withCompilationHandler(Reporter reporter, CompileAction action)
       throws CompilationFailedException {
     try {
       try {
         action.run();
       } catch (IOException e) {
         throw reporter.fatalError(new ExceptionDiagnostic(e, extractIOExceptionOrigin(e)));
-      } catch (CompilationException e) {
-        throw reporter.fatalError(new StringDiagnostic(compilerMessage.apply(e)), e);
       } catch (CompilationError e) {
         throw reporter.fatalError(e);
       } catch (ResourceException e) {
diff --git a/src/main/java/com/android/tools/r8/utils/FlagFile.java b/src/main/java/com/android/tools/r8/utils/FlagFile.java
new file mode 100644
index 0000000..4791217
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/FlagFile.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2018, 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.utils;
+
+import com.android.tools.r8.origin.Origin;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlagFile {
+
+  private static class FlagFileOrigin extends Origin {
+    private final Path path;
+
+    protected FlagFileOrigin(Path path) {
+      super(Origin.root());
+      this.path = path;
+    }
+
+    @Override
+    public String part() {
+      return "flag file argument: '@" + path + "'";
+    }
+  }
+
+  public static String[] expandFlagFiles(String[] args, Reporter reporter) {
+    List<String> flags = new ArrayList<>(args.length);
+    for (String arg : args) {
+      if (arg.startsWith("@")) {
+        Path flagFilePath = Paths.get(arg.substring(1));
+        try {
+          flags.addAll(Files.readAllLines(flagFilePath));
+        } catch (IOException e) {
+          Origin origin = new FlagFileOrigin(flagFilePath);
+          reporter.error(new ExceptionDiagnostic(e, origin));
+        }
+      } else {
+        flags.add(arg);
+      }
+    }
+    return flags.toArray(new String[flags.size()]);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/OutputBuilder.java b/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
index 27d07c4..9237485 100644
--- a/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
@@ -7,14 +7,15 @@
 import com.android.tools.r8.DataEntryResource;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.origin.Origin;
-import java.io.Closeable;
 import java.nio.file.Path;
 
-public interface OutputBuilder extends Closeable {
+public interface OutputBuilder {
   char NAME_SEPARATOR = '/';
 
   void open();
 
+  void close(DiagnosticsHandler handler);
+
   void addDirectory(String name, DiagnosticsHandler handler);
 
   void addFile(String name, DataEntryResource content, DiagnosticsHandler handler);
diff --git a/src/main/java/com/android/tools/r8/utils/Reporter.java b/src/main/java/com/android/tools/r8/utils/Reporter.java
index ebb7ce6..b09f164 100644
--- a/src/main/java/com/android/tools/r8/utils/Reporter.java
+++ b/src/main/java/com/android/tools/r8/utils/Reporter.java
@@ -3,10 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
+import com.android.tools.r8.BaseCompilerCommand;
 import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8Command;
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.R8Command;
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.MainDexOverflow;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
@@ -16,12 +20,18 @@
 public class Reporter implements DiagnosticsHandler {
 
   private final DiagnosticsHandler clientHandler;
+  private final BaseCompilerCommand command;
   private int errorCount = 0;
   private Diagnostic lastError;
   private final Collection<Throwable> suppressedExceptions = new ArrayList<>();
 
   public Reporter(DiagnosticsHandler clientHandler) {
+    this(clientHandler, null);
+  }
+
+  public Reporter(DiagnosticsHandler clientHandler, BaseCompilerCommand command) {
     this.clientHandler = clientHandler;
+    this.command = command;
   }
 
   @Override
@@ -75,6 +85,19 @@
   }
 
   /**
+   * @throws AbortException always.
+   */
+  public RuntimeException fatalError(MainDexOverflow e) {
+    if (command instanceof R8Command) {
+      return fatalError(new StringDiagnostic(e.getMessageForR8()));
+    } else if (command instanceof D8Command) {
+      return fatalError(new StringDiagnostic(e.getMessageForD8()));
+    } else {
+      return fatalError(new StringDiagnostic(e.getMessage()));
+    }
+  }
+
+  /**
    * @throws AbortException if any error was reported.
    */
   public void failIfPendingErrors() {
diff --git a/src/test/examples/abstractmethodremoval/AbstractMethodRemoval.java b/src/test/examples/abstractmethodremoval/AbstractMethodRemoval.java
new file mode 100644
index 0000000..1da772d
--- /dev/null
+++ b/src/test/examples/abstractmethodremoval/AbstractMethodRemoval.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2018, 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 abstractmethodremoval;
+
+import abstractmethodremoval.a.PackageBase;
+import abstractmethodremoval.a.Public;
+import abstractmethodremoval.b.Impl1;
+import abstractmethodremoval.b.Impl2;
+
+public class AbstractMethodRemoval {
+
+  private static int dummy;
+
+  public static void main(String[] args) {
+    dummy = args.length;
+    invokeFoo(new Impl1());
+    invokeFoo(new Impl2());
+    invokeFoo(new Impl2());
+    PackageBase.invokeFoo(new Impl1());
+    PackageBase.invokeFoo(new Impl2());
+    PackageBase.invokeFoo(new Impl2());
+  }
+
+  private static void invokeFoo(Public aPublic) {
+    // Enough instructions to avoid inlining.
+    aPublic.foo(dummy() + dummy() + dummy() + dummy() + dummy() + dummy() + dummy() + dummy());
+  }
+
+  private static int dummy() {
+    // Enough instructions to avoid inlining.
+    return dummy + dummy + dummy + dummy + dummy + dummy + dummy + dummy + dummy + dummy + dummy;
+  }
+}
diff --git a/src/test/examples/abstractmethodremoval/a/PackageBase.java b/src/test/examples/abstractmethodremoval/a/PackageBase.java
new file mode 100644
index 0000000..1075b57
--- /dev/null
+++ b/src/test/examples/abstractmethodremoval/a/PackageBase.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2018, 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 abstractmethodremoval.a;
+
+public abstract class PackageBase {
+  abstract void foo(int i);
+
+  public static void invokeFoo(PackageBase o) {
+    o.foo(0);
+  }
+}
diff --git a/src/test/examples/abstractmethodremoval/a/Public.java b/src/test/examples/abstractmethodremoval/a/Public.java
new file mode 100644
index 0000000..492ab87
--- /dev/null
+++ b/src/test/examples/abstractmethodremoval/a/Public.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2018, 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 abstractmethodremoval.a;
+
+public abstract class Public extends PackageBase {
+  @Override
+  public abstract void foo(int i);
+}
diff --git a/src/test/examples/abstractmethodremoval/b/Impl1.java b/src/test/examples/abstractmethodremoval/b/Impl1.java
new file mode 100644
index 0000000..bfdf4d1
--- /dev/null
+++ b/src/test/examples/abstractmethodremoval/b/Impl1.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, 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 abstractmethodremoval.b;
+
+import abstractmethodremoval.a.Public;
+
+public class Impl1 extends Public {
+  @Override
+  public void foo(int i) {
+    System.out.println("Impl1.foo(" + i + ")");
+  }
+}
diff --git a/src/test/examples/abstractmethodremoval/b/Impl2.java b/src/test/examples/abstractmethodremoval/b/Impl2.java
new file mode 100644
index 0000000..a92e2a1
--- /dev/null
+++ b/src/test/examples/abstractmethodremoval/b/Impl2.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, 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 abstractmethodremoval.b;
+
+import abstractmethodremoval.a.Public;
+
+public class Impl2 extends Public {
+  @Override
+  public void foo(int i) {
+    System.out.println("Impl2.foo(" + i + ")");
+  }
+}
diff --git a/src/test/examples/abstractmethodremoval/keep-rules.txt b/src/test/examples/abstractmethodremoval/keep-rules.txt
new file mode 100644
index 0000000..ec37858
--- /dev/null
+++ b/src/test/examples/abstractmethodremoval/keep-rules.txt
@@ -0,0 +1 @@
+-keep public class abstractmethodremoval.AbstractMethodRemoval { public static void main(...); }
diff --git a/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java b/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java
new file mode 100644
index 0000000..7a4950c
--- /dev/null
+++ b/src/test/examples/catchhandleroverlap/CatchHandlerOverlap.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2018, 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 catchhandleroverlap;
+
+public class CatchHandlerOverlap {
+  private static void f() throws Exception {
+    throw new Exception("f");
+  }
+
+  private static void g() throws Exception {
+    throw new Exception("g");
+  }
+
+  private static void h(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9,
+      int i10, int i11, int i12, int i13, int i14, int i15, int i16, int i17) {
+    System.out.println(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 +
+        i12 + i13 + i14 + i15 + i16 + i17);
+    try {
+      f();
+    } catch (Exception e0) {
+      try {
+        g();
+      } catch (Exception e1) {
+        System.out.println(e0.getMessage() + " " + e1.getMessage());
+      }
+    }
+  }
+
+  public static void main(String[] args) {
+    h(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/AsmTestBase.java b/src/test/java/com/android/tools/r8/AsmTestBase.java
index db26fe6..9cc2b48 100644
--- a/src/test/java/com/android/tools/r8/AsmTestBase.java
+++ b/src/test/java/com/android/tools/r8/AsmTestBase.java
@@ -54,8 +54,8 @@
   }
 
   private void ensureSameOutput(String main, AndroidApp app, byte[]... classes)
-      throws IOException, CompilationException, ExecutionException, CompilationFailedException,
-      ProguardRuleParserException {
+      throws IOException, ExecutionException, CompilationFailedException,
+          ProguardRuleParserException {
     ProcessResult javaResult = runOnJava(main, classes);
     ProcessResult d8Result = runOnArtRaw(compileWithD8(app), main);
     ProcessResult r8Result = runOnArtRaw(compileWithR8(app), main);
@@ -88,8 +88,8 @@
   }
 
   protected void ensureSameOutputAfterMerging(String main, byte[]... classes)
-      throws IOException, CompilationException, ExecutionException,
-      CompilationFailedException, ProguardRuleParserException {
+      throws IOException, ExecutionException, CompilationFailedException,
+          ProguardRuleParserException {
     AndroidApp app = buildAndroidApp(classes);
     // Compile to dex files with D8.
     AndroidApp dexApp = compileWithD8(app);
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index dc16cac..1b846a5 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -6,12 +6,16 @@
 import static com.android.tools.r8.R8CommandTest.getOutputPath;
 import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
 import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static java.nio.file.StandardOpenOption.CREATE_NEW;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
 import com.android.sdklib.AndroidVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
@@ -20,6 +24,7 @@
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.List;
@@ -87,6 +92,39 @@
   }
 
   @Test
+  public void flagsFile() throws Throwable {
+    Path working = temp.getRoot().toPath();
+    Path flagsFile = working.resolve("flags.txt");
+    Path input = Paths.get(EXAMPLES_BUILD_DIR + "/arithmetic.jar").toAbsolutePath();
+    Path output = working.resolve("output.zip");
+    FileUtils.writeTextFile(
+        flagsFile,
+        "--output",
+        "output.zip",
+        "--min-api",
+        "24",
+        input.toString());
+    assertEquals(0, ToolHelper.forkD8(working, "@flags.txt").exitCode);
+    assertTrue(Files.exists(output));
+    Marker marker = ExtractMarker.extractMarkerFromDexFile(output);
+    assertEquals(24, marker.getMinApi().intValue());
+    assertEquals(Tool.D8, marker.getTool());
+  }
+
+  @Test(expected=CompilationFailedException.class)
+  public void nonExistingFlagsFile() throws Throwable {
+    Path working = temp.getRoot().toPath();
+    Path flags = working.resolve("flags.txt").toAbsolutePath();
+    assertNotEquals(0, ToolHelper.forkR8(working, "@flags.txt").exitCode);
+    DiagnosticsChecker.checkErrorsContains("File not found", handler ->
+        D8.run(
+            D8Command.parse(
+                new String[] { "@" + flags.toString() },
+                EmbeddedOrigin.INSTANCE,
+                handler).build()));
+  }
+
+  @Test
   public void printsHelpOnNoInput() throws Throwable {
     ProcessResult result = ToolHelper.forkD8(temp.getRoot().toPath());
     assertFalse(result.exitCode == 0);
@@ -418,6 +456,17 @@
                     .build()));
   }
 
+  @Test
+  public void noInputOutputsEmptyZip() throws CompilationFailedException, IOException {
+    Path emptyZip = temp.getRoot().toPath().resolve("empty.zip");
+    D8.run(
+        D8Command.builder()
+            .setOutput(emptyZip, OutputMode.DexIndexed)
+            .build());
+    assertTrue(Files.exists(emptyZip));
+    assertEquals(0, new ZipFile(emptyZip.toFile()).size());
+  }
+
   private D8Command parse(String... args) throws CompilationFailedException {
     return D8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
   }
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index dc4a9b4..1d9ec50 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -119,7 +119,7 @@
   }
 
   private AndroidApp mergeDexResources(int minAPILevel, List<ProgramResource> individalDexes)
-      throws IOException, CompilationException, CompilationFailedException, ResourceException {
+      throws IOException, CompilationFailedException, ResourceException {
     D8Command.Builder builder = D8Command.builder()
         .setMinApiLevel(minAPILevel);
     for (ProgramResource resource : individalDexes) {
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index 77959d9..ede7e53 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -76,7 +76,7 @@
 
       // compilation should have failed on CompilationError since A is declaring a default method.
       Assert.fail();
-    } catch (CompilationError | CompilationException e) {
+    } catch (CompilationError e) {
       // Expected.
     }
   }
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
index 2265c3e..2f45ae2 100644
--- a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
+++ b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
@@ -49,13 +49,14 @@
     try {
       runner.run(handler);
     } catch (CompilationFailedException e) {
+      List<String> messages = ListUtils.map(handler.errors, Diagnostic::getDiagnosticMessage);
       System.out.println("Expecting match for '" + snippet + "'");
-      System.out.println("StdErr:\n" + handler.errors);
+      System.out.println("StdErr:\n" + messages);
       assertTrue(
           "Expected to find snippet '"
               + snippet
               + "' in error messages:\n"
-              + String.join("\n", ListUtils.map(handler.errors, Diagnostic::getDiagnosticMessage)),
+              + String.join("\n", messages),
           handler.errors.stream().anyMatch(d -> d.getDiagnosticMessage().contains(snippet)));
       throw e;
     }
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index a89cbd4..fbd88be 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -11,9 +11,12 @@
 
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.utils.FileUtils;
 import com.google.common.collect.ImmutableList;
+import java.io.IOException;
 import java.lang.reflect.Method;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -21,6 +24,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.zip.ZipFile;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -88,6 +92,43 @@
   }
 
   @Test
+  public void flagsFile() throws Throwable {
+    Path working = temp.getRoot().toPath();
+    Path library = ToolHelper.getDefaultAndroidJar();
+    Path input = Paths.get(EXAMPLES_BUILD_DIR + "/arithmetic.jar").toAbsolutePath();
+    Path output = working.resolve("output.zip");
+    Path flagsFile = working.resolve("flags.txt");
+    FileUtils.writeTextFile(
+        flagsFile,
+        "--output",
+        "output.zip",
+        "--min-api",
+        "24",
+        "--lib",
+        library.toAbsolutePath().toString(),
+        input.toString());
+    assertEquals(0, ToolHelper.forkR8(working, "@flags.txt").exitCode);
+    assertTrue(Files.exists(output));
+    Marker marker = ExtractMarker.extractMarkerFromDexFile(output);
+    assertEquals(24, marker.getMinApi().intValue());
+    assertEquals(Tool.R8, marker.getTool());
+  }
+
+
+  @Test(expected=CompilationFailedException.class)
+  public void nonExistingFlagsFile() throws Throwable {
+    Path working = temp.getRoot().toPath();
+    Path flags = working.resolve("flags.txt").toAbsolutePath();
+    assertNotEquals(0, ToolHelper.forkR8(working, "@flags.txt").exitCode);
+    DiagnosticsChecker.checkErrorsContains("File not found", handler ->
+        R8.run(
+            R8Command.parse(
+                new String[] { "@" + flags.toString() },
+                EmbeddedOrigin.INSTANCE,
+                handler).build()));
+  }
+
+  @Test
   public void printsHelpOnNoInput() throws Throwable {
     ProcessResult result = ToolHelper.forkR8(temp.getRoot().toPath());
     assertFalse(result.exitCode == 0);
@@ -258,24 +299,6 @@
   }
 
   @Test
-  public void argumentsInFile() throws Throwable {
-    Path inputFile = temp.newFile("foobar.class").toPath();
-    Path pgConfFile = temp.newFile("pgconf.config").toPath();
-    Path argsFile = temp.newFile("more-args.txt").toPath();
-    FileUtils.writeTextFile(argsFile, ImmutableList.of(
-        "--debug --no-minification",
-        "--pg-conf " + pgConfFile,
-        inputFile.toString()
-    ));
-    R8Command command = parse("@" + argsFile.toString());
-    assertEquals(CompilationMode.DEBUG, command.getMode());
-    assertFalse(command.getEnableMinification());
-    assertFalse(
-        command.getEnableTreeShaking()); // We have no keep rules (proguard config file is empty).
-    assertEquals(1, ToolHelper.getApp(command).getClassProgramResourcesForTesting().size());
-  }
-
-  @Test
   public void nonExistingOutputJar() throws Throwable {
     Path nonExistingJar = temp.getRoot().toPath().resolve("non-existing-archive.jar");
     R8Command.builder().setOutput(nonExistingJar, OutputMode.DexIndexed).build();
@@ -428,6 +451,17 @@
     assertTrue(result.stdout.contains("-printconfiguration"));
   }
 
+  @Test
+  public void noInputOutputsEmptyZip() throws CompilationFailedException, IOException {
+    Path emptyZip = temp.getRoot().toPath().resolve("empty.zip");
+    R8.run(
+        R8Command.builder()
+            .setOutput(emptyZip, OutputMode.DexIndexed)
+            .build());
+    assertTrue(Files.exists(emptyZip));
+    assertEquals(0, new ZipFile(emptyZip.toFile()).size());
+  }
+
   private R8Command parse(String... args) throws CompilationFailedException {
     return R8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
   }
diff --git a/src/test/java/com/android/tools/r8/R8EntryPointTests.java b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
index 6f3473e..edf7f20 100644
--- a/src/test/java/com/android/tools/r8/R8EntryPointTests.java
+++ b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
@@ -131,8 +131,7 @@
     Assert.assertTrue(Files.isRegularFile(testFlags.getParent().resolve(SEEDS)));
   }
 
-  private R8Command getCommand(Path out)
-      throws CompilationException, IOException, CompilationFailedException {
+  private R8Command getCommand(Path out) throws IOException, CompilationFailedException {
     return R8Command.builder()
         .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
         .addProgramFiles(INPUT_JAR)
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index b27569b..b12eb2d 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -1388,7 +1388,7 @@
       boolean disableInlining,
       boolean disableClassInlining,
       boolean hasMissingClasses)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      throws IOException, ProguardRuleParserException, ExecutionException,
       CompilationFailedException {
     executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, compilationMode, null,
         disableInlining, disableClassInlining, hasMissingClasses);
@@ -1403,7 +1403,7 @@
       boolean disableInlining,
       boolean disableClassInlining,
       boolean hasMissingClasses)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      throws IOException, ProguardRuleParserException, ExecutionException,
         CompilationFailedException {
     assert mode != null;
     switch (compilerUnderTest) {
@@ -1434,7 +1434,7 @@
                   }
                 }
               } catch (ResourceException e) {
-                throw new CompilationException(e);
+                throw new CompilationError("", e);
               }
             }
           }
@@ -1621,7 +1621,7 @@
 
   protected void runJctfTest(CompilerUnderTest compilerUnderTest, String classFilePath,
       String fullClassName)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      throws IOException, ProguardRuleParserException, ExecutionException,
       CompilationFailedException {
     DexVm dexVm = ToolHelper.getDexVm();
 
@@ -1751,7 +1751,7 @@
       CompilationMode mode,
       DexVm dexVm,
       File resultDir)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      throws IOException, ProguardRuleParserException, ExecutionException,
       CompilationFailedException {
     executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getAbsolutePath(), mode,
         specification.disableInlining, specification.disableClassInlining,
@@ -1930,7 +1930,7 @@
             compilerUnderTest, fileNames, resultDir.getCanonicalPath(), compilationMode,
             specification.disableInlining, specification.disableClassInlining,
             specification.hasMissingClasses);
-      } catch (CompilationException | CompilationFailedException e) {
+      } catch (CompilationFailedException e) {
         throw new CompilationError(e.getMessage(), e);
       } catch (ExecutionException e) {
         throw e.getCause();
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 913278a..d8b886c 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -27,10 +27,12 @@
   @Parameters(name = "{0}_{1}_{2}_{3}_{5}_{6}")
   public static Collection<String[]> data() {
     String[] tests = {
+        "abstractmethodremoval.AbstractMethodRemoval",
         "arithmetic.Arithmetic",
         "arrayaccess.ArrayAccess",
         "barray.BArray",
         "bridge.BridgeMethod",
+        "catchhandleroverlap.CatchHandlerOverlap",
         "cse.CommonSubexpressionElimination",
         "constants.Constants",
         "controlflow.ControlFlow",
diff --git a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
index a504c7c..36a4f00 100644
--- a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
+++ b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
@@ -30,7 +30,7 @@
 
   @Ignore
   @Test
-  public void UnreachableCode() throws IOException, ExecutionException, CompilationException {
+  public void UnreachableCode() throws IOException, ExecutionException {
     String name = "unreachable-code-1";
     AndroidApp input =
         AndroidApp.builder()
diff --git a/src/test/java/com/android/tools/r8/ResourceShrinkerTest.java b/src/test/java/com/android/tools/r8/ResourceShrinkerTest.java
index 301f78f..d21d156 100644
--- a/src/test/java/com/android/tools/r8/ResourceShrinkerTest.java
+++ b/src/test/java/com/android/tools/r8/ResourceShrinkerTest.java
@@ -73,8 +73,7 @@
   }
 
   @Test
-  public void testEmptyClass()
-      throws CompilationFailedException, IOException, ExecutionException, CompilationException {
+  public void testEmptyClass() throws CompilationFailedException, IOException, ExecutionException {
     TrackAll analysis = runAnalysis(EmptyClass.class);
 
     assertThat(analysis.integers, is(Sets.newHashSet()));
@@ -95,7 +94,7 @@
 
   @Test
   public void testConstsAndFieldAndMethods()
-      throws CompilationFailedException, IOException, ExecutionException, CompilationException {
+      throws CompilationFailedException, IOException, ExecutionException {
     TrackAll analysis = runAnalysis(ConstInCode.class);
 
     assertThat(analysis.integers, is(Sets.newHashSet(10, 11)));
@@ -125,7 +124,7 @@
 
   @Test
   public void testStaticValues()
-      throws CompilationFailedException, IOException, ExecutionException, CompilationException {
+      throws CompilationFailedException, IOException, ExecutionException {
     TrackAll analysis = runAnalysis(StaticFields.class);
 
     assertThat(analysis.integers, hasItems(10, 11, 12, 13));
@@ -163,8 +162,7 @@
   }
 
   @Test
-  public void testAnnotations()
-      throws CompilationFailedException, IOException, ExecutionException, CompilationException {
+  public void testAnnotations() throws CompilationFailedException, IOException, ExecutionException {
     TrackAll analysis = runAnalysis(IntAnnotation.class, OuterAnnotation.class, Annotated.class);
 
     assertThat(analysis.integers, hasItems(10, 11, 12, 13, 14, 15, 42));
@@ -184,7 +182,7 @@
 
   @Test
   public void testWithSkippingSome()
-      throws ExecutionException, CompilationFailedException, CompilationException, IOException {
+      throws ExecutionException, CompilationFailedException, IOException {
     TrackAll analysis = runAnalysis(ResourceClassToSkip.class, ToProcess.class);
 
     assertThat(analysis.integers, hasItems(10, 11, 12));
@@ -222,13 +220,13 @@
   }
 
   private TrackAll runAnalysis(Class<?>... classes)
-      throws IOException, CompilationException, ExecutionException, CompilationFailedException {
+      throws IOException, ExecutionException, CompilationFailedException {
     AndroidApp app = readClasses(classes);
     return runOnApp(app);
   }
 
   private TrackAll runOnApp(AndroidApp app)
-      throws IOException, ExecutionException, CompilationFailedException, CompilationException {
+      throws IOException, ExecutionException, CompilationFailedException {
     AndroidApp outputApp = compileWithD8(app);
     Path outputDex = tmp.newFolder().toPath().resolve("classes.dex");
     outputApp.writeToDirectory(outputDex.getParent(), OutputMode.DexIndexed);
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 96591d8..331af0a 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -215,7 +215,7 @@
    * Compile an application with D8.
    */
   protected AndroidApp compileWithD8(AndroidApp app)
-      throws CompilationException, ExecutionException, IOException, CompilationFailedException {
+      throws ExecutionException, IOException, CompilationFailedException {
     D8Command.Builder builder = ToolHelper.prepareD8CommandBuilder(app);
     AndroidAppConsumers appSink = new AndroidAppConsumers(builder);
     D8.run(builder.build());
@@ -226,7 +226,7 @@
    * Compile an application with D8.
    */
   protected AndroidApp compileWithD8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ExecutionException, IOException, CompilationFailedException {
+      throws ExecutionException, IOException, CompilationFailedException {
     return ToolHelper.runD8(app, optionsConsumer);
   }
 
@@ -234,7 +234,7 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(Class... classes)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     return ToolHelper.runR8(readClasses(classes));
   }
@@ -243,7 +243,7 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(List<Class> classes)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(readClasses(classes)).build();
     return ToolHelper.runR8(command);
@@ -253,7 +253,7 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(List<Class> classes, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(readClasses(classes)).build();
     return ToolHelper.runR8(command, optionsConsumer);
@@ -263,7 +263,7 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(AndroidApp app)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(app).build();
     return ToolHelper.runR8(command);
@@ -273,7 +273,7 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(app).build();
     return ToolHelper.runR8(command, optionsConsumer);
@@ -283,7 +283,7 @@
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(List<Class> classes, String proguardConfig)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     return compileWithR8(readClasses(classes), proguardConfig);
   }
@@ -293,7 +293,7 @@
    */
   protected AndroidApp compileWithR8(
       List<Class> classes, String proguardConfig, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     return compileWithR8(readClasses(classes), proguardConfig, optionsConsumer);
   }
@@ -302,7 +302,7 @@
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(List<Class> classes, Path proguardConfig)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     return compileWithR8(readClasses(classes), proguardConfig);
   }
@@ -311,7 +311,7 @@
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(AndroidApp app, Path proguardConfig)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command =
         ToolHelper.prepareR8CommandBuilder(app)
@@ -324,7 +324,7 @@
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(AndroidApp app, String proguardConfig)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     return compileWithR8(app, proguardConfig, null);
   }
@@ -334,7 +334,7 @@
    */
   protected AndroidApp compileWithR8(
       AndroidApp app, String proguardConfig, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command =
         ToolHelper.prepareR8CommandBuilder(app)
@@ -348,7 +348,7 @@
    */
   protected AndroidApp compileWithR8(
       AndroidApp app, Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     R8Command command =
         ToolHelper.prepareR8CommandBuilder(app)
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index afa8c12..45ec280 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.DeviceRunner.DeviceRunnerConfigurationException;
 import com.android.tools.r8.ToolHelper.DexVm.Kind;
 import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -768,7 +767,7 @@
 
   public static ProguardConfiguration loadProguardConfiguration(
       DexItemFactory factory, List<Path> configPaths)
-      throws IOException, ProguardRuleParserException, CompilationException {
+      throws IOException, ProguardRuleParserException {
     Reporter reporter = new Reporter(new DefaultDiagnosticsHandler());
     if (configPaths.isEmpty()) {
       return ProguardConfiguration.defaultConfiguration(factory, reporter);
@@ -789,7 +788,7 @@
     return R8Command.builder(app).setProgramConsumer(DexIndexedConsumer.emptyConsumer());
   }
 
-  public static AndroidApp runR8(AndroidApp app) throws IOException, CompilationException {
+  public static AndroidApp runR8(AndroidApp app) throws IOException {
     try {
       return runR8(prepareR8CommandBuilder(app).build());
     } catch (CompilationFailedException e) {
@@ -798,7 +797,7 @@
   }
 
   public static AndroidApp runR8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, CompilationException {
+      throws IOException {
     try {
       return runR8(prepareR8CommandBuilder(app).build(), optionsConsumer);
     } catch (CompilationFailedException e) {
@@ -806,18 +805,17 @@
     }
   }
 
-  public static AndroidApp runR8(R8Command command) throws IOException, CompilationException {
+  public static AndroidApp runR8(R8Command command) throws IOException {
     return runR8(command, null);
   }
 
   public static AndroidApp runR8(R8Command command, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, CompilationException {
+      throws IOException {
     return runR8WithFullResult(command, optionsConsumer);
   }
 
   public static AndroidApp runR8WithFullResult(
-      R8Command command, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, CompilationException {
+      R8Command command, Consumer<InternalOptions> optionsConsumer) throws IOException {
     // TODO(zerny): Should we really be adding the android library in ToolHelper?
     AndroidApp app = command.getInputApp();
     if (app.getLibraryResourceProviders().isEmpty()) {
@@ -848,12 +846,12 @@
             ImmutableList.of("!junit/**", "!android/test/**"))));
   }
 
-  public static AndroidApp runD8(AndroidApp app) throws CompilationException, IOException {
+  public static AndroidApp runD8(AndroidApp app) throws IOException {
     return runD8(app, null);
   }
 
   public static AndroidApp runD8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, IOException {
+      throws IOException {
     try {
       return runD8(D8Command.builder(app), optionsConsumer);
     } catch (CompilationFailedException e) {
@@ -862,13 +860,13 @@
   }
 
   public static AndroidApp runD8(D8Command.Builder builder)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     return runD8(builder, null);
   }
 
   public static AndroidApp runD8(
       D8Command.Builder builder, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     AndroidAppConsumers compatSink = new AndroidAppConsumers(builder);
     D8Command command = builder.build();
     InternalOptions options = command.getInternalOptions();
@@ -1328,6 +1326,12 @@
     return builder;
   }
 
+  public static R8Command.Builder allowPartiallyImplementedProguardOptions(
+      R8Command.Builder builder) {
+    builder.allowPartiallyImplementedProguardOptions();
+    return builder;
+  }
+
   public static AndroidApp getApp(BaseCommand command) {
     return command.getInputApp();
   }
@@ -1354,7 +1358,7 @@
   }
 
   public static void writeApplication(DexApplication application, InternalOptions options)
-      throws ExecutionException, DexOverflowException {
+      throws ExecutionException {
     R8.writeApplication(
         Executors.newSingleThreadExecutor(),
         application,
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
index 5ec1bce..cc8633e4 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
@@ -432,7 +432,7 @@
     assertEquals(0, javaResult.exitCode);
 
     AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
-        // Disable inlining to avoid the (short) tested method from being inlined then removed.
+        // Disable inlining to avoid the (short) tested method from being inlined and then removed.
         internalOptions -> internalOptions.enableInlining = false);
 
     // Run processed (output) program on ART
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
index 35a7613..60df7d2 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
@@ -109,7 +109,7 @@
     assertEquals(0, javaResult.exitCode);
 
     AndroidApp optimizedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
-        // Disable inlining to avoid the (short) tested method from being inlined then removed.
+        // Disable inlining to avoid the (short) tested method from being inlined and then removed.
         internalOptions -> internalOptions.enableInlining = false);
 
     // Run optimized (output) program on ART
diff --git a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index c6a2bd3..d5d2c39 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -6,7 +6,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
@@ -43,7 +42,7 @@
   }
 
   private void runR8(Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      throws IOException, ProguardRuleParserException, ExecutionException,
       CompilationFailedException {
     ToolHelper.runR8(
         R8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
index 479a558..d0ad1df 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
@@ -5,7 +5,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -32,8 +31,7 @@
   @Rule
   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
-  static AndroidApp compileWithD8(Class... classes) throws CompilationException, IOException,
-      CompilationFailedException {
+  static AndroidApp compileWithD8(Class... classes) throws IOException, CompilationFailedException {
     D8Command.Builder builder = D8Command.builder();
     for (Class clazz : classes) {
       builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index 89935a7..ead9175 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.code.ConstString;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
@@ -108,8 +107,7 @@
   }
 
   @Test
-  public void manyFilesWithSharedSynthesizedClass()
-      throws ExecutionException, IOException, DexOverflowException {
+  public void manyFilesWithSharedSynthesizedClass() throws ExecutionException, IOException {
 
     // Create classes that all reference enough strings to overflow the index, but are all
     // at different offsets in the strings array. This ensures we trigger multiple rounds of
diff --git a/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java b/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
index 2b2a40b..4b3f4fe 100644
--- a/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
+++ b/src/test/java/com/android/tools/r8/dexfilemerger/DexFileMergerTests.java
@@ -6,7 +6,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.DexFileMergerHelper;
@@ -39,7 +38,7 @@
   @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   private Path createMergerInputWithTwoClasses(OutputMode outputMode, boolean addMarker)
-      throws CompilationFailedException, CompilationException, IOException {
+      throws CompilationFailedException, IOException {
     // Compile Class1 and Class2
     Path mergerInputZip = temp.newFolder().toPath().resolve("merger-input.zip");
     D8Command command =
@@ -55,8 +54,7 @@
   }
 
   private void testMarker(boolean addMarkerToInput)
-      throws CompilationFailedException, CompilationException, IOException, ResourceException,
-          ExecutionException {
+      throws CompilationFailedException, IOException, ResourceException, ExecutionException {
     Path mergerInputZip = createMergerInputWithTwoClasses(OutputMode.DexIndexed, addMarkerToInput);
 
     Marker inputMarker = ExtractMarker.extractMarkerFromDexFile(mergerInputZip);
@@ -74,20 +72,18 @@
 
   @Test
   public void testMarkerPreserved()
-      throws CompilationFailedException, CompilationException, IOException, ResourceException,
-          ExecutionException {
+      throws CompilationFailedException, IOException, ResourceException, ExecutionException {
     testMarker(true);
   }
 
   @Test
   public void testMarkerNotAdded()
-      throws CompilationFailedException, CompilationException, IOException, ResourceException,
-          ExecutionException {
+      throws CompilationFailedException, IOException, ResourceException, ExecutionException {
     testMarker(false);
   }
 
   @Test
-  public void mergeTwoFiles() throws CompilationFailedException, CompilationException, IOException {
+  public void mergeTwoFiles() throws CompilationFailedException, IOException {
     Path mergerInputZip = createMergerInputWithTwoClasses(OutputMode.DexFilePerClassFile, false);
 
     Path mergerOutputZip = temp.getRoot().toPath().resolve("merger-out.zip");
@@ -107,7 +103,7 @@
   }
 
   private void generateClassesAndTest(int extraMethodCount, int programResourcesSize)
-      throws IOException, ExecutionException, CompilationException, CompilationFailedException {
+      throws IOException, ExecutionException, CompilationFailedException {
     AndroidApp generatedApp =
         MainDexListTests.generateApplication(
             ImmutableList.of("A", "B"),
@@ -125,8 +121,7 @@
   }
 
   @Test(expected = CompilationFailedException.class)
-  public void failIfTooBig()
-      throws IOException, ExecutionException, CompilationException, CompilationFailedException {
+  public void failIfTooBig() throws IOException, ExecutionException, CompilationFailedException {
     // Generates an application with two classes, each with the number of methods just enough not to
     // fit into a single dex file.
     generateClassesAndTest(1, 2);
@@ -134,7 +129,7 @@
 
   @Test
   public void failIfTooBigControl()
-      throws IOException, ExecutionException, CompilationException, CompilationFailedException {
+      throws IOException, ExecutionException, CompilationFailedException {
     // Control test for failIfTooBig to make sure we don't fail with less methods.
     generateClassesAndTest(0, 1);
   }
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
index 5bbb9d5..a0b2515 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -8,7 +8,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.DexSplitterHelper;
@@ -56,7 +55,7 @@
   @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   private Path createInput(boolean dontCreateMarkerInD8)
-      throws IOException, CompilationFailedException, CompilationException {
+      throws IOException, CompilationFailedException {
     // Initial normal compile to create dex files.
     Path inputZip = temp.newFolder().toPath().resolve("input.zip");
     D8Command command =
@@ -76,8 +75,7 @@
   }
 
   private void testMarker(boolean addMarkerToInput)
-      throws CompilationFailedException, CompilationException, IOException, ResourceException,
-          ExecutionException {
+      throws CompilationFailedException, IOException, ResourceException, ExecutionException {
     Path inputZip = createInput(!addMarkerToInput);
 
     Path output = temp.newFolder().toPath().resolve("output");
@@ -102,15 +100,13 @@
 
   @Test
   public void testMarkerPreserved()
-      throws CompilationFailedException, CompilationException, IOException, ResourceException,
-          ExecutionException {
+      throws CompilationFailedException, IOException, ResourceException, ExecutionException {
     testMarker(true);
   }
 
   @Test
   public void testMarkerNotAdded()
-      throws CompilationFailedException, CompilationException, IOException, ResourceException,
-          ExecutionException {
+      throws CompilationFailedException, IOException, ResourceException, ExecutionException {
     testMarker(false);
   }
 
@@ -124,14 +120,13 @@
   @Test
   public void splitFilesNoObfuscation()
       throws CompilationFailedException, IOException, FeatureMappingException, ResourceException,
-      CompilationException, ExecutionException {
+          ExecutionException {
     noObfuscation(false);
     noObfuscation(true);
   }
 
   private void noObfuscation(boolean useOptions)
-      throws IOException, CompilationFailedException, FeatureMappingException,
-      ResourceException, ExecutionException, CompilationException {
+      throws IOException, CompilationFailedException, FeatureMappingException {
     Path inputZip = createInput(false);
     Path output = temp.newFolder().toPath().resolve("output");
     Files.createDirectory(output);
@@ -228,7 +223,7 @@
   @Test
   public void splitFilesFromJar()
       throws IOException, CompilationFailedException, FeatureMappingException, ResourceException,
-      CompilationException, ExecutionException {
+          ExecutionException {
     splitFromJars(true, true);
     splitFromJars(false, true);
     splitFromJars(true, false);
@@ -236,8 +231,7 @@
   }
 
   private void splitFromJars(boolean useOptions, boolean explicitBase)
-      throws IOException, CompilationFailedException, FeatureMappingException, ResourceException,
-      ExecutionException, CompilationException {
+      throws IOException, CompilationFailedException, FeatureMappingException {
     Path inputZip = createInput(false);
     Path output = temp.newFolder().toPath().resolve("output");
     Files.createDirectory(output);
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 3cab10a..bda9ac5 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -10,7 +10,6 @@
 
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.smali.SmaliTestBase;
 import com.android.tools.r8.utils.AndroidApp;
@@ -165,7 +164,7 @@
   }
 
   @Test
-  public void lookupFieldWithDefaultInInterface() throws DexOverflowException {
+  public void lookupFieldWithDefaultInInterface() {
     SmaliBuilder builder = new SmaliBuilder();
 
     builder.addInterface("Interface");
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
index a542b70..39c8d28 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -8,7 +8,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -64,14 +63,14 @@
       String referenceApk,
       String pgConf,
       String input)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      throws ExecutionException, IOException, ProguardRuleParserException,
           CompilationFailedException {
     return runAndCheckVerification(
         compiler, mode, referenceApk, pgConf, null, Collections.singletonList(input));
   }
 
   public AndroidApp runAndCheckVerification(D8Command.Builder builder, String referenceApk)
-      throws IOException, ExecutionException, CompilationException, CompilationFailedException {
+      throws IOException, ExecutionException, CompilationFailedException {
     AndroidAppConsumers appSink = new AndroidAppConsumers(builder);
     D8.run(builder.build());
     AndroidApp result = appSink.build();
@@ -86,7 +85,7 @@
       String pgConf,
       Consumer<InternalOptions> optionsConsumer,
       List<String> inputs)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      throws ExecutionException, IOException, ProguardRuleParserException,
       CompilationFailedException {
     assertTrue(referenceApk == null || new File(referenceApk).exists());
     AndroidAppConsumers outputApp;
@@ -99,6 +98,7 @@
       builder.setMode(mode);
       builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
       builder.setMinApiLevel(AndroidApiLevel.L.getLevel());
+      ToolHelper.allowPartiallyImplementedProguardOptions(builder);
       ToolHelper.addProguardConfigurationConsumer(
           builder,
           pgConfig -> {
diff --git a/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java
index e546607..ab430d5 100644
--- a/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8FrameworkDeterministicTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -19,7 +18,7 @@
   private static final String JAR = "third_party/framework/framework_160115954.jar";
 
   private AndroidApp doRun(D8Command.Builder builder)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     builder.setProgramConsumer(null);
     AndroidAppConsumers appSink = new AndroidAppConsumers(builder);
     D8.run(builder.build());
diff --git a/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
index 0946ded..390105e 100644
--- a/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
@@ -17,7 +16,7 @@
       "third_party/photos/2017-06-06/PhotosEnglishOnlyLegacy_proguard.jar";
 
   public void runD8AndCheckVerification(CompilationMode mode, String version)
-      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     runAndCheckVerification(CompilerUnderTest.D8, mode, version, null, version);
   }
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
index df4bbfe..f0b1ece 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
@@ -33,14 +32,14 @@
   static final String REFERENCE_APK = "noshrink_x86_GmsCore_prod_alldpi_release_unsigned.apk";
 
   public void runR8AndCheckVerification(CompilationMode mode, String version)
-      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException,
+      throws ProguardRuleParserException, ExecutionException, IOException,
       CompilationFailedException {
     runAndCheckVerification(CompilerUnderTest.R8, mode, version);
   }
 
   public void runAndCheckVerification(
       CompilerUnderTest compiler, CompilationMode mode, String version)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      throws ExecutionException, IOException, ProguardRuleParserException,
       CompilationFailedException {
     runAndCheckVerification(
         compiler, mode, version + GMSCORE_APK, null, Paths.get(version, GMSCORE_APK).toString());
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
index 5f635ff..09ce657 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
@@ -19,7 +18,7 @@
 
   public AndroidApp buildFromDeployJar(
       CompilerUnderTest compiler, CompilationMode mode, String base, boolean hasReference)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      throws ExecutionException, IOException, ProguardRuleParserException,
       CompilationFailedException {
     return runAndCheckVerification(
         compiler, mode, hasReference ? base + REFERENCE_APK : null, null, base + DEPLOY_JAR);
@@ -29,7 +28,7 @@
   public AndroidApp buildFromDeployJar(
       CompilerUnderTest compiler, CompilationMode mode, String base, boolean hasReference,
       Consumer<InternalOptions> optionsConsumer)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      throws ExecutionException, IOException, ProguardRuleParserException,
       CompilationFailedException {
     return runAndCheckVerification(
         compiler,
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
index 5e1c61f..abc7a73 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.OutputMode;
@@ -33,7 +32,7 @@
   }
 
   private AndroidApp doRun()
-      throws IOException, ProguardRuleParserException, CompilationException, ExecutionException,
+      throws IOException, ProguardRuleParserException, ExecutionException,
       CompilationFailedException {
     R8Command command =
         R8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
index 5afb068..d4ab6c6 100644
--- a/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/IrInjectionTestBase.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -37,7 +36,7 @@
       return buildApplication(
           AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build(),
           options);
-    } catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
+    } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
@@ -118,8 +117,7 @@
       return iterator;
     }
 
-    private AndroidApp writeDex(DexApplication application, InternalOptions options)
-        throws DexOverflowException {
+    private AndroidApp writeDex(DexApplication application, InternalOptions options) {
       try {
         ToolHelper.writeApplication(application, options);
         options.signalFinishedToConsumers();
@@ -129,7 +127,7 @@
       }
     }
 
-    public String run() throws DexOverflowException, IOException {
+    public String run() throws IOException {
       AppInfo appInfo = new AppInfo(application);
       IRConverter converter = new IRConverter(appInfo, options);
       converter.replaceCodeForTesting(method, code);
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
index 967b33e..aefe665 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeAnalysisTest.java
@@ -7,9 +7,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
-import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfo;
@@ -124,15 +122,11 @@
             .method(
                 new MethodSignature("subtractConstants8bitRegisters", "int", ImmutableList.of()))
             .getMethod();
-    try {
-      IRCode irCode = subtract.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
-      TypeAnalysis analysis = new TypeAnalysis(appInfo, subtract, irCode);
-      analysis.forEach((v, l) -> {
-        assertEither(l, PRIMITIVE, NULL, TOP);
-      });
-    } catch (ApiLevelException e) {
-      fail(e.getMessage());
-    }
+    IRCode irCode = subtract.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+    TypeAnalysis analysis = new TypeAnalysis(appInfo, subtract, irCode);
+    analysis.forEach((v, l) -> {
+      assertEither(l, PRIMITIVE, NULL, TOP);
+    });
   }
 
   // A couple branches, along with some recursive calls.
@@ -142,15 +136,11 @@
         inspector.clazz("Test")
             .method(new MethodSignature("fibonacci", "int", ImmutableList.of("int")))
             .getMethod();
-    try {
-      IRCode irCode = fib.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
-      TypeAnalysis analysis = new TypeAnalysis(appInfo, fib, irCode);
-      analysis.forEach((v, l) -> {
-        assertEither(l, PRIMITIVE, NULL);
-      });
-    } catch (ApiLevelException e) {
-      fail(e.getMessage());
-    }
+    IRCode irCode = fib.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+    TypeAnalysis analysis = new TypeAnalysis(appInfo, fib, irCode);
+    analysis.forEach((v, l) -> {
+      assertEither(l, PRIMITIVE, NULL);
+    });
   }
 
   // fill-array-data
@@ -160,32 +150,28 @@
         inspector.clazz("Test")
             .method(new MethodSignature("test1", "int[]", ImmutableList.of()))
             .getMethod();
-    try {
-      IRCode irCode = test1.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
-      TypeAnalysis analysis = new TypeAnalysis(appInfo, test1, irCode);
-      Value array = null;
-      InstructionIterator iterator = irCode.instructionIterator();
-      while (iterator.hasNext()) {
-        Instruction instruction = iterator.next();
-        if (instruction instanceof NewArrayEmpty) {
-          array = instruction.outValue();
-          break;
-        }
+    IRCode irCode = test1.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+    TypeAnalysis analysis = new TypeAnalysis(appInfo, test1, irCode);
+    Value array = null;
+    InstructionIterator iterator = irCode.instructionIterator();
+    while (iterator.hasNext()) {
+      Instruction instruction = iterator.next();
+      if (instruction instanceof NewArrayEmpty) {
+        array = instruction.outValue();
+        break;
       }
-      assertNotNull(array);
-      final Value finalArray = array;
-      analysis.forEach((v, l) -> {
-        if (v == finalArray) {
-          assertTrue(l.isArrayTypeLatticeElement());
-          ArrayTypeLatticeElement lattice = l.asArrayTypeLatticeElement();
-          assertTrue(lattice.getArrayType().isPrimitiveArrayType());
-          assertEquals(1, lattice.getNesting());
-          assertFalse(lattice.isNullable());
-        }
-      });
-    } catch (ApiLevelException e) {
-      fail(e.getMessage());
     }
+    assertNotNull(array);
+    final Value finalArray = array;
+    analysis.forEach((v, l) -> {
+      if (v == finalArray) {
+        assertTrue(l.isArrayTypeLatticeElement());
+        ArrayTypeLatticeElement lattice = l.asArrayTypeLatticeElement();
+        assertTrue(lattice.getArrayType().isPrimitiveArrayType());
+        assertEquals(1, lattice.getNesting());
+        assertFalse(lattice.isNullable());
+      }
+    });
   }
 
   // filled-new-array
@@ -195,32 +181,28 @@
         inspector.clazz("Test")
             .method(new MethodSignature("test4", "int[]", ImmutableList.of()))
             .getMethod();
-    try {
-      IRCode irCode = test4.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
-      TypeAnalysis analysis = new TypeAnalysis(appInfo, test4, irCode);
-      Value array = null;
-      InstructionIterator iterator = irCode.instructionIterator();
-      while (iterator.hasNext()) {
-        Instruction instruction = iterator.next();
-        if (instruction instanceof InvokeNewArray) {
-          array = instruction.outValue();
-          break;
-        }
+    IRCode irCode = test4.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+    TypeAnalysis analysis = new TypeAnalysis(appInfo, test4, irCode);
+    Value array = null;
+    InstructionIterator iterator = irCode.instructionIterator();
+    while (iterator.hasNext()) {
+      Instruction instruction = iterator.next();
+      if (instruction instanceof InvokeNewArray) {
+        array = instruction.outValue();
+        break;
       }
-      assertNotNull(array);
-      final Value finalArray = array;
-      analysis.forEach((v, l) -> {
-        if (v == finalArray) {
-          assertTrue(l.isArrayTypeLatticeElement());
-          ArrayTypeLatticeElement lattice = l.asArrayTypeLatticeElement();
-          assertTrue(lattice.getArrayType().isPrimitiveArrayType());
-          assertEquals(1, lattice.getNesting());
-          assertFalse(lattice.isNullable());
-        }
-      });
-    } catch (ApiLevelException e) {
-      fail(e.getMessage());
     }
+    assertNotNull(array);
+    final Value finalArray = array;
+    analysis.forEach((v, l) -> {
+      if (v == finalArray) {
+        assertTrue(l.isArrayTypeLatticeElement());
+        ArrayTypeLatticeElement lattice = l.asArrayTypeLatticeElement();
+        assertTrue(lattice.getArrayType().isPrimitiveArrayType());
+        assertEquals(1, lattice.getNesting());
+        assertFalse(lattice.isNullable());
+      }
+    });
   }
 
   // Make sure the analysis does not hang.
@@ -230,20 +212,16 @@
         inspector.clazz("Test")
             .method(new MethodSignature("loop2", "void", ImmutableList.of()))
             .getMethod();
-    try {
-      IRCode irCode = loop2.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
-      TypeAnalysis analysis = new TypeAnalysis(appInfo, loop2, irCode);
-      analysis.forEach((v, l) -> {
-        if (l.isClassTypeLatticeElement()) {
-          ClassTypeLatticeElement lattice = l.asClassTypeLatticeElement();
-          assertEquals("Ljava/io/PrintStream;", lattice.getClassType().toDescriptorString());
-          // TODO(b/70795205): Can be refined by using control-flow info.
-          assertTrue(l.isNullable());
-        }
-      });
-    } catch (ApiLevelException e) {
-      fail(e.getMessage());
-    }
+    IRCode irCode = loop2.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+    TypeAnalysis analysis = new TypeAnalysis(appInfo, loop2, irCode);
+    analysis.forEach((v, l) -> {
+      if (l.isClassTypeLatticeElement()) {
+        ClassTypeLatticeElement lattice = l.asClassTypeLatticeElement();
+        assertEquals("Ljava/io/PrintStream;", lattice.getClassType().toDescriptorString());
+        // TODO(b/70795205): Can be refined by using control-flow info.
+        assertTrue(l.isNullable());
+      }
+    });
   }
 
   // move-exception
@@ -253,19 +231,15 @@
         inspector.clazz("Test")
             .method(new MethodSignature("test2_throw", "int", ImmutableList.of()))
             .getMethod();
-    try {
-      IRCode irCode = test2.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
-      TypeAnalysis analysis = new TypeAnalysis(appInfo, test2, irCode);
-      analysis.forEach((v, l) -> {
-        if (l.isClassTypeLatticeElement()) {
-          ClassTypeLatticeElement lattice = l.asClassTypeLatticeElement();
-          assertEquals("Ljava/lang/Throwable;", lattice.getClassType().toDescriptorString());
-          assertFalse(l.isNullable());
-        }
-      });
-    } catch (ApiLevelException e) {
-      fail(e.getMessage());
-    }
+    IRCode irCode = test2.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+    TypeAnalysis analysis = new TypeAnalysis(appInfo, test2, irCode);
+    analysis.forEach((v, l) -> {
+      if (l.isClassTypeLatticeElement()) {
+        ClassTypeLatticeElement lattice = l.asClassTypeLatticeElement();
+        assertEquals("Ljava/lang/Throwable;", lattice.getClassType().toDescriptorString());
+        assertFalse(l.isNullable());
+      }
+    });
   }
 
   // One very complicated example.
@@ -283,13 +257,9 @@
         ConstString.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, false),
         CheckCast.class, new ClassTypeLatticeElement(test, true),
         NewInstance.class, new ClassTypeLatticeElement(test, false));
-    try {
-      IRCode irCode = method.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
-      TypeAnalysis analysis = new TypeAnalysis(appInfo, method, irCode);
-      analysis.forEach((v, l) -> verifyTypeEnvironment(expectedLattices, v, l));
-    } catch (ApiLevelException e) {
-      fail(e.getMessage());
-    }
+    IRCode irCode = method.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+    TypeAnalysis analysis = new TypeAnalysis(appInfo, method, irCode);
+    analysis.forEach((v, l) -> verifyTypeEnvironment(expectedLattices, v, l));
   }
 
   // One more complicated example.
@@ -305,13 +275,9 @@
       ConstString.class, new ClassTypeLatticeElement(appInfo.dexItemFactory.stringType, false),
       InstanceOf.class, PRIMITIVE,
       StaticGet.class, new ClassTypeLatticeElement(test, true));
-    try {
-      IRCode irCode = method.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
-      TypeAnalysis analysis = new TypeAnalysis(appInfo, method, irCode);
-      analysis.forEach((v, l) -> verifyTypeEnvironment(expectedLattices, v, l));
-    } catch (ApiLevelException e) {
-      fail(e.getMessage());
-    }
+    IRCode irCode = method.buildIR(appInfo, TEST_OPTIONS, Origin.unknown());
+    TypeAnalysis analysis = new TypeAnalysis(appInfo, method, irCode);
+    analysis.forEach((v, l) -> verifyTypeEnvironment(expectedLattices, v, l));
   }
 
   private static void assertEither(TypeLatticeElement actual, TypeLatticeElement... expected) {
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
index 9c58d25..1f4d2ba 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.utils.DexInspector;
 import java.util.Collections;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class CovariantReturnTypeAnnotationTransformerTest extends AsmTestBase {
@@ -49,7 +48,6 @@
     failsIndependentOfFlag(input);
   }
 
-  @Ignore("b/78618808")
   @Test
   public void testVersion2WithClient1And2() throws Exception {
     AndroidApp input =
@@ -63,7 +61,6 @@
     succeedsIndependentOfFlag(input, true);
   }
 
-  @Ignore("b/78618808")
   @Test
   public void testVersion2WithClient3() throws Exception {
     AndroidApp input =
@@ -108,6 +105,23 @@
     succeedsIndependentOfFlag(input, false);
   }
 
+  @Test
+  public void testRepeatedCompilation() throws Exception {
+    AndroidApp input =
+        buildAndroidApp(
+            ToolHelper.getClassAsBytes(Client.class),
+            ToolHelper.getClassAsBytes(A.class),
+            com.android.tools.r8.ir.desugar.annotations.version2.BDump.dump(),
+            com.android.tools.r8.ir.desugar.annotations.version2.CDump.dump());
+
+    AndroidApp output =
+        compileWithD8(input, options -> options.processCovariantReturnTypeAnnotations = true);
+
+    // Compilation will fail with a compilation error the second time if the implementation does
+    // not remove the CovariantReturnType annotations properly during the first compilation.
+    compileWithD8(output, options -> options.processCovariantReturnTypeAnnotations = true);
+  }
+
   private void succeedsWithOption(
       AndroidApp input, boolean option, boolean checkPresenceOfSyntheticMethods) throws Exception {
     AndroidApp output =
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
index 47f0db3..a5c7093 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -6,7 +6,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
@@ -99,8 +98,7 @@
         });
   }
 
-  private Path runR8(Path proguardConfig)
-      throws IOException, CompilationException, CompilationFailedException {
+  private Path runR8(Path proguardConfig) throws IOException, CompilationFailedException {
     Path dexOutputDir = temp.newFolder().toPath();
     ToolHelper.runR8(
         R8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
index 990fe6e..7b74453 100644
--- a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
+++ b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.jsr45;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
@@ -50,7 +49,7 @@
   public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
 
   private AndroidApp compileWithD8(Path intputPath, Path outputPath)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     D8Command.Builder builder =
         D8Command.builder()
             .setMinApiLevel(AndroidApiLevel.O.getLevel())
@@ -62,7 +61,7 @@
   }
 
   private AndroidApp compileWithR8(Path inputPath, Path outputPath, Path keepRulesPath)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     return ToolHelper.runR8(
         R8Command.builder()
             .addProgramFiles(inputPath)
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index bb83093..c175e04 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -49,6 +49,12 @@
   // invoke the tested method.
   private static final String JASMIN_MAIN_CLASS = "TestMain";
 
+  @Parameter(0) public boolean allowAccessModification;
+  @Parameter(1) public KotlinTargetVersion targetVersion;
+
+  private final List<Path> classpath = new ArrayList<>();
+  private final List<Path> extraClasspath = new ArrayList<>();
+
   @Parameters(name = "allowAccessModification: {0} target: {1}")
   public static Collection<Object[]> data() {
     ImmutableList.Builder<Object[]> builder = new ImmutableList.Builder<>();
@@ -59,11 +65,6 @@
     return builder.build();
   }
 
-  @Parameter(0) public boolean allowAccessModification;
-  @Parameter(1) public KotlinTargetVersion targetVersion;
-
-  private final List<Path> extraClasspath = new ArrayList<>();
-
   protected void addExtraClasspath(Path path) {
     extraClasspath.add(path);
   }
@@ -111,47 +112,67 @@
     }));
   }
 
-  protected static ClassSubject checkClassExists(DexInspector inspector, String className) {
+  protected ClassSubject checkClassIsKept(DexInspector inspector, String className) {
+    checkClassExistsInInput(className);
     ClassSubject classSubject = inspector.clazz(className);
     assertNotNull(classSubject);
     assertTrue("No class " + className, classSubject.isPresent());
     return classSubject;
   }
 
-  protected static FieldSubject checkFieldIsPresent(ClassSubject classSubject, String fieldType,
+  protected FieldSubject checkFieldIsKept(ClassSubject classSubject, String fieldType,
       String fieldName) {
+    // Field must exist in the input.
+    checkFieldPresenceInInput(classSubject.getOriginalName(), fieldType, fieldName, true);
     FieldSubject fieldSubject = classSubject.field(fieldType, fieldName);
     assertTrue("No field " + fieldName + " in " + classSubject.getOriginalName(),
         fieldSubject.isPresent());
     return fieldSubject;
   }
 
-  protected static void checkFieldIsAbsent(ClassSubject classSubject, String fieldType,
+  protected void checkFieldIsAbsent(ClassSubject classSubject, String fieldType,
       String fieldName) {
+    // Field must NOT exist in the input.
+    checkFieldPresenceInInput(classSubject.getOriginalName(), fieldType, fieldName, false);
     FieldSubject fieldSubject = classSubject.field(fieldType, fieldName);
     assertNotNull(fieldSubject);
     assertFalse(fieldSubject.isPresent());
   }
 
-  protected static MethodSubject checkMethodIsPresent(ClassSubject classSubject,
+  protected void checkMethodIsAbsent(ClassSubject classSubject,
       MethodSignature methodSignature) {
-    return checkMethod(classSubject, methodSignature, true);
+    checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, false);
+    checkMethodPresenceInOutput(classSubject, methodSignature, false);
   }
 
-  protected static void checkMethodIsAbsent(ClassSubject classSubject,
+  protected MethodSubject checkMethodIsKept(ClassSubject classSubject,
       MethodSignature methodSignature) {
-    checkMethod(classSubject, methodSignature, false);
+    checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
+    return checkMethodisKeptOrRemoved(classSubject, methodSignature, true);
   }
 
-  protected static MethodSubject checkMethod(ClassSubject classSubject,
+  protected void checkMethodIsRemoved(ClassSubject classSubject,
+      MethodSignature methodSignature) {
+    checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
+    checkMethodisKeptOrRemoved(classSubject, methodSignature, false);
+  }
+
+  protected MethodSubject checkMethodisKeptOrRemoved(ClassSubject classSubject,
+      MethodSignature methodSignature, boolean isPresent) {
+    checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
+    return checkMethodPresenceInOutput(classSubject, methodSignature, isPresent);
+  }
+
+  private MethodSubject checkMethodPresenceInOutput(ClassSubject classSubject,
       MethodSignature methodSignature, boolean isPresent) {
     MethodSubject methodSubject = classSubject.method(methodSignature);
     assertNotNull(methodSubject);
 
+    String methodSig = methodSignature.name + methodSignature.toDescriptor();
     if (isPresent) {
-      assertTrue("No method " + methodSignature.name, methodSubject.isPresent());
+      assertTrue("No method " + methodSig + " in output", methodSubject.isPresent());
     } else {
-      assertFalse("Method " + methodSignature.name + " exists", methodSubject.isPresent());
+      assertFalse("Method " + methodSig + " exists in output", methodSubject.isPresent());
     }
     return methodSubject;
   }
@@ -210,7 +231,7 @@
     }
 
     // Build classpath for compilation (and java execution)
-    List<Path> classpath = new ArrayList<>(extraClasspath.size() + 1);
+    assert classpath.isEmpty();
     classpath.add(getKotlinJarFile(folder));
     classpath.add(getJavaJarFile(folder));
     classpath.addAll(extraClasspath);
@@ -240,6 +261,36 @@
     inspector.inspectApp(app);
   }
 
+  protected void checkClassExistsInInput(String className) {
+    if (!AsmUtils.doesClassExist(classpath, className)) {
+      throw new AssertionError("Class " + className + " does not exist in input");
+    }
+  }
+
+  private void checkMethodPresenceInInput(String className, MethodSignature methodSignature,
+      boolean isPresent) {
+    boolean foundMethod = AsmUtils.doesMethodExist(classpath, className,
+        methodSignature.name, methodSignature.toDescriptor());
+    if (isPresent != foundMethod) {
+      throw new AssertionError(
+          "Method " + methodSignature.name + methodSignature.toDescriptor()
+              + " " + (foundMethod ? "exists" : "does not exist")
+              + " in input class " + className + " but is expected to be "
+              + (isPresent ? "present" : "absent"));
+    }
+  }
+
+  private void checkFieldPresenceInInput(String className, String fieldType, String fieldName,
+      boolean isPresent) {
+    boolean foundField = AsmUtils.doesFieldExist(classpath, className, fieldName, fieldType);
+    if (isPresent != foundField) {
+      throw new AssertionError(
+          "Field " + fieldName + " " + (foundField ? "exists" : "does not exist")
+              + " in input class " + className + " but is expected to be "
+              + (isPresent ? "present" : "absent"));
+    }
+  }
+
   private Path getKotlinJarFile(String folder) {
     return Paths.get(ToolHelper.TESTS_BUILD_DIR, "kotlinR8TestResources",
         targetVersion.getFolderName(), folder + FileUtils.JAR_EXTENSION);
diff --git a/src/test/java/com/android/tools/r8/kotlin/AsmUtils.java b/src/test/java/com/android/tools/r8/kotlin/AsmUtils.java
new file mode 100644
index 0000000..7b7abcb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/AsmUtils.java
@@ -0,0 +1,144 @@
+// Copyright (c) 2018, 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.kotlin;
+
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Utilities to lookup symbols in a classpath using ASM.
+ */
+public final class AsmUtils {
+
+  private AsmUtils() {
+  }
+
+  public static boolean doesClassExist(List<Path> classpath, String className) {
+    byte[] classData = loadClassBytesFromClasspath(classpath, className);
+    return classData != null;
+  }
+
+  public static boolean doesMethodExist(List<Path> classpath, String className,
+      String methodName,
+      String methodDescriptor) {
+    MethodFinder classVisitor = new MethodFinder(methodName, methodDescriptor);
+    visitClass(classpath, className, classVisitor);
+    return classVisitor.foundMethod;
+  }
+
+  private static final class MethodFinder extends ClassVisitor {
+
+    private final String methodName;
+    private final String methodDescriptor;
+    public boolean foundMethod = false;
+
+    public MethodFinder(String methodName, String methodDescriptor) {
+      super(Opcodes.ASM6);
+      this.methodName = methodName;
+      this.methodDescriptor = methodDescriptor;
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+        String[] exceptions) {
+      if (name.equals(methodName) && desc.equals(methodDescriptor)) {
+        assert !foundMethod;
+        foundMethod = true;
+      }
+      return null;
+    }
+  }
+
+  public static boolean doesFieldExist(List<Path> classpath, String className,
+      String fieldName,
+      String fieldType) {
+    FieldFinder classVisitor = new FieldFinder(fieldName, fieldType);
+    visitClass(classpath, className, classVisitor);
+    return classVisitor.foundField;
+  }
+
+  private static final class FieldFinder extends ClassVisitor {
+
+    private final String fieldName;
+    private final String fieldDescriptor;
+    public boolean foundField = false;
+
+    public FieldFinder(String fieldName, String fieldType) {
+      super(Opcodes.ASM6);
+      this.fieldName = fieldName;
+      this.fieldDescriptor = DescriptorUtils.javaTypeToDescriptor(fieldType);
+    }
+
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc, String signature,
+        Object value) {
+      if (name.equals(fieldName) && desc.equals(fieldDescriptor)) {
+        assert !foundField;
+        foundField = true;
+      }
+      return null;
+    }
+  }
+
+  private static void visitClass(List<Path> classpath, String className,
+      ClassVisitor classVisitor) {
+    byte[] classData = loadClassBytesFromClasspath(classpath, className);
+    new ClassReader(classData).accept(classVisitor,
+        ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+  }
+
+  private static byte[] loadClassBytesFromClasspath(List<Path> classpath, String className) {
+    String classFilename = DescriptorUtils.getPathFromJavaType(className);
+    for (Path path : classpath) {
+      if (path.toFile().getPath().endsWith(".jar")) {
+        try (JarInputStream jarInputStream = new JarInputStream(Files.newInputStream(path))) {
+          JarEntry jarEntry;
+          while ((jarEntry = jarInputStream.getNextJarEntry()) != null) {
+            if (jarEntry.isDirectory()) {
+              continue;
+            }
+            String entryName = jarEntry.getName();
+            if (entryName.equals(classFilename)) {
+              byte[] data = new byte[1024];
+              ByteArrayOutputStream baos = new ByteArrayOutputStream();
+              while (true) {
+                int bytesRead = jarInputStream.read(data, 0, data.length);
+                if (bytesRead < 0) {
+                  break;
+                }
+                baos.write(data, 0, bytesRead);
+              }
+              return baos.toByteArray();
+            }
+          }
+        } catch (IOException e) {
+          throw new AssertionError(e);
+        }
+      } else if (path.toFile().getPath().endsWith(".class")) {
+        if (path.toFile().getPath().equals(classFilename)) {
+          try {
+            return Files.readAllBytes(path);
+          } catch (IOException e) {
+            throw new AssertionError(e);
+          }
+        }
+
+      }
+    }
+    return null;
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index df04931..7425082 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -66,9 +66,9 @@
         "companionProperties_usePrimitiveProp");
     runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+      ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
       String propertyName = "primitiveProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, "int", propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, "int", propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getterAccessor = testedClass
@@ -78,12 +78,12 @@
 
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(outerClass, getterAccessor);
-        checkMethodIsAbsent(outerClass, setterAccessor);
+        checkMethodIsRemoved(outerClass, getterAccessor);
+        checkMethodIsRemoved(outerClass, setterAccessor);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(outerClass, getterAccessor);
-        checkMethodIsPresent(outerClass, setterAccessor);
+        checkMethodIsKept(outerClass, getterAccessor);
+        checkMethodIsKept(outerClass, setterAccessor);
       }
     });
   }
@@ -95,9 +95,9 @@
         "companionProperties_usePrivateProp");
     runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+      ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
       String propertyName = "privateProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getterAccessor = testedClass
@@ -107,13 +107,13 @@
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
 
-        checkMethodIsAbsent(outerClass, getterAccessor);
-        checkMethodIsAbsent(outerClass, setterAccessor);
+        checkMethodIsRemoved(outerClass, getterAccessor);
+        checkMethodIsRemoved(outerClass, setterAccessor);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
 
-        checkMethodIsPresent(outerClass, getterAccessor);
-        checkMethodIsPresent(outerClass, setterAccessor);
+        checkMethodIsKept(outerClass, getterAccessor);
+        checkMethodIsKept(outerClass, setterAccessor);
       }
     });
   }
@@ -125,9 +125,9 @@
         "companionProperties_useInternalProp");
     runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+      ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
       String propertyName = "internalProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getterAccessor = testedClass
@@ -137,12 +137,12 @@
 
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(outerClass, getterAccessor);
-        checkMethodIsAbsent(outerClass, setterAccessor);
+        checkMethodIsRemoved(outerClass, getterAccessor);
+        checkMethodIsRemoved(outerClass, setterAccessor);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(outerClass, getterAccessor);
-        checkMethodIsPresent(outerClass, setterAccessor);
+        checkMethodIsKept(outerClass, getterAccessor);
+        checkMethodIsKept(outerClass, setterAccessor);
       }
     });
   }
@@ -154,9 +154,9 @@
         "companionProperties_usePublicProp");
     runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+      ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
       String propertyName = "publicProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getterAccessor = testedClass
@@ -166,12 +166,12 @@
 
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(outerClass, getterAccessor);
-        checkMethodIsAbsent(outerClass, setterAccessor);
+        checkMethodIsRemoved(outerClass, getterAccessor);
+        checkMethodIsRemoved(outerClass, setterAccessor);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(outerClass, getterAccessor);
-        checkMethodIsPresent(outerClass, setterAccessor);
+        checkMethodIsKept(outerClass, getterAccessor);
+        checkMethodIsKept(outerClass, setterAccessor);
       }
     });
   }
@@ -183,9 +183,9 @@
         "companionLateInitProperties_usePrivateLateInitProp");
     runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+      ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
       String propertyName = "privateLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getterAccessor = testedClass
@@ -194,12 +194,12 @@
           .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(outerClass, getterAccessor);
-        checkMethodIsAbsent(outerClass, setterAccessor);
+        checkMethodIsRemoved(outerClass, getterAccessor);
+        checkMethodIsRemoved(outerClass, setterAccessor);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(outerClass, getterAccessor);
-        checkMethodIsPresent(outerClass, setterAccessor);
+        checkMethodIsKept(outerClass, getterAccessor);
+        checkMethodIsKept(outerClass, setterAccessor);
       }
     });
   }
@@ -211,10 +211,11 @@
         "companionLateInitProperties_useInternalLateInitProp");
     runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+      ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
       String propertyName = "internalLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
+      assertTrue(fieldSubject.getField().accessFlags.isPublic());
 
       MemberNaming.MethodSignature getterAccessor = testedClass
           .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
@@ -234,10 +235,11 @@
         "companionLateInitProperties_usePublicLateInitProp");
     runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+      ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
       String propertyName = "publicLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
+      assertTrue(fieldSubject.getField().accessFlags.isPublic());
 
       MemberNaming.MethodSignature getterAccessor = testedClass
           .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
@@ -257,49 +259,50 @@
         "accessor_accessPropertyFromCompanionClass");
     runTest("accessors", mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
-      ClassSubject companionClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
+      ClassSubject companionClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "property";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       // The getter is always inlined since it just calls into the accessor.
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
-      checkMethodIsAbsent(companionClass, getter);
+      checkMethodIsRemoved(companionClass, getter);
 
       MemberNaming.MethodSignature getterAccessor =
           testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(outerClass, getterAccessor);
+        checkMethodIsRemoved(outerClass, getterAccessor);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(outerClass, getterAccessor);
+        checkMethodIsKept(outerClass, getterAccessor);
       }
     });
   }
 
   @Test
+  @Ignore("b/74103342")
   public void testAccessorFromPrivate() throws Exception {
     TestKotlinCompanionClass testedClass = ACCESSOR_COMPANION_PROPERTY_CLASS;
     String mainClass = addMainToClasspath("accessors.AccessorKt",
         "accessor_accessPropertyFromOuterClass");
     runTest("accessors", mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
-      ClassSubject companionClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
+      ClassSubject companionClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "property";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       // We cannot inline the getter because we don't know if NPE is preserved.
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
-      checkMethodIsPresent(companionClass, getter);
+      checkMethodIsKept(companionClass, getter);
 
       // We should always inline the static accessor method.
       MemberNaming.MethodSignature getterAccessor =
           testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
-      checkMethodIsAbsent(outerClass, getterAccessor);
+      checkMethodIsRemoved(companionClass, getterAccessor);
 
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
@@ -316,7 +319,7 @@
         "noUseOfPropertyAccessorFromInnerClass");
     runTest("accessors", mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject classSubject = checkClassIsKept(dexInspector, testedClass.getClassName());
 
       for (String propertyName : testedClass.properties.keySet()) {
         MemberNaming.MethodSignature getterAccessor =
@@ -324,8 +327,8 @@
         MemberNaming.MethodSignature setterAccessor =
             testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
 
-        checkMethodIsAbsent(classSubject, getterAccessor);
-        checkMethodIsAbsent(classSubject, setterAccessor);
+        checkMethodIsRemoved(classSubject, getterAccessor);
+        checkMethodIsRemoved(classSubject, setterAccessor);
       }
     });
   }
@@ -338,10 +341,10 @@
         "usePrivatePropertyAccessorFromInnerClass");
     runTest("accessors", mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject classSubject = checkClassIsKept(dexInspector, testedClass.getClassName());
 
       String propertyName = "privateProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING,
+      FieldSubject fieldSubject = checkFieldIsKept(classSubject, JAVA_LANG_STRING,
           propertyName);
       assertFalse(fieldSubject.getField().accessFlags.isStatic());
 
@@ -351,12 +354,12 @@
           testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(classSubject, getterAccessor);
-        checkMethodIsAbsent(classSubject, setterAccessor);
+        checkMethodIsRemoved(classSubject, getterAccessor);
+        checkMethodIsRemoved(classSubject, setterAccessor);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(classSubject, getterAccessor);
-        checkMethodIsPresent(classSubject, setterAccessor);
+        checkMethodIsKept(classSubject, getterAccessor);
+        checkMethodIsKept(classSubject, setterAccessor);
       }
     });
   }
@@ -369,10 +372,10 @@
         "usePrivateLateInitPropertyAccessorFromInnerClass");
     runTest("accessors", mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject classSubject = checkClassIsKept(dexInspector, testedClass.getClassName());
 
       String propertyName = "privateLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING,
+      FieldSubject fieldSubject = checkFieldIsKept(classSubject, JAVA_LANG_STRING,
           propertyName);
       assertFalse(fieldSubject.getField().accessFlags.isStatic());
 
@@ -382,12 +385,12 @@
           testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(classSubject, getterAccessor);
-        checkMethodIsAbsent(classSubject, setterAccessor);
+        checkMethodIsRemoved(classSubject, getterAccessor);
+        checkMethodIsRemoved(classSubject, setterAccessor);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(classSubject, getterAccessor);
-        checkMethodIsPresent(classSubject, setterAccessor);
+        checkMethodIsKept(classSubject, getterAccessor);
+        checkMethodIsKept(classSubject, setterAccessor);
       }
     });
   }
@@ -400,7 +403,7 @@
         "noUseOfPropertyAccessorFromLambda");
     runTest("accessors", mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject classSubject = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "property";
 
       MemberNaming.MethodSignature getterAccessor =
@@ -408,8 +411,8 @@
       MemberNaming.MethodSignature setterAccessor =
           testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
 
-      checkMethodIsAbsent(classSubject, getterAccessor);
-      checkMethodIsAbsent(classSubject, setterAccessor);
+      checkMethodIsRemoved(classSubject, getterAccessor);
+      checkMethodIsRemoved(classSubject, setterAccessor);
     });
   }
 
@@ -421,9 +424,9 @@
         "usePropertyAccessorFromLambda");
     runTest("accessors", mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject classSubject = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "property";
-      FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
       assertFalse(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getterAccessor =
@@ -432,12 +435,12 @@
           testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(classSubject, getterAccessor);
-        checkMethodIsAbsent(classSubject, setterAccessor);
+        checkMethodIsRemoved(classSubject, getterAccessor);
+        checkMethodIsRemoved(classSubject, setterAccessor);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(classSubject, getterAccessor);
-        checkMethodIsPresent(classSubject, setterAccessor);
+        checkMethodIsKept(classSubject, getterAccessor);
+        checkMethodIsKept(classSubject, setterAccessor);
       }
     });
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index 29c5582..c8c8aec 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -41,23 +41,23 @@
     final String extraRules = keepClassMethod(mainClassName, testMethodSignature);
     runTest("dataclass", mainClassName, extraRules, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject dataClass = checkClassExists(dexInspector, TEST_DATA_CLASS.getClassName());
+      ClassSubject dataClass = checkClassIsKept(dexInspector, TEST_DATA_CLASS.getClassName());
 
       // Getters should be removed after inlining, which is possible only if access is relaxed.
       final boolean areGetterPresent = !allowAccessModification;
-      checkMethod(dataClass, NAME_GETTER_METHOD, areGetterPresent);
-      checkMethod(dataClass, AGE_GETTER_METHOD, areGetterPresent);
+      checkMethodisKeptOrRemoved(dataClass, NAME_GETTER_METHOD, areGetterPresent);
+      checkMethodisKeptOrRemoved(dataClass, AGE_GETTER_METHOD, areGetterPresent);
 
       // No use of componentN functions.
-      checkMethodIsAbsent(dataClass, COMPONENT1_METHOD);
-      checkMethodIsAbsent(dataClass, COMPONENT2_METHOD);
+      checkMethodIsRemoved(dataClass, COMPONENT1_METHOD);
+      checkMethodIsRemoved(dataClass, COMPONENT2_METHOD);
 
       // No use of copy functions.
-      checkMethodIsAbsent(dataClass, COPY_METHOD);
-      checkMethodIsAbsent(dataClass, COPY_DEFAULT_METHOD);
+      checkMethodIsRemoved(dataClass, COPY_METHOD);
+      checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
 
-      ClassSubject classSubject = checkClassExists(dexInspector, mainClassName);
-      MethodSubject testMethod = checkMethodIsPresent(classSubject, testMethodSignature);
+      ClassSubject classSubject = checkClassIsKept(dexInspector, mainClassName);
+      MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature);
       DexCode dexCode = getDexCode(testMethod);
       if (allowAccessModification) {
         // Both getters should be inlined
@@ -76,24 +76,24 @@
     final String extraRules = keepClassMethod(mainClassName, testMethodSignature);
     runTest("dataclass", mainClassName, extraRules, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject dataClass = checkClassExists(dexInspector, TEST_DATA_CLASS.getClassName());
+      ClassSubject dataClass = checkClassIsKept(dexInspector, TEST_DATA_CLASS.getClassName());
 
       // ComponentN functions should be removed after inlining, which is possible only if access
       // is relaxed.
       final boolean areComponentMethodsPresent = !allowAccessModification;
-      checkMethod(dataClass, COMPONENT1_METHOD, areComponentMethodsPresent);
-      checkMethod(dataClass, COMPONENT2_METHOD, areComponentMethodsPresent);
+      checkMethodisKeptOrRemoved(dataClass, COMPONENT1_METHOD, areComponentMethodsPresent);
+      checkMethodisKeptOrRemoved(dataClass, COMPONENT2_METHOD, areComponentMethodsPresent);
 
       // No use of getter.
-      checkMethodIsAbsent(dataClass, NAME_GETTER_METHOD);
-      checkMethodIsAbsent(dataClass, AGE_GETTER_METHOD);
+      checkMethodIsRemoved(dataClass, NAME_GETTER_METHOD);
+      checkMethodIsRemoved(dataClass, AGE_GETTER_METHOD);
 
       // No use of copy functions.
-      checkMethodIsAbsent(dataClass, COPY_METHOD);
-      checkMethodIsAbsent(dataClass, COPY_DEFAULT_METHOD);
+      checkMethodIsRemoved(dataClass, COPY_METHOD);
+      checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
 
-      ClassSubject classSubject = checkClassExists(dexInspector, mainClassName);
-      MethodSubject testMethod = checkMethodIsPresent(classSubject, testMethodSignature);
+      ClassSubject classSubject = checkClassIsKept(dexInspector, mainClassName);
+      MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature);
       DexCode dexCode = getDexCode(testMethod);
       if (allowAccessModification) {
         checkMethodIsNeverInvoked(dexCode, COMPONENT1_METHOD, COMPONENT2_METHOD);
@@ -111,24 +111,24 @@
     final String extraRules = keepClassMethod(mainClassName, testMethodSignature);
     runTest("dataclass", mainClassName, extraRules, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject dataClass = checkClassExists(dexInspector, TEST_DATA_CLASS.getClassName());
+      ClassSubject dataClass = checkClassIsKept(dexInspector, TEST_DATA_CLASS.getClassName());
 
       boolean component2IsPresent = !allowAccessModification;
-      checkMethod(dataClass, COMPONENT2_METHOD, component2IsPresent);
+      checkMethodisKeptOrRemoved(dataClass, COMPONENT2_METHOD, component2IsPresent);
 
       // Function component1 is not used.
-      checkMethodIsAbsent(dataClass, COMPONENT1_METHOD);
+      checkMethodIsRemoved(dataClass, COMPONENT1_METHOD);
 
       // No use of getter.
-      checkMethodIsAbsent(dataClass, NAME_GETTER_METHOD);
-      checkMethodIsAbsent(dataClass, AGE_GETTER_METHOD);
+      checkMethodIsRemoved(dataClass, NAME_GETTER_METHOD);
+      checkMethodIsRemoved(dataClass, AGE_GETTER_METHOD);
 
       // No use of copy functions.
-      checkMethodIsAbsent(dataClass, COPY_METHOD);
-      checkMethodIsAbsent(dataClass, COPY_DEFAULT_METHOD);
+      checkMethodIsRemoved(dataClass, COPY_METHOD);
+      checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
 
-      ClassSubject classSubject = checkClassExists(dexInspector, mainClassName);
-      MethodSubject testMethod = checkMethodIsPresent(classSubject, testMethodSignature);
+      ClassSubject classSubject = checkClassIsKept(dexInspector, mainClassName);
+      MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature);
       DexCode dexCode = getDexCode(testMethod);
       if (allowAccessModification) {
         checkMethodIsNeverInvoked(dexCode, COMPONENT2_METHOD);
@@ -146,10 +146,10 @@
     final String extraRules = keepClassMethod(mainClassName, testMethodSignature);
     runTest("dataclass", mainClassName, extraRules, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject dataClass = checkClassExists(dexInspector, TEST_DATA_CLASS.getClassName());
+      ClassSubject dataClass = checkClassIsKept(dexInspector, TEST_DATA_CLASS.getClassName());
 
-      checkMethodIsAbsent(dataClass, COPY_METHOD);
-      checkMethodIsAbsent(dataClass, COPY_DEFAULT_METHOD);
+      checkMethodIsRemoved(dataClass, COPY_METHOD);
+      checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
     });
   }
 
@@ -161,9 +161,9 @@
     final String extraRules = keepClassMethod(mainClassName, testMethodSignature);
     runTest("dataclass", mainClassName, extraRules, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject dataClass = checkClassExists(dexInspector, TEST_DATA_CLASS.getClassName());
+      ClassSubject dataClass = checkClassIsKept(dexInspector, TEST_DATA_CLASS.getClassName());
 
-      checkMethodIsAbsent(dataClass, COPY_DEFAULT_METHOD);
+      checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
     });
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
index 8fef44c..021e045 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -28,7 +28,7 @@
 
     runTest("intrinsics", "intrinsics.IntrinsicsKt", extraRules, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject intrinsicsClass = checkClassExists(
+      ClassSubject intrinsicsClass = checkClassIsKept(
           dexInspector, KOTLIN_INTRINSICS_CLASS.getClassName());
 
       checkMethodsPresence(intrinsicsClass,
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index f884376..74e3d5e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.kotlin.TestKotlinClass.KotlinProperty;
 import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
 import com.android.tools.r8.naming.MemberNaming;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -13,6 +14,7 @@
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.DexInspector.FieldSubject;
 import com.android.tools.r8.utils.InternalOptions;
+import java.util.Map.Entry;
 import java.util.function.Consumer;
 import org.junit.Test;
 
@@ -89,13 +91,19 @@
         "mutableProperty_noUseOfProperties");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           MUTABLE_PROPERTY_CLASS.getClassName());
-      for (String propertyName : MUTABLE_PROPERTY_CLASS.properties.keySet()) {
-        checkMethodIsAbsent(classSubject,
-            MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName));
-        checkMethodIsAbsent(classSubject,
-            MUTABLE_PROPERTY_CLASS.getSetterForProperty(propertyName));
+      for (Entry<String, KotlinProperty> property : MUTABLE_PROPERTY_CLASS.properties.entrySet()) {
+        MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(property.getKey());
+        MethodSignature setter = MUTABLE_PROPERTY_CLASS.getSetterForProperty(property.getKey());
+        if (property.getValue().getVisibility() == Visibility.PRIVATE) {
+          // Private properties have no getter/setter
+          checkMethodIsAbsent(classSubject, getter);
+          checkMethodIsAbsent(classSubject, setter);
+        } else {
+          checkMethodIsRemoved(classSubject, getter);
+          checkMethodIsRemoved(classSubject, setter);
+        }
       }
     });
   }
@@ -106,10 +114,10 @@
         "mutableProperty_usePrivateProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           MUTABLE_PROPERTY_CLASS.getClassName());
       String propertyName = "privateProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
       if (!allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
       } else {
@@ -128,19 +136,19 @@
         "mutableProperty_useProtectedProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           MUTABLE_PROPERTY_CLASS.getClassName());
       String propertyName = "protectedProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
 
       // Protected property has private field.
       MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(classSubject, getter);
+        checkMethodIsRemoved(classSubject, getter);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(classSubject, getter);
+        checkMethodIsKept(classSubject, getter);
       }
     });
   }
@@ -151,19 +159,19 @@
         "mutableProperty_useInternalProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           MUTABLE_PROPERTY_CLASS.getClassName());
       String propertyName = "internalProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
 
       // Internal property has private field
       MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(classSubject, getter);
+        checkMethodIsRemoved(classSubject, getter);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(classSubject, getter);
+        checkMethodIsKept(classSubject, getter);
       }
     });
   }
@@ -174,19 +182,19 @@
         "mutableProperty_usePublicProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           MUTABLE_PROPERTY_CLASS.getClassName());
       String propertyName = "publicProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
 
       // Public property has private field
       MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(classSubject, getter);
+        checkMethodIsRemoved(classSubject, getter);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(classSubject, getter);
+        checkMethodIsKept(classSubject, getter);
       }
     });
   }
@@ -197,21 +205,21 @@
         "mutableProperty_usePrimitiveProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           MUTABLE_PROPERTY_CLASS.getClassName());
       String propertyName = "primitiveProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(classSubject, "int", propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(classSubject, "int", propertyName);
 
       MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
       MethodSignature setter = MUTABLE_PROPERTY_CLASS.getSetterForProperty(propertyName);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(classSubject, getter);
-        checkMethodIsAbsent(classSubject, setter);
+        checkMethodIsRemoved(classSubject, getter);
+        checkMethodIsRemoved(classSubject, setter);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(classSubject, getter);
-        checkMethodIsPresent(classSubject, setter);
+        checkMethodIsKept(classSubject, getter);
+        checkMethodIsKept(classSubject, setter);
       }
     });
   }
@@ -222,13 +230,20 @@
         "lateInitProperty_noUseOfProperties");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           LATE_INIT_PROPERTY_CLASS.getClassName());
-      for (String propertyName : LATE_INIT_PROPERTY_CLASS.properties.keySet()) {
-        checkMethodIsAbsent(classSubject,
-            LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
-        checkMethodIsAbsent(classSubject,
-            LATE_INIT_PROPERTY_CLASS.getSetterForProperty(propertyName));
+      for (Entry<String, KotlinProperty> property : LATE_INIT_PROPERTY_CLASS.properties.entrySet()) {
+        MethodSignature getter = LATE_INIT_PROPERTY_CLASS.getGetterForProperty(property.getKey());
+        MethodSignature setter = LATE_INIT_PROPERTY_CLASS.getSetterForProperty(property.getKey());
+        if (property.getValue().getVisibility() == Visibility.PRIVATE) {
+          // Private properties have no getter or setter.
+          checkMethodIsAbsent(classSubject, getter);
+          checkMethodIsAbsent(classSubject, setter);
+
+        } else {
+          checkMethodIsRemoved(classSubject, getter);
+          checkMethodIsRemoved(classSubject, setter);
+        }
       }
     });
   }
@@ -239,7 +254,7 @@
         "properties/LateInitPropertyKt", "lateInitProperty_usePrivateLateInitProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           LATE_INIT_PROPERTY_CLASS.getClassName());
       String propertyName = "privateLateInitProp";
       FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
@@ -262,7 +277,7 @@
         "lateInitProperty_useProtectedLateInitProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           LATE_INIT_PROPERTY_CLASS.getClassName());
       String propertyName = "protectedLateInitProp";
       FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
@@ -272,7 +287,7 @@
       }
 
       // Protected late init property have protected getter
-      checkMethodIsAbsent(classSubject,
+      checkMethodIsRemoved(classSubject,
           LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
     });
   }
@@ -283,7 +298,7 @@
         "properties/LateInitPropertyKt", "lateInitProperty_useInternalLateInitProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           LATE_INIT_PROPERTY_CLASS.getClassName());
       String propertyName = "internalLateInitProp";
       FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
@@ -291,7 +306,7 @@
       assertTrue(fieldSubject.getField().accessFlags.isPublic());
 
       // Internal late init property have protected getter
-      checkMethodIsAbsent(classSubject,
+      checkMethodIsRemoved(classSubject,
           LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
     });
   }
@@ -302,7 +317,7 @@
         "properties/LateInitPropertyKt", "lateInitProperty_usePublicLateInitProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           LATE_INIT_PROPERTY_CLASS.getClassName());
       String propertyName = "publicLateInitProp";
       FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
@@ -310,7 +325,7 @@
       assertTrue(fieldSubject.getField().accessFlags.isPublic());
 
       // Internal late init property have protected getter
-      checkMethodIsAbsent(classSubject,
+      checkMethodIsRemoved(classSubject,
           LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
     });
   }
@@ -321,12 +336,12 @@
         "properties/UserDefinedPropertyKt", "userDefinedProperty_noUseOfProperties");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           USER_DEFINED_PROPERTY_CLASS.getClassName());
       for (String propertyName : USER_DEFINED_PROPERTY_CLASS.properties.keySet()) {
-        checkMethodIsAbsent(classSubject,
+        checkMethodIsRemoved(classSubject,
             USER_DEFINED_PROPERTY_CLASS.getGetterForProperty(propertyName));
-        checkMethodIsAbsent(classSubject,
+        checkMethodIsRemoved(classSubject,
             USER_DEFINED_PROPERTY_CLASS.getSetterForProperty(propertyName));
       }
     });
@@ -338,22 +353,22 @@
         "properties/UserDefinedPropertyKt", "userDefinedProperty_useProperties");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject classSubject = checkClassExists(dexInspector,
+      ClassSubject classSubject = checkClassIsKept(dexInspector,
           USER_DEFINED_PROPERTY_CLASS.getClassName());
       String propertyName = "durationInSeconds";
       // The 'wrapper' property is not assigned to a backing field, it only relies on the wrapped
       // property.
       checkFieldIsAbsent(classSubject, "int", "durationInSeconds");
 
-      FieldSubject fieldSubject = checkFieldIsPresent(classSubject, "int",
+      FieldSubject fieldSubject = checkFieldIsKept(classSubject, "int",
           "durationInMilliSeconds");
       MethodSignature getter = USER_DEFINED_PROPERTY_CLASS.getGetterForProperty(propertyName);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(classSubject, getter);
+        checkMethodIsRemoved(classSubject, getter);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(classSubject, getter);
+        checkMethodIsKept(classSubject, getter);
       }
     });
   }
@@ -364,12 +379,12 @@
         "properties.CompanionPropertiesKt", "companionProperties_usePrimitiveProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector,
+      ClassSubject outerClass = checkClassIsKept(dexInspector,
           "properties.CompanionProperties");
-      ClassSubject companionClass = checkClassExists(dexInspector,
+      ClassSubject companionClass = checkClassIsKept(dexInspector,
           COMPANION_PROPERTY_CLASS.getClassName());
       String propertyName = "primitiveProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, "int", propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, "int", propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = COMPANION_PROPERTY_CLASS
@@ -379,8 +394,8 @@
 
       // Getter and setter cannot be inlined because we don't know if null check semantic is
       // preserved.
-      checkMethodIsPresent(companionClass, getter);
-      checkMethodIsPresent(companionClass, setter);
+      checkMethodIsKept(companionClass, getter);
+      checkMethodIsKept(companionClass, setter);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
       } else {
@@ -395,12 +410,12 @@
         "properties.CompanionPropertiesKt", "companionProperties_usePrivateProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector,
+      ClassSubject outerClass = checkClassIsKept(dexInspector,
           "properties.CompanionProperties");
-      ClassSubject companionClass = checkClassExists(dexInspector,
+      ClassSubject companionClass = checkClassIsKept(dexInspector,
           COMPANION_PROPERTY_CLASS.getClassName());
       String propertyName = "privateProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = COMPANION_PROPERTY_CLASS
@@ -413,8 +428,8 @@
       // non-null, thus the getter/setter can be inlined if their code is small enough.
       // Because the backing field is private, they will call into an accessor (static) method. If
       // access relaxation is enabled, this accessor can be removed.
-      checkMethodIsAbsent(companionClass, getter);
-      checkMethodIsAbsent(companionClass, setter);
+      checkMethodIsRemoved(companionClass, getter);
+      checkMethodIsRemoved(companionClass, setter);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
       } else {
@@ -429,12 +444,12 @@
         "properties.CompanionPropertiesKt", "companionProperties_useInternalProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector,
+      ClassSubject outerClass = checkClassIsKept(dexInspector,
           "properties.CompanionProperties");
-      ClassSubject companionClass = checkClassExists(dexInspector,
+      ClassSubject companionClass = checkClassIsKept(dexInspector,
           COMPANION_PROPERTY_CLASS.getClassName());
       String propertyName = "internalProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = COMPANION_PROPERTY_CLASS
@@ -444,8 +459,8 @@
 
       // Getter and setter cannot be inlined because we don't know if null check semantic is
       // preserved.
-      checkMethodIsPresent(companionClass, getter);
-      checkMethodIsPresent(companionClass, setter);
+      checkMethodIsKept(companionClass, getter);
+      checkMethodIsKept(companionClass, setter);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
       } else {
@@ -460,12 +475,12 @@
         "properties.CompanionPropertiesKt", "companionProperties_usePublicProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector,
+      ClassSubject outerClass = checkClassIsKept(dexInspector,
           "properties.CompanionProperties");
-      ClassSubject companionClass = checkClassExists(dexInspector,
+      ClassSubject companionClass = checkClassIsKept(dexInspector,
           COMPANION_PROPERTY_CLASS.getClassName());
       String propertyName = "publicProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = COMPANION_PROPERTY_CLASS
@@ -475,8 +490,8 @@
 
       // Getter and setter cannot be inlined because we don't know if null check semantic is
       // preserved.
-      checkMethodIsPresent(companionClass, getter);
-      checkMethodIsPresent(companionClass, setter);
+      checkMethodIsKept(companionClass, getter);
+      checkMethodIsKept(companionClass, setter);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
       } else {
@@ -492,10 +507,10 @@
         "companionLateInitProperties_usePrivateLateInitProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
-      ClassSubject companionClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
+      ClassSubject companionClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "privateLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -506,8 +521,8 @@
       // non-null, thus the getter/setter can be inlined if their code is small enough.
       // Because the backing field is private, they will call into an accessor (static) method. If
       // access relaxation is enabled, this accessor can be removed.
-      checkMethodIsAbsent(companionClass, getter);
-      checkMethodIsAbsent(companionClass, setter);
+      checkMethodIsRemoved(companionClass, getter);
+      checkMethodIsRemoved(companionClass, setter);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
       } else {
@@ -523,10 +538,10 @@
         "companionLateInitProperties_useInternalLateInitProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
-      ClassSubject companionClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
+      ClassSubject companionClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "internalLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -534,8 +549,8 @@
 
       // Getter and setter cannot be inlined because we don't know if null check semantic is
       // preserved.
-      checkMethodIsPresent(companionClass, getter);
-      checkMethodIsPresent(companionClass, setter);
+      checkMethodIsKept(companionClass, getter);
+      checkMethodIsKept(companionClass, setter);
       assertTrue(fieldSubject.getField().accessFlags.isPublic());
     });
   }
@@ -547,10 +562,10 @@
         "companionLateInitProperties_usePublicLateInitProp");
     runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
-      ClassSubject companionClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
+      ClassSubject companionClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "publicLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -558,8 +573,8 @@
 
       // Getter and setter cannot be inlined because we don't know if null check semantic is
       // preserved.
-      checkMethodIsPresent(companionClass, getter);
-      checkMethodIsPresent(companionClass, setter);
+      checkMethodIsKept(companionClass, getter);
+      checkMethodIsKept(companionClass, setter);
       assertTrue(fieldSubject.getField().accessFlags.isPublic());
     });
   }
@@ -571,9 +586,9 @@
         "properties.ObjectPropertiesKt", "objectProperties_usePrimitiveProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "primitiveProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, "int", propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, "int", propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -581,8 +596,8 @@
 
       // Getter and setter cannot be inlined when we don't know if null check semantic is
       // preserved.
-      checkMethodIsPresent(objectClass, getter);
-      checkMethodIsPresent(objectClass, setter);
+      checkMethodIsKept(objectClass, getter);
+      checkMethodIsKept(objectClass, setter);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
       } else {
@@ -598,9 +613,9 @@
         "properties.ObjectPropertiesKt", "objectProperties_usePrivateProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "privateProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -625,9 +640,9 @@
         "properties.ObjectPropertiesKt", "objectProperties_useInternalProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "internalProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -635,8 +650,8 @@
 
       // Getter and setter cannot be inlined when we don't know if null check semantic is
       // preserved.
-      checkMethodIsPresent(objectClass, getter);
-      checkMethodIsPresent(objectClass, setter);
+      checkMethodIsKept(objectClass, getter);
+      checkMethodIsKept(objectClass, setter);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
       } else {
@@ -652,9 +667,9 @@
         "properties.ObjectPropertiesKt", "objectProperties_usePublicProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "publicProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -662,8 +677,8 @@
 
       // Getter and setter cannot be inlined when we don't know if null check semantic is
       // preserved.
-      checkMethodIsPresent(objectClass, getter);
-      checkMethodIsPresent(objectClass, setter);
+      checkMethodIsKept(objectClass, getter);
+      checkMethodIsKept(objectClass, setter);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
       } else {
@@ -679,9 +694,9 @@
         "properties.ObjectPropertiesKt", "objectProperties_useLateInitPrivateProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "privateLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -706,9 +721,9 @@
         "properties.ObjectPropertiesKt", "objectProperties_useLateInitInternalProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "internalLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -716,8 +731,8 @@
 
       // Getter and setter cannot be inlined when we don't know if null check semantic is
       // preserved.
-      checkMethodIsPresent(objectClass, getter);
-      checkMethodIsPresent(objectClass, setter);
+      checkMethodIsKept(objectClass, getter);
+      checkMethodIsKept(objectClass, setter);
       assertTrue(fieldSubject.getField().accessFlags.isPublic());
     });
   }
@@ -729,9 +744,9 @@
         "properties.ObjectPropertiesKt", "objectProperties_useLateInitPublicProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "publicLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -739,8 +754,8 @@
 
       // Getter and setter cannot be inlined when we don't know if null check semantic is
       // preserved.
-      checkMethodIsPresent(objectClass, getter);
-      checkMethodIsPresent(objectClass, setter);
+      checkMethodIsKept(objectClass, getter);
+      checkMethodIsKept(objectClass, setter);
       assertTrue(fieldSubject.getField().accessFlags.isPublic());
     });
   }
@@ -752,9 +767,9 @@
         "properties.FilePropertiesKt", "fileProperties_usePrimitiveProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "primitiveProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, "int", propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, "int", propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -762,12 +777,12 @@
 
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(objectClass, getter);
-        checkMethodIsAbsent(objectClass, setter);
+        checkMethodIsRemoved(objectClass, getter);
+        checkMethodIsRemoved(objectClass, setter);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(objectClass, getter);
-        checkMethodIsPresent(objectClass, setter);
+        checkMethodIsKept(objectClass, getter);
+        checkMethodIsKept(objectClass, setter);
       }
     });
   }
@@ -779,9 +794,9 @@
         "properties.FilePropertiesKt", "fileProperties_usePrivateProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "privateProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -805,9 +820,9 @@
         "properties.FilePropertiesKt", "fileProperties_useInternalProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "internalProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       // We expect getter to be inlined when access (of the backing field) is relaxed to public.
@@ -816,10 +831,10 @@
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(objectClass, getter);
+        checkMethodIsRemoved(objectClass, getter);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(objectClass, getter);
+        checkMethodIsKept(objectClass, getter);
       }
     });
   }
@@ -831,9 +846,9 @@
         "properties.FilePropertiesKt", "fileProperties_usePublicProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "publicProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       // We expect getter to be inlined when access (of the backing field) is relaxed to public.
@@ -843,10 +858,10 @@
 
       if (allowAccessModification) {
         assertTrue(fieldSubject.getField().accessFlags.isPublic());
-        checkMethodIsAbsent(objectClass, getter);
+        checkMethodIsRemoved(objectClass, getter);
       } else {
         assertTrue(fieldSubject.getField().accessFlags.isPrivate());
-        checkMethodIsPresent(objectClass, getter);
+        checkMethodIsKept(objectClass, getter);
       }
     });
   }
@@ -858,9 +873,9 @@
         "properties.FilePropertiesKt", "fileProperties_useLateInitPrivateProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject fileClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject fileClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "privateLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(fileClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(fileClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
@@ -884,9 +899,9 @@
         "properties.FilePropertiesKt", "fileProperties_useLateInitInternalProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "internalLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
       assertTrue(fieldSubject.getField().accessFlags.isPublic());
 
@@ -894,10 +909,10 @@
       MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
 
       // Field is public and getter is small so we expect to always inline it.
-      checkMethodIsAbsent(objectClass, getter);
+      checkMethodIsRemoved(objectClass, getter);
 
       // Setter has null check of new value, thus may not be inlined.
-      checkMethodIsPresent(objectClass, setter);
+      checkMethodIsKept(objectClass, setter);
     });
   }
 
@@ -908,16 +923,16 @@
         "properties.FilePropertiesKt", "fileProperties_useLateInitPublicProp");
     runTest(PACKAGE_NAME, mainClass, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject objectClass = checkClassExists(dexInspector, testedClass.getClassName());
+      ClassSubject objectClass = checkClassIsKept(dexInspector, testedClass.getClassName());
       String propertyName = "publicLateInitProp";
-      FieldSubject fieldSubject = checkFieldIsPresent(objectClass, JAVA_LANG_STRING, propertyName);
+      FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
       assertTrue(fieldSubject.getField().accessFlags.isStatic());
 
       MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
       MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
 
-      checkMethodIsAbsent(objectClass, getter);
-      checkMethodIsPresent(objectClass, setter);
+      checkMethodIsRemoved(objectClass, getter);
+      checkMethodIsKept(objectClass, setter);
       assertTrue(fieldSubject.getField().accessFlags.isPublic());
     });
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index 2b1c33f..5a30cfd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -36,9 +36,9 @@
     final String extraRules = keepAllMembers(mainClassName);
     runTest(FOLDER, mainClassName, extraRules, app -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject clazz = checkClassExists(dexInspector, ex1.getClassName());
+      ClassSubject clazz = checkClassIsKept(dexInspector, ex1.getClassName());
 
-      MethodSubject testMethod = checkMethodIsPresent(clazz, testMethodSignature);
+      MethodSubject testMethod = checkMethodIsKept(clazz, testMethodSignature);
       DexCode dexCode = getDexCode(testMethod);
       long count = Arrays.stream(dexCode.instructions)
           .filter(SimplifyIfNotNullKotlinTest::isIf).count();
@@ -62,9 +62,9 @@
     final String extraRules = keepAllMembers(mainClassName);
     runTest(FOLDER, mainClassName, extraRules, app -> {
       DexInspector dexInspector = new DexInspector(app);
-      ClassSubject clazz = checkClassExists(dexInspector, ex2.getClassName());
+      ClassSubject clazz = checkClassIsKept(dexInspector, ex2.getClassName());
 
-      MethodSubject testMethod = checkMethodIsPresent(clazz, testMethodSignature);
+      MethodSubject testMethod = checkMethodIsKept(clazz, testMethodSignature);
       DexCode dexCode = getDexCode(testMethod);
       long count = Arrays.stream(dexCode.instructions)
           .filter(SimplifyIfNotNullKotlinTest::isIf).count();
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 590baa6..4214a67 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -10,11 +10,12 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.StringResource;
@@ -23,9 +24,7 @@
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DebugLocalInfo;
@@ -54,9 +53,11 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AbortException;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
+import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
@@ -64,6 +65,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.MainDexList;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
@@ -102,6 +104,8 @@
   public static TemporaryFolder generatedApplicationsFolder =
       ToolHelper.getTemporaryFolderForTest();
 
+  private List<Diagnostic> errors = new ArrayList<>();
+
   // Generate the test applications in a @BeforeClass method, as they are used by several tests.
   @BeforeClass
   public static void generateTestApplications() throws Throwable {
@@ -179,12 +183,15 @@
     try {
       verifyMainDexContains(TWO_LARGE_CLASSES, getTwoLargeClassesAppPath(), false);
       fail("Expect to fail, for there are too many classes for the main-dex list.");
-    } catch (DexOverflowException e) {
+    } catch (AbortException e) {
+      assertEquals(1, errors.size());
+      String message = errors.get(0).getDiagnosticMessage();
       // Make sure {@link MonoDexDistributor} was _not_ used.
-      assertFalse(e.getMessage().contains("single dex file"));
+      assertFalse(message.contains("single dex file"));
       // Make sure what exceeds the limit is the number of methods.
-      assertTrue(e.getMessage().contains("# methods: "
-          + String.valueOf(TWO_LARGE_CLASSES.size() * MAX_METHOD_COUNT)));
+      assertTrue(
+          message.contains(
+              "# methods: " + String.valueOf(TWO_LARGE_CLASSES.size() * MAX_METHOD_COUNT)));
     }
   }
 
@@ -220,12 +227,16 @@
     try {
       verifyMainDexContains(MANY_CLASSES, getManyClassesMultiDexAppPath(), false);
       fail("Expect to fail, for there are too many classes for the main-dex list.");
-    } catch (DexOverflowException e) {
+    } catch (AbortException e) {
+      assertEquals(1, errors.size());
+      String message = errors.get(0).getDiagnosticMessage();
       // Make sure {@link MonoDexDistributor} was _not_ used.
-      assertFalse(e.getMessage().contains("single dex file"));
+      assertFalse(message.contains("single dex file"));
       // Make sure what exceeds the limit is the number of methods.
-      assertTrue(e.getMessage().contains("# methods: "
-          + String.valueOf(MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS)));
+      assertTrue(
+          message.contains(
+              "# methods: "
+                  + String.valueOf(MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS)));
     }
   }
 
@@ -444,15 +455,22 @@
     // Notice that this one fails due to the min API.
     try {
       generateApplication(
-          MANY_CLASSES, AndroidApiLevel.K.getLevel(), false,
-          MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS);
+          MANY_CLASSES,
+          AndroidApiLevel.K.getLevel(),
+          false,
+          MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS,
+          new TestDiagnosticsHandler());
       fail("Expect to fail, for there are many classes while multidex is not enabled.");
-    } catch (DexOverflowException e) {
+    } catch (AbortException e) {
+      assertEquals(1, errors.size());
+      String message = errors.get(0).getDiagnosticMessage();
       // Make sure {@link MonoDexDistributor} was used.
-      assertTrue(e.getMessage().contains("single dex file"));
+      assertTrue(message.contains("single dex file"));
       // Make sure what exceeds the limit is the number of methods.
-      assertTrue(e.getMessage().contains("# methods: "
-          + String.valueOf(MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS)));
+      assertTrue(
+          message.contains(
+              "# methods: "
+                  + String.valueOf(MANY_CLASSES_COUNT * MANY_CLASSES_MULTI_DEX_METHODS_PER_CLASS)));
     }
   }
 
@@ -498,7 +516,7 @@
   private void doVerifyMainDexContains(
       List<String> mainDex, Path app, boolean singleDexApp, boolean minimalMainDex,
       MultiDexTestMode testMode)
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException,
+      throws IOException, ExecutionException, ProguardRuleParserException,
       CompilationFailedException {
     AndroidApp originalApp = AndroidApp.builder().addProgramFiles(app).build();
     DexInspector originalInspector = new DexInspector(originalApp);
@@ -508,7 +526,7 @@
     }
     Path outDir = temp.newFolder().toPath();
     R8Command.Builder builder =
-        R8Command.builder()
+        R8Command.builder(new TestDiagnosticsHandler())
             .addProgramFiles(app)
             .setMode(
                 minimalMainDex && mainDex.size() > 0
@@ -587,15 +605,27 @@
   }
 
   public static AndroidApp generateApplication(List<String> classes, int minApi, int methodCount)
-      throws IOException, ExecutionException, CompilationException {
+      throws IOException, ExecutionException {
     return generateApplication(classes, minApi, false, methodCount);
   }
 
   private static AndroidApp generateApplication(
       List<String> classes, int minApi, boolean intermediate, int methodCount)
-      throws IOException, ExecutionException, CompilationException {
+      throws IOException, ExecutionException {
+    return generateApplication(
+        classes, minApi, intermediate, methodCount, new DefaultDiagnosticsHandler());
+  }
+
+  private static AndroidApp generateApplication(
+      List<String> classes,
+      int minApi,
+      boolean intermediate,
+      int methodCount,
+      DiagnosticsHandler diagnosticsHandler)
+      throws IOException, ExecutionException {
     Timing timing = new Timing("MainDexListTests");
-    InternalOptions options = new InternalOptions();
+    InternalOptions options =
+        new InternalOptions(new DexItemFactory(), new Reporter(diagnosticsHandler));
     options.minApiLevel = minApi;
     options.intermediate = intermediate;
     DexItemFactory factory = options.itemFactory;
@@ -765,4 +795,12 @@
       throw new Unreachable();
     }
   }
+
+  private class TestDiagnosticsHandler implements DiagnosticsHandler {
+
+    @Override
+    public void error(Diagnostic error) {
+      errors.add(error);
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
index deb90e4..38af4de 100644
--- a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
@@ -9,7 +9,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8Command;
@@ -270,8 +269,7 @@
   }
 
   private R8Command.Builder getCommandForInstrumentation(
-      Path out, Path flag, Path mainApp, Path instrApp)
-      throws CompilationException, IOException {
+      Path out, Path flag, Path mainApp, Path instrApp) throws IOException {
     return R8Command.builder()
         .addLibraryFiles(ToolHelper.getDefaultAndroidJar(), mainApp)
         .addProgramFiles(instrApp)
@@ -279,9 +277,8 @@
         .addProguardConfigurationFiles(flag);
   }
 
-  private R8Command.Builder getCommandForApps(
-      Path out, Path flag, Path... jars)
-      throws CompilationException, IOException {
+  private R8Command.Builder getCommandForApps(Path out, Path flag, Path... jars)
+      throws IOException {
     return R8Command.builder()
         .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
         .addProgramFiles(jars)
@@ -290,7 +287,7 @@
   }
 
   private static AndroidApp runR8(R8Command command)
-      throws ProguardRuleParserException, ExecutionException, CompilationException, IOException {
+      throws ProguardRuleParserException, ExecutionException, IOException {
     return ToolHelper.runR8(command, options -> {
       // Disable inlining to make this test not depend on inlining decisions.
       options.enableInlining = false;
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index b60ede7..d9808c0 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.ClassAndMemberPublicizer;
@@ -65,7 +64,7 @@
   }
 
   NamingLens runMinifier(List<Path> configPaths)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+      throws IOException, ProguardRuleParserException, ExecutionException {
     ProguardConfiguration configuration =
         ToolHelper.loadProguardConfiguration(dexItemFactory, configPaths);
     InternalOptions options = new InternalOptions(configuration,
diff --git a/src/test/java/com/android/tools/r8/naming/b80083341/B80083341.java b/src/test/java/com/android/tools/r8/naming/b80083341/B80083341.java
index 1a1ae50..008e4ff 100644
--- a/src/test/java/com/android/tools/r8/naming/b80083341/B80083341.java
+++ b/src/test/java/com/android/tools/r8/naming/b80083341/B80083341.java
@@ -16,14 +16,12 @@
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(VmTestRunner.class)
 public class B80083341 extends TestBase {
 
-  @Ignore("b/80083341")
   @Test
   public void test() throws Exception {
     Class mainClass = TestMain.class;
diff --git a/src/test/java/com/android/tools/r8/resource/DataResourceTest.java b/src/test/java/com/android/tools/r8/resource/DataResourceTest.java
index dbd4e23..2df0612 100644
--- a/src/test/java/com/android/tools/r8/resource/DataResourceTest.java
+++ b/src/test/java/com/android/tools/r8/resource/DataResourceTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.resource;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.R8Command;
@@ -25,8 +24,7 @@
   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   @Test
-  public void dataResourceTest()
-      throws IOException, CompilationFailedException, CompilationException {
+  public void dataResourceTest() throws IOException, CompilationFailedException {
     String packageName = "dataresource";
     String mainClassName = packageName + ".ResourceTest";
     Path inputJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR,
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
index e6c9ada..e0d9ecf 100644
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.rewrite.longcompare;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
@@ -35,7 +34,7 @@
   public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
 
   void compileWithD8(Path intputPath, Path outputPath)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     D8.run(
         D8Command.builder()
             .addProgramFiles(intputPath)
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
index fe74da9..a50a667 100644
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.rewrite.longcompare;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
@@ -34,7 +33,7 @@
   public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
 
   void compileWithD8(Path intputPath, Path outputPath, CompilationMode mode)
-      throws IOException, CompilationException, CompilationFailedException {
+      throws IOException, CompilationFailedException {
     D8.run(
         D8Command.builder()
             .setMode(mode)
@@ -68,7 +67,7 @@
   }
 
   private void runTest(CompilationMode mode)
-      throws IOException, CompilationException, ExecutionException, CompilationFailedException {
+      throws IOException, ExecutionException, CompilationFailedException {
     final Path inputPath = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/rewrite.jar");
     Path outputPath = tmpOutputDir.newFolder().toPath();
 
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
index 831d664..8f00f8d 100644
--- a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
@@ -117,7 +117,7 @@
     assertThat(javaResult.stdout, containsString(impl2.name));
 
     AndroidApp processedApp = compileWithR8(jasminBuilder.build(), proguardConfig,
-        // Disable inlining to avoid the (short) tested method from being inlined then removed.
+        // Disable inlining to avoid the (short) tested method from being inlined and then removed.
         internalOptions -> internalOptions.enableInlining = false);
 
     // Run processed (output) program on ART
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index f558ee7..c176ff3 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -149,6 +149,13 @@
     parser = new ProguardConfigurationParser(new DexItemFactory(), reporter);
   }
 
+  @Before
+  public void resetAllowPartiallyImplementedOptions() {
+    handler = new KeepingDiagnosticHandler();
+    reporter = new Reporter(handler);
+    parser = new ProguardConfigurationParser(new DexItemFactory(), reporter, false);
+  }
+
   @Test
   public void parse() throws Exception {
     ProguardConfigurationParser parser;
@@ -859,7 +866,7 @@
   @Test
   public void parseKeepdirectories() throws Exception {
     ProguardConfigurationParser parser =
-        new ProguardConfigurationParser(new DexItemFactory(), reporter);
+        new ProguardConfigurationParser(new DexItemFactory(), reporter, false);
     parser.parse(Paths.get(KEEPDIRECTORIES));
     verifyParserEndsCleanly();
   }
@@ -1216,6 +1223,7 @@
 
   @Test
   public void parse_adaptresourcexxx_keepdirectories_noArguments1() {
+    resetAllowPartiallyImplementedOptions();
     ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
         "-adaptresourcefilenames",
         "-adaptresourcefilecontents",
@@ -1228,6 +1236,7 @@
 
   @Test
   public void parse_adaptresourcexxx_keepdirectories_noArguments2() {
+    resetAllowPartiallyImplementedOptions();
     ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
         "-keepdirectories",
         "-adaptresourcefilenames",
@@ -1240,6 +1249,7 @@
 
   @Test
   public void parse_adaptresourcexxx_keepdirectories_noArguments3() {
+    resetAllowPartiallyImplementedOptions();
     ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
         "-adaptresourcefilecontents",
         "-keepdirectories",
@@ -1261,6 +1271,7 @@
 
   @Test
   public void parse_adaptresourcexxx_keepdirectories_singleArgument() {
+    resetAllowPartiallyImplementedOptions();
     ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
         "-adaptresourcefilenames " + FILE_FILTER_SINGLE,
         "-adaptresourcefilecontents " + FILE_FILTER_SINGLE,
@@ -1293,6 +1304,7 @@
 
   @Test
   public void parse_adaptresourcexxx_keepdirectories_multipleArgument() {
+    resetAllowPartiallyImplementedOptions();
     ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of(
         "-adaptresourcefilenames " + FILE_FILTER_MULTIPLE,
         "-adaptresourcefilecontents " + FILE_FILTER_MULTIPLE,
@@ -1309,7 +1321,7 @@
         "-adaptresourcefilenames", "-adaptresourcefilecontents", "-keepdirectories");
     for (String option : options) {
       try {
-        reset();
+        resetAllowPartiallyImplementedOptions();
         parser.parse(createConfigurationForTesting(ImmutableList.of(option + " ,")));
         fail("Expect to fail due to the lack of path filter.");
       } catch (AbortException e) {
@@ -1324,7 +1336,7 @@
         "-adaptresourcefilenames", "-adaptresourcefilecontents", "-keepdirectories");
     for (String option : options) {
       try {
-        reset();
+        resetAllowPartiallyImplementedOptions();
         parser.parse(createConfigurationForTesting(ImmutableList.of(option + " xxx,,yyy")));
         fail("Expect to fail due to the lack of path filter.");
       } catch (AbortException e) {
@@ -1339,7 +1351,7 @@
         "-adaptresourcefilenames", "-adaptresourcefilecontents", "-keepdirectories");
     for (String option : options) {
       try {
-        reset();
+        resetAllowPartiallyImplementedOptions();
         parser.parse(createConfigurationForTesting(ImmutableList.of(option + " xxx,")));
         fail("Expect to fail due to the lack of path filter.");
       } catch (AbortException e) {
@@ -1745,6 +1757,24 @@
     verifyWithProguard(proguardConfig);
   }
 
+  public void testNotSupported(String option) {
+    try {
+      reset();
+      parser.parse(createConfigurationForTesting(ImmutableList.of(option)));
+      fail("Expect to fail due to unsupported option.");
+    } catch (AbortException e) {
+      checkDiagnostic(handler.errors, null, 1, 1, "Option " + option + " currently not supported");
+    }
+  }
+
+  @Test
+  public void parse_pariallyImplemented_notSupported() {
+    testNotSupported("-keepdirectories");
+    testNotSupported("-adaptresourcefilenames");
+    testNotSupported("-adaptresourcefilecontents");
+  }
+
+
   private ProguardConfiguration parseAndVerifyParserEndsCleanly(List<String> config) {
     parser.parse(createConfigurationForTesting(config));
     verifyParserEndsCleanly();
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java b/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
new file mode 100644
index 0000000..7fcb318
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
@@ -0,0 +1,156 @@
+// Copyright (c) 2018, 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.shaking.examples;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.io.ByteStreams;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+
+public class InliningClassVersionTest extends TestBase {
+
+  private final int OLD_VERSION = Opcodes.V1_6;
+  private final String BASE_DESCRIPTOR = DescriptorUtils.javaTypeToDescriptor(Base.class.getName());
+
+  private static class Base {
+
+    public static void main(String[] args) {
+      System.out.println(Inlinee.foo());
+    }
+  }
+
+  private static class Inlinee {
+    public static String foo() {
+      return "Hello from Inlinee!";
+    }
+  }
+
+  private static class DowngradeVisitor extends ClassVisitor {
+
+    private final int version;
+
+    DowngradeVisitor(ClassVisitor cv, int version) {
+      super(Opcodes.ASM6, cv);
+      this.version = version;
+    }
+
+    @Override
+    public void visit(
+        int version,
+        int access,
+        String name,
+        String signature,
+        String superName,
+        String[] interfaces) {
+      assert version > this.version
+          : "Going from " + version + " to " + this.version + " is not a downgrade";
+      super.visit(this.version, access, name, signature, superName, interfaces);
+    }
+  }
+
+  private static byte[] downgradeClass(byte[] classBytes, int version) {
+    ClassWriter writer = new ClassWriter(0);
+    new ClassReader(classBytes).accept(new DowngradeVisitor(writer, version), 0);
+    return writer.toByteArray();
+  }
+
+  @Test
+  public void test() throws Exception {
+    Path inputJar = writeInput();
+    assertEquals(OLD_VERSION, getBaseClassVersion(inputJar));
+    ProcessResult runInput = run(inputJar);
+    assertEquals(0, runInput.exitCode);
+    Path outputJar = runR8(inputJar);
+    ProcessResult runOutput = run(outputJar);
+    assertEquals(runInput.toString(), runOutput.toString());
+    assertNotEquals(
+        "Inliner did not upgrade classfile version", OLD_VERSION, getBaseClassVersion(outputJar));
+  }
+
+  private int getBaseClassVersion(Path jar) throws Exception {
+    return getClassVersion(jar, BASE_DESCRIPTOR);
+  }
+
+  private int getClassVersion(Path jar, String descriptor) throws Exception {
+
+    class ClassVersionReader extends ClassVisitor {
+      private int version = -1;
+
+      private ClassVersionReader() {
+        super(Opcodes.ASM6);
+      }
+
+      @Override
+      public void visit(
+          int version,
+          int access,
+          String name,
+          String signature,
+          String superName,
+          String[] interfaces) {
+        super.visit(version, access, name, signature, superName, interfaces);
+        assert version != -1;
+        this.version = version;
+      }
+    }
+
+    byte[] bytes =
+        ByteStreams.toByteArray(
+            new ArchiveClassFileProvider(jar).getProgramResource(descriptor).getByteStream());
+    ClassVersionReader reader = new ClassVersionReader();
+    new ClassReader(bytes).accept(reader, 0);
+    assert reader.version != -1;
+    return reader.version;
+  }
+
+  private Path writeInput() throws Exception {
+    Path inputJar = temp.getRoot().toPath().resolve("input.jar");
+    ClassFileConsumer consumer = new ClassFileConsumer.ArchiveConsumer(inputJar);
+    consumer.accept(
+        downgradeClass(ToolHelper.getClassAsBytes(Base.class), OLD_VERSION), BASE_DESCRIPTOR, null);
+    consumer.accept(
+        ToolHelper.getClassAsBytes(Inlinee.class),
+        DescriptorUtils.javaTypeToDescriptor(Inlinee.class.getName()),
+        null);
+    consumer.finished(null);
+    return inputJar;
+  }
+
+  private ProcessResult run(Path jar) throws Exception {
+    return ToolHelper.runJava(jar, Base.class.getName());
+  }
+
+  private Path runR8(Path inputJar) throws Exception {
+    List<String> keepRule =
+        Collections.singletonList(
+            "-keep class " + Base.class.getName() + " { public static void main(...); }");
+    Path outputJar = temp.getRoot().toPath().resolve("output.jar");
+    ToolHelper.runR8(
+        R8Command.builder()
+            .addProgramFiles(inputJar)
+            .addLibraryFiles(Paths.get(ToolHelper.JAVA_8_RUNTIME))
+            .addProguardConfiguration(keepRule, Origin.unknown())
+            .setOutput(outputJar, OutputMode.ClassFile)
+            .build());
+    return outputJar;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java
new file mode 100644
index 0000000..a5b13a7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2018, 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.shaking.examples;
+
+import com.android.tools.r8.TestBase.MinifyMode;
+import com.android.tools.r8.shaking.TreeShakingTest;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TreeShakingAbstractMethodRemovalTest extends TreeShakingTest {
+
+  @Parameters(name = "mode:{0}-{1} minify:{2}")
+  public static Collection<Object[]> data() {
+    List<Object[]> parameters = new ArrayList<>();
+    for (MinifyMode minify : MinifyMode.values()) {
+      parameters.add(new Object[] {Frontend.JAR, Backend.CF, minify});
+      parameters.add(new Object[] {Frontend.JAR, Backend.DEX, minify});
+      parameters.add(new Object[] {Frontend.DEX, Backend.DEX, minify});
+    }
+    return parameters;
+  }
+
+  public TreeShakingAbstractMethodRemovalTest(
+      Frontend frontend, Backend backend, MinifyMode minify) {
+    super(
+        "examples/abstractmethodremoval",
+        "abstractmethodremoval.AbstractMethodRemoval",
+        frontend,
+        backend,
+        minify);
+  }
+
+  @Test
+  public void test() throws Exception {
+    runTest(
+        null,
+        null,
+        null,
+        ImmutableList.of("src/test/examples/abstractmethodremoval/keep-rules.txt"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 91fc9d4..642045c 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -110,7 +110,7 @@
 
     // The test contains only a member class so the enclosing-method attribute will be null.
     assertEquals(
-        !keepAnnotations && forceProguardCompatibility,
+        forceProguardCompatibility,
         !clazz.getDexClass().getInnerClasses().isEmpty());
     assertEquals(forceProguardCompatibility || keepAnnotations,
         clazz.annotation(annotationClass.getCanonicalName()).isPresent());
diff --git a/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java b/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
index 1fd6afd..3381473 100644
--- a/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
+++ b/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
@@ -585,7 +585,7 @@
 
   private void notIntMethodBuilder(SmaliBuilder builder, String name, Object parameters) {
     Integer value = (Integer) parameters;
-    builder.addStaticMethod("long", name, Collections.emptyList(),
+    builder.addStaticMethod("int", name, Collections.emptyList(),
         1,
         "    const v0, " + value,
         "    not-int v0, v0",
@@ -950,6 +950,7 @@
     addCmpFloatFoldTests(testBuilder);
     addCmpDoubleFoldTests(testBuilder);
     addCmpLongFold(testBuilder);
+    runDex2Oat(testBuilder.builder.build());
     testBuilder.run();
   }
 
diff --git a/src/test/java/com/android/tools/r8/smali/DexMoveInstructionsTest.java b/src/test/java/com/android/tools/r8/smali/DexMoveInstructionsTest.java
new file mode 100644
index 0000000..c92b73b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/DexMoveInstructionsTest.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2018, 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.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class DexMoveInstructionsTest extends SmaliTestBase {
+
+  public static final String CLASS = "Test";
+
+  @Test
+  public void testValidObjectMoves() throws Throwable {
+    ProcessResult result = testMoves("ExpectedToPass", "Ljava/lang/String;", Arrays.asList(
+        "move-object",
+        "move-object/from16",
+        "move-object/16"));
+    assertEquals(result.toString(), 0, result.exitCode);
+  }
+
+  @Test
+  public void testInvalidObjectMoves() throws Throwable {
+    ProcessResult result = testMoves("ExpectedToFail", "Ljava/lang/String;", Arrays.asList(
+        "move",
+        "move/from16",
+        "move/16"));
+    assertEquals(result.toString(), 1, result.exitCode);
+    assertTrue("Did not find 'Verification error' in " + result.stderr,
+        result.stderr.contains("Verification error") || result.stderr.contains("VerifyError"));
+  }
+
+  @Test
+  public void testValidSingleMoves() throws Throwable {
+    ProcessResult result = testMoves("ExpectedToPass", "I", Arrays.asList(
+        "move",
+        "move/from16",
+        "move/16"));
+    assertEquals(result.toString(), 0, result.exitCode);
+  }
+
+  @Test
+  public void testInvalidSingleMoves() throws Throwable {
+    ProcessResult result = testMoves("ExpectedToFail", "I", Arrays.asList(
+        "move-object",
+        "move-object/from16",
+        "move-object/16"));
+    assertEquals(result.toString(), 1, result.exitCode);
+    assertTrue("Did not find 'Verification error' in " + result.stderr,
+        result.stderr.contains("Verification error") || result.stderr.contains("VerifyError"));
+  }
+
+  private ProcessResult testMoves(String clazz, String typeDesc, List<String> moveOps)
+      throws Throwable {
+    String typeName = DescriptorUtils.descriptorToJavaType(typeDesc);
+
+    SmaliBuilder builder = new SmaliBuilder(clazz);
+    int i = 0;
+    for (String moveOp : moveOps) {
+      builder.addStaticMethod(typeName, "test" + i++, Collections.singletonList(typeName),
+          1,
+          "    " + moveOp + " v0, p0",
+          typeDesc.startsWith("L") ? "return-object v0" : "    return v0"
+      );
+    }
+
+    List<String> main = new ArrayList<>();
+    main.add("  sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;");
+    main.add("  const v2, 0");
+    i = 0;
+    for (String moveOp : moveOps) {
+      main.add("  invoke-static { v2 }, L" + clazz + ";->test" + i++
+          + "(" + typeDesc + ")" + typeDesc);
+      if (typeDesc.startsWith("L")) {
+        main.add("  move-result-object v1");
+      } else {
+        main.add("  move-result v1");
+      }
+      main.add("  invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(" + typeDesc + ")V");
+    }
+    main.add("  return-void");
+    builder.addMainMethod(3, main.toArray(new String[0]));
+
+    return runOnArtRaw(builder.build(), clazz);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/IfZeroObjectTest.java b/src/test/java/com/android/tools/r8/smali/IfZeroObjectTest.java
new file mode 100644
index 0000000..ebcd835
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/IfZeroObjectTest.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2018, 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.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class IfZeroObjectTest extends SmaliTestBase {
+
+  public static final String CLASS = "Test";
+
+  @Test
+  public void testObjectIfs() throws Throwable {
+    ProcessResult result = testIfs("ExpectedToPass", Arrays.asList(
+        "eqz",
+        "nez"));
+    assertEquals(result.toString(), 0, result.exitCode);
+  }
+
+  @Test
+  public void testNumericIfs() throws Throwable {
+    ProcessResult result = testIfs("ExpectedToFail", Arrays.asList(
+        "ltz",
+        "gez",
+        "gtz",
+        "lez"));
+    assertEquals(result.toString(), 1, result.exitCode);
+    assertTrue("Did not find 'Verification error' in " + result.stderr,
+        result.stderr.contains("Verification error") || result.stderr.contains("VerifyError"));
+  }
+
+  private ProcessResult testIfs(String clazz, List<String> ifZeroOps) throws Throwable {
+
+    SmaliBuilder builder = new SmaliBuilder(clazz);
+    for (String ifZeroOp : ifZeroOps) {
+      builder.addStaticMethod("int", "if" + ifZeroOp, Collections.singletonList("java.lang.Object"),
+          1,
+          "    if-" + ifZeroOp + " p0, :L",
+          "    const v0, 0",
+          "    return v0",
+          "  :L",
+          "    const v0, 1",
+          "    return v0"
+      );
+    }
+
+    List<String> main = new ArrayList<>();
+    main.add("  sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;");
+    for (String ifZeroOp : ifZeroOps) {
+      main.add("  invoke-static { p0 }, L" + clazz + ";->if" + ifZeroOp + "(Ljava/lang/Object;)I");
+      main.add("  move-result v1");
+      main.add("  invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->print(I)V");
+    }
+    main.add("  return-void");
+    builder.addMainMethod(2, main.toArray(new String[0]));
+
+    return runOnArtRaw(builder.build(), clazz);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index eb66c51..371c4d1 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -94,7 +94,7 @@
         DEFAULT_METHOD_NAME,
         parameters,
         2,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-string        v1, \"Test\"",
         "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -158,7 +158,7 @@
         DEFAULT_METHOD_NAME,
         parameters,
         2,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-string        v1, \"Test1\"",
         "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -231,7 +231,7 @@
         DEFAULT_METHOD_NAME,
         parameters,
         2,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-string        v1, \"Test\"",
         "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -354,7 +354,7 @@
         DEFAULT_METHOD_NAME,
         parameters,
         3,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-wide          v1, 0x7fffffff00000000L",
         "    invoke-virtual      { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -430,7 +430,7 @@
         DEFAULT_METHOD_NAME,
         parameters,
         3,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-wide          v1, 0x3ff0000000000000L",
         "    invoke-virtual      { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(D)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -793,7 +793,7 @@
         "method1",
         parameters,
         2,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-string        v1, \"Test\"",
         "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -809,7 +809,7 @@
         "method2",
         parameters,
         2,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-string        v1, \"Test\"",
         "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
@@ -878,7 +878,7 @@
         DEFAULT_METHOD_NAME,
         parameters,
         2,
-        "    move                v0, p0",
+        "    move-object         v0, p0",
         "    const-string        v1, \"Test\"",
         "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
         "    move-result-object  v0",
diff --git a/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java b/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
index dae717e..5270cd4 100644
--- a/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/smali/RemoveWriteOfUnusedFieldsTest.java
@@ -6,12 +6,16 @@
 
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
 import org.junit.Test;
 
 public class RemoveWriteOfUnusedFieldsTest extends SmaliTestBase {
@@ -33,20 +37,28 @@
     builder.addStaticField("stringField", "Ljava/lang/String;");
     builder.addStaticField("testField", "LTest;");
 
+    boolean isDalvik = ToolHelper.getDexVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST);
+    String additionalConstZero = isDalvik ? "const v0, 0" : "";
+    String additionalConstZeroWide = isDalvik ? "const-wide v0, 0" : "";
+
     builder.addStaticMethod("void", "test", ImmutableList.of(),
         2,
         "const               v0, 0",
-        "sput-byte           v0, LTest;->booleanField:Z",
+        "sput-boolean        v0, LTest;->booleanField:Z",
         "sput-byte           v0, LTest;->byteField:B",
         "sput-short          v0, LTest;->shortField:S",
         "sput                v0, LTest;->intField:I",
+        // Dalvik 4.x. does not require a new const 0 here.
         "sput                v0, LTest;->floatField:F",
+        additionalConstZero,  // Required for Dalvik 4.x.
         "sput-char           v0, LTest;->charField:C",
+        "const               v0, 0",
         "sput-object         v0, LTest;->objectField:Ljava/lang/Object;",
         "sput-object         v0, LTest;->stringField:Ljava/lang/String;",
         "sput-object         v0, LTest;->testField:LTest;",
         "const-wide          v0, 0",
         "sput-wide           v0, LTest;->longField:J",
+        additionalConstZeroWide,  // Required for Dalvik 4.x.
         "sput-wide           v0, LTest;->doubleField:D",
         "return-void");
 
@@ -55,9 +67,16 @@
         "    invoke-static       { }, LTest;->test()V",
         "    return-void                             ");
 
+    AndroidApp input =
+        AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build();
+
+    Path inputPath = temp.getRoot().toPath().resolve("input.zip");
+    input.writeToZip(inputPath, OutputMode.DexIndexed);
+    ToolHelper.runArtNoVerificationErrors(inputPath.toString(), DEFAULT_CLASS_NAME);
+
     AndroidApp app =
         compileWithR8(
-            AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build(),
+            input,
             keepMainProguardConfiguration("Test"),
             options -> options.enableInlining = false);
 
@@ -84,32 +103,53 @@
     builder.addInstanceField("stringField", "Ljava/lang/String;");
     builder.addInstanceField("testField", "LTest;");
 
+    boolean isDalvik = ToolHelper.getDexVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST);
+    String additionalConstZero = isDalvik ? "const v0, 0" : "";
+    String additionalConstZeroWide = isDalvik ? "const-wide v0, 0" : "";
+
     builder.addInstanceMethod("void", "test", ImmutableList.of(),
         2,
         "const               v0, 0",
-        "iput-byte           v0, p0, LTest;->booleanField:Z",
+        "iput-boolean        v0, p0, LTest;->booleanField:Z",
         "iput-byte           v0, p0, LTest;->byteField:B",
         "iput-short          v0, p0, LTest;->shortField:S",
         "iput                v0, p0, LTest;->intField:I",
+        // Dalvik 4.x. does not require a new const 0 here.
         "iput                v0, p0, LTest;->floatField:F",
+        additionalConstZero,  // Required for Dalvik 4.x.
         "iput-char           v0, p0, LTest;->charField:C",
+        "const               v0, 0",
         "iput-object         v0, p0, LTest;->objectField:Ljava/lang/Object;",
         "iput-object         v0, p0, LTest;->stringField:Ljava/lang/String;",
         "iput-object         v0, p0, LTest;->testField:LTest;",
         "const-wide          v0, 0",
         "iput-wide           v0, p0, LTest;->longField:J",
+        additionalConstZeroWide,  // Required for Dalvik 4.x.
         "iput-wide           v0, p0, LTest;->doubleField:D",
         "return-void");
 
+    builder.addInitializer(ImmutableList.of(), 0,
+        "invoke-direct {p0}, Ljava/lang/Object;-><init>()V",
+        "return-void"
+    );
+
     builder.addMainMethod(
         1,
         "    new-instance         v0, LTest;",
+        "    invoke-direct        { v0 }, LTest;-><init>()V",
         "    invoke-virtual       { v0 }, LTest;->test()V",
         "    return-void                             ");
 
+    AndroidApp input =
+        AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build();
+
+    Path inputPath = temp.getRoot().toPath().resolve("input.zip");
+    input.writeToZip(inputPath, OutputMode.DexIndexed);
+    ToolHelper.runArtNoVerificationErrors(inputPath.toString(), DEFAULT_CLASS_NAME);
+
     AndroidApp app =
         compileWithR8(
-            AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build(),
+            input,
             keepMainProguardConfiguration("Test"),
             options -> options.enableInlining = false);
 
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
index 2f6eb51..9a6e778 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.smali;
 
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -334,13 +333,11 @@
     return result;
   }
 
-  public byte[] compile()
-      throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+  public byte[] compile() throws IOException, RecognitionException, ExecutionException {
     return Smali.compile(buildSource());
   }
 
-  public AndroidApp build()
-      throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+  public AndroidApp build() throws IOException, RecognitionException, ExecutionException {
     return AndroidApp.builder().addDexProgramData(compile(), Origin.unknown()).build();
   }
 
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
index a380834..2297ebd 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
@@ -6,7 +6,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.SmaliWriter;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
@@ -27,7 +26,7 @@
       AndroidApp application =
           AndroidApp.builder().addDexProgramData(Smali.compile(smali), Origin.unknown()).build();
       assertEquals(smali, SmaliWriter.smali(application, new InternalOptions()));
-    } catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
+    } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index 3c101ad..6c4b884 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -6,7 +6,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.OutputMode;
@@ -15,7 +14,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
@@ -46,7 +44,7 @@
   protected AndroidApp buildApplication(SmaliBuilder builder) {
     try {
       return AndroidApp.builder().addDexProgramData(builder.compile(), Origin.unknown()).build();
-    } catch (IOException | RecognitionException | DexOverflowException | ExecutionException e) {
+    } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
@@ -57,7 +55,7 @@
           .addDexProgramData(builder.compile(), EmbeddedOrigin.INSTANCE)
           .addLibraryFiles(ToolHelper.getDefaultAndroidJar())
           .build();
-    } catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
+    } catch (IOException | RecognitionException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }
@@ -79,7 +77,7 @@
       Consumer<InternalOptions> optionsConsumer) {
     try {
       return ToolHelper.runR8(application, optionsConsumer);
-    } catch (IOException | CompilationException e) {
+    } catch (IOException e) {
       throw new RuntimeException(e);
     }
   }
@@ -105,7 +103,9 @@
           .addDexProgramData(builder.compile(), EmbeddedOrigin.INSTANCE);
       ToolHelper.runR8WithFullResult(command.build(), optionsConsumer);
       return dexOutputDir.resolve("classes.dex");
-    } catch (CompilationException | IOException | RecognitionException | ExecutionException
+    } catch (IOException
+        | RecognitionException
+        | ExecutionException
         | CompilationFailedException e) {
       throw new RuntimeException(e);
     }
@@ -232,13 +232,11 @@
         processdApplication, DEFAULT_CLASS_NAME, returnType, DEFAULT_METHOD_NAME, parameters);
   }
 
-  public String runArt(AndroidApp application) throws DexOverflowException {
+  public String runArt(AndroidApp application) {
     return runArt(application, DEFAULT_MAIN_CLASS_NAME);
   }
 
-
-  public String runArt(AndroidApp application, String mainClass)
-      throws DexOverflowException {
+  public String runArt(AndroidApp application, String mainClass) {
     try {
       Path out = temp.getRoot().toPath().resolve("run-art-input.zip");
       // TODO(sgjesse): Pass in a unique temp directory for each run.
@@ -257,8 +255,7 @@
     }
   }
 
-  public void runDex2Oat(AndroidApp application)
-      throws DexOverflowException {
+  public void runDex2Oat(AndroidApp application) {
     try {
       Path dexOut = temp.getRoot().toPath().resolve("run-dex2oat-input.zip");
       Path oatFile = temp.getRoot().toPath().resolve("oat-file");
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index 30aa7ef..68de752 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
-import com.android.tools.r8.errors.DexOverflowException;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
@@ -37,27 +36,27 @@
 public class Smali {
 
   public static byte[] compile(String smaliText)
-      throws RecognitionException, IOException, DexOverflowException, ExecutionException {
+      throws RecognitionException, IOException, ExecutionException {
     return compile(smaliText, 15);
   }
 
   public static byte[] compile(String... smaliText)
-      throws RecognitionException, IOException, DexOverflowException, ExecutionException {
+      throws RecognitionException, IOException, ExecutionException {
     return compile(Arrays.asList(smaliText), 15);
   }
 
   public static byte[] compile(String smaliText, int apiLevel)
-      throws IOException, RecognitionException, DexOverflowException, ExecutionException {
+      throws IOException, RecognitionException, ExecutionException {
     return compile(ImmutableList.of(smaliText), apiLevel);
   }
 
   public static byte[] compile(List<String> smaliTexts)
-      throws RecognitionException, IOException, DexOverflowException, ExecutionException {
+      throws RecognitionException, IOException, ExecutionException {
     return compile(smaliTexts, 15);
   }
 
   public static byte[] compile(List<String> smaliTexts, int apiLevel)
-      throws RecognitionException, IOException, ExecutionException, DexOverflowException {
+      throws RecognitionException, IOException, ExecutionException {
     DexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(apiLevel));
 
     for (String smaliText : smaliTexts) {
diff --git a/tests/d8_api_usage_sample.jar b/tests/d8_api_usage_sample.jar
index eb6f10f..f901b9d 100644
--- a/tests/d8_api_usage_sample.jar
+++ b/tests/d8_api_usage_sample.jar
Binary files differ
diff --git a/tests/r8_api_usage_sample.jar b/tests/r8_api_usage_sample.jar
index d838141..f901b9d 100644
--- a/tests/r8_api_usage_sample.jar
+++ b/tests/r8_api_usage_sample.jar
Binary files differ
diff --git a/tools/minify_tool.py b/tools/minify_tool.py
index 08b2ada..787c1e1 100755
--- a/tools/minify_tool.py
+++ b/tools/minify_tool.py
@@ -9,6 +9,9 @@
 Given an input JAR (default: r8.jar) and a main-class, generates a new input JAR
 with the given main-class in the manifest along with a -keep rule for keeping
 just the main entrypoint, and runs R8 in release+classfile mode on the JAR.
+
+If --benchmark-name NAME is given, prints "<NAME>(RunTimeRaw): <elapsed> ms"
+to standard output at the end of the run.
 '''
 
 import argparse
@@ -44,8 +47,8 @@
     '-O', '--no-debug', dest='debug', action='store_false',
     help='Disable assertions when running R8')
 parser.add_argument(
-    '--print-runtimeraw', metavar='BENCHMARKNAME',
-    help='Print "<BENCHMARKNAME>(RunTimeRaw): <elapsed> ms" at the end')
+    '--benchmark-name',
+    help='Print benchmarks with the given name')
 
 def generate_output_name(input_jar, mainclass):
   if not mainclass:
@@ -83,7 +86,7 @@
     return mo.group(1)
 
 def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None, lib=RT,
-                debug=True, build=True, print_runtimeraw=None):
+                debug=True, build=True, benchmark_name=None):
   if output_jar is None:
     output_jar = generate_output_name(input_jar, mainclass)
   with utils.TempDir() as path:
@@ -104,9 +107,9 @@
             tmp_input_path)
     start_time = time.time()
     return_code = toolhelper.run('r8', args, debug=debug, build=build)
-    if print_runtimeraw:
+    if benchmark_name:
       elapsed_ms = 1000 * (time.time() - start_time)
-      print('%s-Total(RunTimeRaw): %s ms' % (print_runtimeraw, elapsed_ms))
+      print('%s(RunTimeRaw): %s ms' % (benchmark_name, elapsed_ms))
     return return_code
 
 if __name__ == '__main__':
diff --git a/tools/run_bootstrap_benchmark.py b/tools/run_bootstrap_benchmark.py
index 0c608f9..2ae97b2 100755
--- a/tools/run_bootstrap_benchmark.py
+++ b/tools/run_bootstrap_benchmark.py
@@ -14,8 +14,8 @@
 
 parser = argparse.ArgumentParser()
 parser.add_argument(
-    '--print-runtimeraw', metavar='BENCHMARKNAME',
-    help='Print "<BENCHMARKNAME>(RunTimeRaw): <elapsed> ms" at the end')
+    '--name', metavar='NAME', dest='benchmark_name',
+    help='Print "<NAME>(RunTimeRaw): <elapsed> ms" at the end')
 
 
 if __name__ == '__main__':