Merge "Use ApiLevelException for Invoke Polymorphic error"
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 0c0ac08..5321808 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -210,7 +210,7 @@
   private static DexApplication optimize(
       DexApplication application, AppInfo appInfo, InternalOptions options,
       Timing timing, ExecutorService executor)
-      throws IOException, ExecutionException {
+      throws IOException, ExecutionException, ApiLevelException {
     final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
 
     IRConverter converter = new IRConverter(timing, application, appInfo, options, printer);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index b7afda7..328f215 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -114,12 +114,12 @@
       DexApplication application,
       AppInfoWithSubtyping appInfo,
       InternalOptions options)
-      throws ProguardRuleParserException, ExecutionException, IOException {
+      throws ApiLevelException, ExecutionException, IOException {
     return new R8(options).optimize(application, appInfo);
   }
 
   private DexApplication optimize(DexApplication application, AppInfoWithSubtyping appInfo)
-      throws IOException, ProguardRuleParserException, ExecutionException {
+      throws IOException, ApiLevelException, ExecutionException {
     return optimize(application, appInfo, GraphLense.getIdentityLense(),
         Executors.newSingleThreadExecutor());
   }
@@ -129,7 +129,7 @@
       AppInfoWithSubtyping appInfo,
       GraphLense graphLense,
       ExecutorService executorService)
-      throws IOException, ProguardRuleParserException, ExecutionException {
+      throws IOException, ApiLevelException, ExecutionException {
     final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
 
     timing.begin("Create IR");
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 085b6fa..21402a8 100644
--- a/src/main/java/com/android/tools/r8/code/FilledNewArray.java
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
@@ -3,6 +3,7 @@
 // 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;
@@ -38,7 +39,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 25a941f..cb2bd6f 100644
--- a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
@@ -3,6 +3,7 @@
 // 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;
@@ -38,7 +39,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 a87217b..85e0318 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -3,6 +3,7 @@
 // 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;
@@ -179,7 +180,7 @@
     return NO_TARGETS;
   }
 
-  public abstract void buildIR(IRBuilder builder);
+  public abstract void buildIR(IRBuilder builder) throws ApiLevelException;
 
   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 58fb8c1..e52b1ed 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeDirect.java
@@ -3,6 +3,7 @@
 // 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;
@@ -47,7 +48,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 742546d..f7f0c93 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
@@ -3,6 +3,7 @@
 // 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;
@@ -45,7 +46,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 512f6bc..e8cff27 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeInterface.java
@@ -3,6 +3,7 @@
 // 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;
@@ -47,7 +48,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 06eb69e..b40e177 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
@@ -3,6 +3,7 @@
 // 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;
@@ -46,7 +47,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 d5ecd11..891dcca 100644
--- a/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java
@@ -3,6 +3,7 @@
 // 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;
@@ -29,7 +30,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 e6a16f5..4068d80 100644
--- a/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
@@ -3,6 +3,7 @@
 // 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;
@@ -48,7 +49,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 8fbf95c..27c3255 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeStatic.java
@@ -3,6 +3,7 @@
 // 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;
@@ -46,7 +47,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 92a4047..69394b3 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
@@ -3,6 +3,7 @@
 // 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;
@@ -46,7 +47,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 7a6e32a..0382872 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeSuper.java
@@ -3,6 +3,7 @@
 // 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;
@@ -47,7 +48,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 9ee05b7..e12bccb 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
@@ -3,6 +3,7 @@
 // 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;
@@ -46,7 +47,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 1fbf9d9..4de66f6 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
@@ -3,6 +3,7 @@
 // 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;
@@ -46,7 +47,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     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 c5c8249..fbacf0b 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
@@ -3,6 +3,7 @@
 // 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;
@@ -46,7 +47,7 @@
   }
 
   @Override
-  public void buildIR(IRBuilder builder) {
+  public void buildIR(IRBuilder builder) throws ApiLevelException {
     builder.addInvokeRange(Type.VIRTUAL, getMethod(), getProto(), AA, CCCC);
   }
 
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 d47c57d..38e292c 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -49,6 +49,7 @@
 import com.android.tools.r8.naming.NamingLens;
 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;
@@ -365,21 +366,13 @@
   }
 
   private <T extends DexItem> void writeFixedSectionItems(T[] items, int offset,
-      ItemWriter<T> writer) throws ApiLevelException {
+      ThrowingConsumer<T, ApiLevelException> writer) throws ApiLevelException {
     assert dest.position() == offset;
     for (T item : items) {
       writer.accept(item);
     }
   }
 
-  /**
-   * Similar to a {@link Consumer} but throws an {@link ApiLevelException}.
-   */
-  @FunctionalInterface
-  private interface ItemWriter<T> {
-    void accept(T t) throws ApiLevelException;
-  }
-
   private <T extends DexItem> void writeItems(Collection<T> items, Consumer<Integer> offsetSetter,
       Consumer<T> writer) {
     writeItems(items, offsetSetter, writer, 1);
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 3845507..0ae2cbc 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -3,6 +3,7 @@
 // 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;
@@ -13,7 +14,8 @@
 
 public abstract class Code extends CachedHashValueDexItem {
 
-  public abstract IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options);
+  public abstract IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
+      throws ApiLevelException;
 
   public abstract void registerReachableDefinitions(UseRegistry registry);
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 3f492b5..82febb9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -3,10 +3,12 @@
 // 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.Resource;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.ThrowingConsumer;
 import com.google.common.base.MoreObjects;
 import java.util.Arrays;
 import java.util.function.Consumer;
@@ -91,6 +93,16 @@
     }
   }
 
+  public <E extends Throwable> void forEachMethodThrowing(
+      ThrowingConsumer<DexEncodedMethod, E> consumer) throws E {
+    for (DexEncodedMethod method : directMethods()) {
+      consumer.accept(method);
+    }
+    for (DexEncodedMethod method : virtualMethods()) {
+      consumer.accept(method);
+    }
+  }
+
   public DexEncodedMethod[] allMethodsSorted() {
     int vLen = virtualMethods().length;
     int dLen = directMethods().length;
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 f62dda0..1a8602c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -3,6 +3,7 @@
 // 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;
@@ -147,7 +148,8 @@
   }
 
   @Override
-  public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
+  public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
+      throws ApiLevelException {
     DexSourceCode source = new DexSourceCode(this, encodedMethod);
     IRBuilder builder = new IRBuilder(encodedMethod, source, options);
     return builder.build();
@@ -156,7 +158,8 @@
   public IRCode buildIR(
       DexEncodedMethod encodedMethod,
       ValueNumberGenerator valueNumberGenerator,
-      InternalOptions options) {
+      InternalOptions options)
+      throws ApiLevelException {
     DexSourceCode source = new DexSourceCode(this, encodedMethod);
     IRBuilder builder = new IRBuilder(encodedMethod, source, valueNumberGenerator, options);
     return builder.build();
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 e66b984..8d28546 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -9,6 +9,7 @@
 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.code.Const;
 import com.android.tools.r8.code.ConstString;
 import com.android.tools.r8.code.ConstStringJumbo;
@@ -155,11 +156,12 @@
     compilationState = CompilationState.NOT_PROCESSED;
   }
 
-  public IRCode buildIR(InternalOptions options) {
+  public IRCode buildIR(InternalOptions options) throws ApiLevelException {
     return code == null ? null : code.buildIR(this, options);
   }
 
-  public IRCode buildIR(ValueNumberGenerator valueNumberGenerator, InternalOptions options) {
+  public IRCode buildIR(ValueNumberGenerator valueNumberGenerator, InternalOptions options)
+      throws ApiLevelException {
     return code == null
         ? null
         : code.asDexCode().buildIR(this, valueNumberGenerator, options);
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 6b9c06d..1dc5964 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -3,6 +3,7 @@
 // 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.ir.code.IRCode;
 import com.android.tools.r8.ir.code.ValueNumberGenerator;
@@ -80,7 +81,8 @@
   }
 
   @Override
-  public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
+  public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
+      throws ApiLevelException {
     triggerDelayedParsingIfNeccessary();
     return options.debug
         ? internalBuildWithLocals(encodedMethod, null, options)
@@ -88,7 +90,8 @@
   }
 
   public IRCode buildIR(
-      DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options) {
+      DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options)
+      throws ApiLevelException {
     assert generator != null;
     triggerDelayedParsingIfNeccessary();
     return options.debug
@@ -97,7 +100,8 @@
   }
 
   private IRCode internalBuildWithLocals(
-      DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options) {
+      DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options)
+      throws ApiLevelException {
     try {
       return internalBuild(encodedMethod, generator, options);
     } catch (InvalidDebugInfoException e) {
@@ -108,7 +112,8 @@
   }
 
   private IRCode internalBuild(
-      DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options) {
+      DexEncodedMethod encodedMethod, ValueNumberGenerator generator, InternalOptions options)
+      throws ApiLevelException {
     if (!options.debug) {
       node.localVariables.clear();
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
index dad58d8..585260b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -293,7 +294,8 @@
     return nodes.size() == 0;
   }
 
-  public void forEachMethod(Consumer<DexEncodedMethod> consumer, ExecutorService executorService)
+  public <E extends Exception> void forEachMethod(
+      ThrowingConsumer<DexEncodedMethod, E> consumer, ExecutorService executorService)
       throws ExecutionException {
     while (!isEmpty()) {
       List<DexEncodedMethod> methods = extractLeaves();
@@ -302,6 +304,7 @@
       for (DexEncodedMethod method : methods) {
         futures.add(executorService.submit(() -> {
           consumer.accept(method);
+          return null; // we want a Callable not a Runnable to be able to throw
         }));
       }
       ThreadUtils.awaitFutures(futures);
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 9e07ad6..bc821d3 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,6 +4,7 @@
 
 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;
@@ -148,7 +149,7 @@
   }
 
   @Override
-  public void buildInstruction(IRBuilder builder, int instructionIndex) {
+  public void buildInstruction(IRBuilder builder, int instructionIndex) throws ApiLevelException {
     updateCurrentCatchHandlers(instructionIndex);
     emitDebugPosition(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 7f7b726..e10c690 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
@@ -4,6 +4,7 @@
 
 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.InternalCompilerError;
@@ -311,7 +312,7 @@
    *
    * @return The list of basic blocks. First block is the main entry.
    */
-  public IRCode build() {
+  public IRCode build() throws ApiLevelException {
     assert source != null;
     source.setUp();
 
@@ -441,7 +442,7 @@
     return true;
   }
 
-  private void processWorklist() {
+  private void processWorklist() throws ApiLevelException {
     for (WorklistItem item = ssaWorklist.poll(); item != null; item = ssaWorklist.poll()) {
       if (item.block.isFilled()) {
         continue;
@@ -917,12 +918,14 @@
     add(instruction);
   }
 
-  public void addInvoke(
-      Type type, DexItem item, DexProto callSiteProto, List<Value> arguments) {
+  public void addInvoke(Type type, DexItem item, DexProto callSiteProto, List<Value> arguments)
+      throws ApiLevelException {
     if (type == Invoke.Type.POLYMORPHIC && !options.canUseInvokePolymorphic()) {
-      throw new CompilationError(
-          "MethodHandle.invoke and MethodHandle.invokeExact is unsupported before "
-              + "Android O (--min-api " + Constants.ANDROID_O_API + ")");
+      throw new ApiLevelException(
+          Constants.ANDROID_O_API,
+          "Android O",
+          "MethodHandle.invoke and MethodHandle.invokeExact",
+          null /* sourceString */);
     }
     add(Invoke.create(type, item, callSiteProto, null, arguments));
   }
@@ -932,7 +935,8 @@
       DexItem item,
       DexProto callSiteProto,
       List<MoveType> types,
-      List<Integer> registers) {
+      List<Integer> registers)
+      throws ApiLevelException {
     assert types.size() == registers.size();
     List<Value> arguments = new ArrayList<>(types.size());
     for (int i = 0; i < types.size(); i++) {
@@ -1000,7 +1004,8 @@
       DexMethod method,
       DexProto callSiteProto,
       int argumentRegisterCount,
-      int[] argumentRegisters) {
+      int[] argumentRegisters)
+      throws ApiLevelException {
     // 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);
@@ -1027,7 +1032,8 @@
     addInvoke(type, method, callSiteProto, arguments);
   }
 
-  public void addInvokeNewArray(DexType type, int argumentCount, int[] argumentRegisters) {
+  public void addInvokeNewArray(DexType type, int argumentCount, int[] argumentRegisters)
+      throws ApiLevelException {
     String descriptor = type.descriptor.toString();
     assert descriptor.charAt(0) == '[';
     assert descriptor.length() >= 2;
@@ -1051,7 +1057,8 @@
       DexMethod method,
       DexProto callSiteProto,
       int argumentCount,
-      int firstArgumentRegister) {
+      int firstArgumentRegister)
+      throws ApiLevelException {
     // 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);
@@ -1078,7 +1085,8 @@
     addInvoke(type, method, callSiteProto, arguments);
   }
 
-  public void addInvokeRangeNewArray(DexType type, int argumentCount, int firstArgumentRegister) {
+  public void addInvokeRangeNewArray(DexType type, int argumentCount, int firstArgumentRegister)
+      throws ApiLevelException {
     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 fad378e..82cc92f 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,6 +6,7 @@
 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;
@@ -42,6 +43,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -196,7 +198,7 @@
     }
   }
 
-  private void synthesizeLambdaClasses(Builder builder) {
+  private void synthesizeLambdaClasses(Builder builder) throws ApiLevelException {
     if (lambdaRewriter != null) {
       lambdaRewriter.adjustAccessibility();
       lambdaRewriter.synthesizeLambdaClasses(builder);
@@ -204,13 +206,15 @@
   }
 
   private void desugarInterfaceMethods(
-      Builder builder, InterfaceMethodRewriter.Flavor includeAllResources) {
+      Builder builder, InterfaceMethodRewriter.Flavor includeAllResources)
+      throws ApiLevelException {
     if (interfaceMethodRewriter != null) {
       interfaceMethodRewriter.desugarInterfaceMethods(builder, includeAllResources);
     }
   }
 
-  public DexApplication convertToDex(ExecutorService executor) throws ExecutionException {
+  public DexApplication convertToDex(ExecutorService executor)
+      throws ExecutionException, ApiLevelException {
     removeLambdaDeserializationMethods();
 
     convertClassesToDex(application.classes(), executor);
@@ -229,16 +233,20 @@
       ExecutorService executor) throws ExecutionException {
     List<Future<?>> futures = new ArrayList<>();
     for (DexProgramClass clazz : classes) {
-      futures.add(executor.submit(() -> clazz.forEachMethod(this::convertMethodToDex)));
+      futures.add(
+          executor.submit(
+              () -> {
+                clazz.forEachMethodThrowing(this::convertMethodToDex);
+                return null; // we want a Callable not a Runnable to be able to throw
+              }));
     }
-
     ThreadUtils.awaitFutures(futures);
 
     // Get rid of <clinit> methods with no code.
     removeEmptyClassInitializers();
   }
 
-  private void convertMethodToDex(DexEncodedMethod method) {
+  void convertMethodToDex(DexEncodedMethod method) throws ApiLevelException {
     if (method.getCode() != null) {
       boolean matchesMethodFilter = options.methodMatchesFilter(method);
       if (matchesMethodFilter) {
@@ -250,7 +258,7 @@
     }
   }
 
-  public DexApplication optimize() throws ExecutionException {
+  public DexApplication optimize() throws ExecutionException, ApiLevelException {
     ExecutorService executor = Executors.newSingleThreadExecutor();
     try {
       return optimize(executor);
@@ -259,7 +267,8 @@
     }
   }
 
-  public DexApplication optimize(ExecutorService executorService) throws ExecutionException {
+  public DexApplication optimize(ExecutorService executorService)
+      throws ExecutionException, ApiLevelException {
     removeLambdaDeserializationMethods();
 
     timing.begin("Build call graph");
@@ -380,7 +389,7 @@
     return result;
   }
 
-  private DexProgramClass prepareOutlining() {
+  private DexProgramClass prepareOutlining() throws ApiLevelException {
     if (!outliner.selectMethodsForOutlining()) {
       return null;
     }
@@ -389,12 +398,12 @@
     return outlineClass;
   }
 
-  public void optimizeSynthesizedClass(DexProgramClass clazz) {
+  public void optimizeSynthesizedClass(DexProgramClass clazz) throws ApiLevelException {
     // Process the generated class, but don't apply any outlining.
-    clazz.forEachMethod(this::optimizeSynthesizedMethod);
+    clazz.forEachMethodThrowing(this::optimizeSynthesizedMethod);
   }
 
-  public void optimizeSynthesizedMethod(DexEncodedMethod method) {
+  public void optimizeSynthesizedMethod(DexEncodedMethod method) throws ApiLevelException {
     // Process the generated method, but don't apply any outlining.
     processMethod(method, ignoreOptimizationFeedback, Outliner::noProcessing);
   }
@@ -405,7 +414,8 @@
 
   public void processMethod(DexEncodedMethod method,
       OptimizationFeedback feedback,
-      BiConsumer<IRCode, DexEncodedMethod> outlineHandler) {
+      BiConsumer<IRCode, DexEncodedMethod> outlineHandler)
+          throws ApiLevelException {
     Code code = method.getCode();
     boolean matchesMethodFilter = options.methodMatchesFilter(method);
     if (code != null && matchesMethodFilter) {
@@ -418,7 +428,8 @@
 
   private void rewriteCode(DexEncodedMethod method,
       OptimizationFeedback feedback,
-      BiConsumer<IRCode, DexEncodedMethod> outlineHandler) {
+      BiConsumer<IRCode, DexEncodedMethod> outlineHandler)
+      throws ApiLevelException {
     if (options.verbose) {
       System.out.println("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 4081dbd..668e220 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,6 +3,7 @@
 // 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;
@@ -32,6 +33,7 @@
 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.Int2ReferenceOpenHashMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
@@ -46,7 +48,6 @@
 import java.util.Map;
 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;
@@ -461,7 +462,7 @@
   }
 
   @Override
-  public void buildInstruction(IRBuilder builder, int instructionIndex) {
+  public void buildInstruction(IRBuilder builder, int instructionIndex) throws ApiLevelException {
     if (instructionIndex == EXCEPTIONAL_SYNC_EXIT_OFFSET) {
       buildExceptionalPostlude(builder);
       return;
@@ -1777,7 +1778,7 @@
 
   // IR instruction building procedures.
 
-  private void build(AbstractInsnNode insn, IRBuilder builder) {
+  private void build(AbstractInsnNode insn, IRBuilder builder) throws ApiLevelException {
     switch (insn.getType()) {
       case AbstractInsnNode.INSN:
         build((InsnNode) insn, builder);
@@ -2505,7 +2506,7 @@
     }
   }
 
-  private void build(MethodInsnNode insn, IRBuilder builder) {
+  private void build(MethodInsnNode insn, IRBuilder builder) throws ApiLevelException {
     // Resolve the target method of the invoke.
     DexMethod method = application.getMethod(insn.owner, insn.name, insn.desc);
 
@@ -2528,8 +2529,12 @@
   }
 
   private void buildInvoke(
-      String methodDesc, Type methodOwner, boolean addImplicitReceiver,
-      IRBuilder builder, BiConsumer<List<MoveType>, List<Integer>> creator) {
+      String methodDesc,
+      Type methodOwner,
+      boolean addImplicitReceiver,
+      IRBuilder builder,
+      ThrowingBiConsumer<List<MoveType>, List<Integer>, ApiLevelException> creator)
+      throws ApiLevelException {
 
     // 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.
@@ -2570,7 +2575,7 @@
     registers.add(slot.register);
   }
 
-  private void build(InvokeDynamicInsnNode insn, IRBuilder builder) {
+  private void build(InvokeDynamicInsnNode insn, IRBuilder builder) throws ApiLevelException {
     // Bootstrap method
     Handle bsmHandle = insn.bsm;
     if (bsmHandle.getTag() != Opcodes.H_INVOKESTATIC &&
@@ -2770,7 +2775,7 @@
     builder.addSwitch(index, keys, fallthroughOffset, labelOffsets);
   }
 
-  private void build(MultiANewArrayInsnNode insn, IRBuilder builder) {
+  private void build(MultiANewArrayInsnNode insn, IRBuilder builder) throws ApiLevelException {
     // Type of the full array.
     Type arrayType = Type.getObjectType(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 b1f9a14..cf1e669 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,6 +4,7 @@
 
 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;
 
@@ -46,7 +47,7 @@
 
   // Delegates for IR building.
   void buildPrelude(IRBuilder builder);
-  void buildInstruction(IRBuilder builder, int instructionIndex);
+  void buildInstruction(IRBuilder builder, int instructionIndex) throws ApiLevelException;
   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/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index c3f2d08..cbeda1e 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,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar;
 
+import com.android.tools.r8.ApiLevelException;
 import com.android.tools.r8.Resource;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unimplemented;
@@ -193,7 +194,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) {
+  public void desugarInterfaceMethods(Builder builder, Flavor flavour) throws ApiLevelException {
     // 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 353f866..f03a619 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,6 +4,7 @@
 
 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;
@@ -387,7 +388,7 @@
     }
 
     // Ensure access of the referenced symbol(s).
-    abstract boolean ensureAccessibility();
+    abstract boolean ensureAccessibility() throws ApiLevelException;
 
     DexClass definitionFor(DexType type) {
       return rewriter.converter.appInfo.app.definitionFor(type);
@@ -482,7 +483,7 @@
     }
 
     @Override
-    boolean ensureAccessibility() {
+    boolean ensureAccessibility() throws ApiLevelException {
       // 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 f6f8f2c..86ad391 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,6 +4,7 @@
 
 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;
@@ -191,7 +192,7 @@
    * Adjust accessibility of referenced application symbols or
    * creates necessary accessors.
    */
-  public void adjustAccessibility() {
+  public void adjustAccessibility() throws ApiLevelException {
     // 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.
@@ -201,7 +202,7 @@
   }
 
   /** Generates lambda classes and adds them to the builder. */
-  public void synthesizeLambdaClasses(Builder builder) {
+  public void synthesizeLambdaClasses(Builder builder) throws ApiLevelException {
     for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
       DexProgramClass synthesizedClass = lambdaClass.synthesizeLambdaClass();
       converter.optimizeSynthesizedClass(synthesizedClass);
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 b2ebecc..38c8da8 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,6 +3,7 @@
 // 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;
@@ -47,14 +48,16 @@
     return ordinalsMaps.get(enumClass);
   }
 
-  public void run() {
-    appInfo.classes().forEach(this::processClasses);
+  public void run() throws ApiLevelException {
+    for (DexProgramClass clazz : appInfo.classes()) {
+      processClasses(clazz);
+    }
     if (!ordinalsMaps.isEmpty()) {
       appInfo.setExtension(EnumOrdinalMapCollector.class, ordinalsMaps);
     }
   }
 
-  private void processClasses(DexProgramClass clazz) {
+  private void processClasses(DexProgramClass clazz) throws ApiLevelException {
     // 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 814812c..b90d2ad 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,6 +3,7 @@
 // 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.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexAccessFlags;
 import com.android.tools.r8.graph.DexClass;
@@ -127,7 +128,7 @@
   }
 
   public synchronized void processDoubleInlineCallers(IRConverter converter,
-      OptimizationFeedback feedback) {
+      OptimizationFeedback feedback) throws ApiLevelException {
     if (doubleInlineCallers.size() > 0) {
       applyDoubleInlining = true;
       List<DexEncodedMethod> methods = doubleInlineCallers
@@ -226,7 +227,7 @@
     }
 
     public IRCode buildIR(ValueNumberGenerator generator, AppInfoWithSubtyping appInfo,
-        GraphLense graphLense, InternalOptions options) {
+        GraphLense graphLense, InternalOptions options) throws ApiLevelException {
       if (target.isProcessed()) {
         assert target.getCode().isDexCode();
         return target.buildIR(generator, options);
@@ -314,7 +315,8 @@
     return true;
   }
 
-  public void performInlining(DexEncodedMethod method, IRCode code, CallGraph callGraph) {
+  public void performInlining(DexEncodedMethod method, IRCode code, CallGraph callGraph)
+      throws ApiLevelException {
     int instruction_allowance = 1500;
     instruction_allowance -= numberOfInstructions(code);
     if (instruction_allowance < 0) {
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 3857c1f..f6d9c26 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,6 +4,7 @@
 
 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;
@@ -969,7 +970,8 @@
     }
 
     @Override
-    public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
+    public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
+        throws ApiLevelException {
       OutlineSourceCode source = new OutlineSourceCode(outline);
       IRBuilder builder = new IRBuilder(encodedMethod, source, options);
       return builder.build();
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 44f0759..58bf0bb 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,6 +3,7 @@
 // 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,8 +76,10 @@
     intArrayType = appInfo.dexItemFactory.createType("[I");
   }
 
-  public void run() {
-    appInfo.classes().forEach(this::processClasses);
+  public void run() throws ApiLevelException {
+    for (DexProgramClass clazz : appInfo.classes()) {
+      processClasses(clazz);
+    }
     if (!switchMaps.isEmpty()) {
       appInfo.setExtension(SwitchMapCollector.class, switchMaps);
     }
@@ -89,7 +92,7 @@
     return switchMaps.get(field);
   }
 
-  private void processClasses(DexProgramClass clazz) {
+  private void processClasses(DexProgramClass clazz) throws ApiLevelException {
     // 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/synthetic/SingleBlockSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java
index bd7efce..5fa247e 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java
@@ -6,6 +6,7 @@
 
 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;
@@ -16,9 +17,9 @@
 import com.android.tools.r8.ir.code.Value;
 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;
 
 public abstract class SingleBlockSourceCode implements SourceCode {
 
@@ -38,7 +39,7 @@
   private Value[] paramValues;
 
   // Instruction constructors
-  private List<Consumer<IRBuilder>> constructors = new ArrayList<>();
+  private List<ThrowingConsumer<IRBuilder, ApiLevelException>> constructors = new ArrayList<>();
 
   protected SingleBlockSourceCode(DexType receiver, DexProto proto) {
     assert proto != null;
@@ -57,7 +58,7 @@
     }
   }
 
-  protected final void add(Consumer<IRBuilder> constructor) {
+  protected final void add(ThrowingConsumer<IRBuilder, ApiLevelException> constructor) {
     constructors.add(constructor);
   }
 
@@ -174,7 +175,8 @@
   }
 
   @Override
-  public final void buildInstruction(IRBuilder builder, int instructionIndex) {
+  public final void buildInstruction(IRBuilder builder, int instructionIndex)
+      throws ApiLevelException {
     constructors.get(instructionIndex).accept(builder);
   }
 
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 3d3f9f1..0de034a 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,6 +4,7 @@
 
 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.Code;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -31,7 +32,8 @@
   }
 
   @Override
-  public final IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
+  public final IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options)
+      throws ApiLevelException {
     return new IRBuilder(encodedMethod, sourceCode, options).build();
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingBiConsumer.java b/src/main/java/com/android/tools/r8/utils/ThrowingBiConsumer.java
new file mode 100644
index 0000000..d7da332
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ThrowingBiConsumer.java
@@ -0,0 +1,15 @@
+package com.android.tools.r8.utils;
+
+import java.util.function.BiConsumer;
+
+/**
+ * Similar to a {@link BiConsumer} but throws a single {@link Throwable}.
+ *
+ * @param <T> the type of the first argument
+ * @param <U> the type of the second argument
+ * @param <E> the type of the {@link Throwable}
+ */
+@FunctionalInterface
+public interface ThrowingBiConsumer<T, U, E extends Throwable> {
+  void accept(T t, U u) throws E;
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java b/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java
new file mode 100644
index 0000000..8fb8c84
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java
@@ -0,0 +1,14 @@
+package com.android.tools.r8.utils;
+
+import java.util.function.Consumer;
+
+/**
+ * Similar to a {@link Consumer} but throws a single {@link Throwable}.
+ *
+ * @param <T> the type of the input
+ * @param <E> the type of the {@link Throwable}
+ */
+@FunctionalInterface
+public interface ThrowingConsumer<T, E extends Throwable> {
+  void accept(T t) throws E;
+}
diff --git a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
index 66853cb..61a049e 100644
--- a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
+++ b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
@@ -28,7 +28,7 @@
   private static final Path SMALI_DIR = Paths.get(ToolHelper.SMALI_BUILD_DIR);
 
   @Test
-  public void UnreachableCode() throws IOException, ExecutionException {
+  public void UnreachableCode() throws IOException, ExecutionException, CompilationException {
     String name = "unreachable-code-1";
     AndroidApp input = AndroidApp.fromProgramFiles(SMALI_DIR.resolve(name).resolve(name + ".dex"));
     ExecutorService executorService = Executors.newSingleThreadExecutor();
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 937f55c..0ca84a1 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -106,9 +106,6 @@
     }
 
     void run() throws Throwable {
-      if (compilationErrorExpected(testName)) {
-        thrown.expect(CompilationError.class);
-      }
       if (minSdkErrorExpected(testName)) {
         thrown.expect(ApiLevelException.class);
       }
@@ -154,11 +151,9 @@
     abstract void build(Path inputFile, Path out) throws Throwable;
   }
 
-  private static List<String> compilationErrorExpected =
-      ImmutableList.of("invokepolymorphic-error-due-to-min-sdk");
-
   private static List<String> minSdkErrorExpected =
-      ImmutableList.of("invokecustom-error-due-to-min-sdk");
+      ImmutableList.of(
+          "invokepolymorphic-error-due-to-min-sdk", "invokecustom-error-due-to-min-sdk");
 
   private static Map<DexVm, List<String>> failsOn =
       ImmutableMap.of(
@@ -216,10 +211,6 @@
     return failsOn(failsOn, name);
   }
 
-  boolean compilationErrorExpected(String testName) {
-    return compilationErrorExpected.contains(testName);
-  }
-
   boolean minSdkErrorExpected(String testName) {
     return minSdkErrorExpected.contains(testName);
   }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 663d6bd..cb8e307 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -523,7 +523,7 @@
       DexApplication application,
       AppInfoWithSubtyping appInfo,
       InternalOptions options)
-      throws ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ExecutionException, IOException {
     return R8.optimize(application, appInfo, options);
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
index 50cee1b..cf191de 100644
--- a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir;
 
+import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -31,7 +32,7 @@
    * Third block: Return instruction
    *
    */
-  IRCode simpleCode() {
+  IRCode simpleCode() throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String returnType = "int";
@@ -61,7 +62,7 @@
   }
 
   @Test
-  public void removeBeforeNext() {
+  public void removeBeforeNext() throws Exception {
     IRCode code = simpleCode();
 
     ListIterator<BasicBlock> blocks = code.listIterator();
@@ -70,7 +71,7 @@
   }
 
   @Test
-  public void removeTwice() {
+  public void removeTwice() throws Exception {
     IRCode code = simpleCode();
 
     ListIterator<BasicBlock> blocks = code.listIterator();
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index f4206ea..6fb5db1 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -24,7 +24,7 @@
 
 public class InlineTest extends SmaliTestBase {
 
-  TestApplication codeForMethodReplaceTest(int a, int b) {
+  TestApplication codeForMethodReplaceTest(int a, int b) throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     MethodSignature signature = builder.addStaticMethod(
@@ -88,7 +88,7 @@
         ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
   }
 
-  public void runInlineTest(int a, int b, int expectedA, int expectedB) {
+  public void runInlineTest(int a, int b, int expectedA, int expectedB) throws Exception {
     // Run code without inlining.
     TestApplication test = codeForMethodReplaceTest(a, b);
     String result = test.run();
@@ -116,12 +116,12 @@
   }
 
   @Test
-  public void inline() {
+  public void inline() throws Exception {
     runInlineTest(1, 1, 2, 0);
     runInlineTest(1, 2, 3, 1);
   }
 
-  TestApplication codeForMethodReplaceReturnVoidTest(int a, int b) {
+  TestApplication codeForMethodReplaceReturnVoidTest(int a, int b) throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     MethodSignature signature = builder.addStaticMethod(
@@ -169,7 +169,7 @@
   }
 
   @Test
-  public void inlineReturnVoid() {
+  public void inlineReturnVoid() throws Exception {
     // Run code without inlining.
     TestApplication test = codeForMethodReplaceReturnVoidTest(1, 2);
     String result = test.run();
@@ -187,7 +187,7 @@
     assertEquals(Integer.toString(1), result);
   }
 
-  TestApplication codeForMultipleMethodReplaceTest(int a, int b) {
+  TestApplication codeForMultipleMethodReplaceTest(int a, int b) throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     MethodSignature signature = builder.addStaticMethod(
@@ -259,7 +259,7 @@
         additionalCode, valueNumberGenerator, options);
   }
 
-  public void runInlineMultipleTest(int a, int b, int expectedA, int expectedB) {
+  public void runInlineMultipleTest(int a, int b, int expectedA, int expectedB) throws Exception {
     // Run code without inlining.
     TestApplication test = codeForMultipleMethodReplaceTest(a, b);
     String result = test.run();
@@ -304,12 +304,13 @@
   }
 
   @Test
-  public void inlineMultiple() {
+  public void inlineMultiple() throws Exception {
     runInlineMultipleTest(1, 1, 4, 1);
     runInlineMultipleTest(1, 2, 7, 8);
   }
 
-  TestApplication codeForMethodReplaceTestWithCatchHandler(int a, int b, boolean twoGuards) {
+  TestApplication codeForMethodReplaceTestWithCatchHandler(int a, int b, boolean twoGuards)
+      throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String secondGuard = twoGuards ?
@@ -385,7 +386,7 @@
   }
 
   public void runInlineCallerHasCatchHandlersTest(
-      int a, int b, boolean twoGuards, int expectedA, int expectedB) {
+      int a, int b, boolean twoGuards, int expectedA, int expectedB) throws Exception {
     // Run code without inlining.
     TestApplication test = codeForMethodReplaceTestWithCatchHandler(a, b, twoGuards);
     String result = test.run();
@@ -413,14 +414,14 @@
   }
 
   @Test
-  public void inlineCallerHasCatchHandlers() {
+  public void inlineCallerHasCatchHandlers() throws Exception {
     runInlineCallerHasCatchHandlersTest(1, 1, false, 2, 0);
     runInlineCallerHasCatchHandlersTest(1, 2, false, 3, 1);
     runInlineCallerHasCatchHandlersTest(1, 1, true, 2, 0);
     runInlineCallerHasCatchHandlersTest(1, 2, true, 3, 1);
   }
 
-  TestApplication codeForInlineCanThrow(int a, int b, boolean twoGuards) {
+  TestApplication codeForInlineCanThrow(int a, int b, boolean twoGuards) throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String secondGuard = twoGuards ?
@@ -499,7 +500,7 @@
   }
 
   public void runInlineCanThrow(
-      int a, int b, boolean twoGuards, int expectedA, int expectedB) {
+      int a, int b, boolean twoGuards, int expectedA, int expectedB) throws Exception {
     // Run code without inlining.
     TestApplication test = codeForInlineCanThrow(a, b, twoGuards);
     String result = test.run();
@@ -527,14 +528,14 @@
   }
 
   @Test
-  public void inlineCanThrow() {
+  public void inlineCanThrow() throws Exception {
     runInlineCanThrow(2, 2, false, 1, 1);
     runInlineCanThrow(2, 0, false, -2, -1);
     runInlineCanThrow(2, 2, true, 1, 1);
     runInlineCanThrow(2, 0, true, -2, -1);
   }
 
-  private TestApplication codeForInlineAlwaysThrows(boolean twoGuards) {
+  private TestApplication codeForInlineAlwaysThrows(boolean twoGuards) throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String secondGuard = twoGuards ?
@@ -611,7 +612,8 @@
         ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
   }
 
-  private void runInlineAlwaysThrows(boolean twoGuards, int expectedA, int expectedB) {
+  private void runInlineAlwaysThrows(boolean twoGuards, int expectedA, int expectedB)
+      throws Exception {
     // Run code without inlining.
     TestApplication test = codeForInlineAlwaysThrows(twoGuards);
     String result = test.run();
@@ -640,12 +642,12 @@
   }
 
   @Test
-  public void inlineAlwaysThrows() {
+  public void inlineAlwaysThrows() throws Exception {
     runInlineAlwaysThrows(false, -2, -2);
     runInlineAlwaysThrows(true, -2, -1);
   }
 
-  private TestApplication codeForInlineAlwaysThrowsMultiple(boolean twoGuards) {
+  private TestApplication codeForInlineAlwaysThrowsMultiple(boolean twoGuards) throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String secondGuard = twoGuards ?
@@ -732,7 +734,8 @@
         application, method, code, additionalCode, valueNumberGenerator, options);
   }
 
-  private void runInlineAlwaysThrowsMultiple(boolean twoGuards, int expectedA, int expectedB) {
+  private void runInlineAlwaysThrowsMultiple(boolean twoGuards, int expectedA, int expectedB)
+      throws Exception {
     // Run code without inlining.
     TestApplication test = codeForInlineAlwaysThrows(twoGuards);
     String result = test.run();
@@ -785,13 +788,13 @@
   }
 
   @Test
-  public void inlineAlwaysThrowsMultiple() {
+  public void inlineAlwaysThrowsMultiple() throws Exception {
     runInlineAlwaysThrowsMultiple(false, -2, -2);
     runInlineAlwaysThrowsMultiple(true, -2, -1);
   }
 
   private TestApplication codeForInlineAlwaysThrowsMultipleWithControlFlow(
-      int a, boolean twoGuards) {
+      int a, boolean twoGuards) throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String secondGuard = twoGuards ?
@@ -886,7 +889,7 @@
   }
 
   private void runInlineAlwaysThrowsMultipleWithControlFlow(
-      int a, boolean twoGuards, int expectedA, int expectedB) {
+      int a, boolean twoGuards, int expectedA, int expectedB) throws Exception {
     // Run code without inlining.
     TestApplication test = codeForInlineAlwaysThrows(twoGuards);
     String result = test.run();
@@ -939,7 +942,7 @@
   }
 
   @Test
-  public void inlineAlwaysThrowsMultipleWithControlFlow() {
+  public void inlineAlwaysThrowsMultipleWithControlFlow() throws Exception {
     runInlineAlwaysThrowsMultipleWithControlFlow(0, false, -2, -2);
     runInlineAlwaysThrowsMultipleWithControlFlow(0, true, -2, -1);
     runInlineAlwaysThrowsMultipleWithControlFlow(1, false, -2, -2);
@@ -948,8 +951,9 @@
     runInlineAlwaysThrowsMultipleWithControlFlow(2, true, -2, -1);
   }
 
-  private TestApplication codeForInlineWithHandlersCanThrow(int a, int b, int c,
-      boolean twoGuards, boolean callerHasCatchAll, boolean inlineeHasCatchAll) {
+  private TestApplication codeForInlineWithHandlersCanThrow(
+      int a, int b, int c, boolean twoGuards, boolean callerHasCatchAll, boolean inlineeHasCatchAll)
+      throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String secondGuard = "";
@@ -1122,7 +1126,7 @@
 
   private void runInlineWithHandlersCanThrow(int a, int b, int c,
       boolean twoGuards, boolean callerHasCatchAll, boolean inlineeHasCatchAll,
-      int expectedA, int expectedB) {
+      int expectedA, int expectedB) throws Exception {
     // Run code without inlining.
     TestApplication test = codeForInlineWithHandlersCanThrow(
         a, b, c, twoGuards, callerHasCatchAll, inlineeHasCatchAll);
@@ -1153,7 +1157,7 @@
   }
 
   @Test
-  public void inlineCanWithHandlersThrow() {
+  public void inlineCanWithHandlersThrow() throws Exception {
     // The base generated code will be:
     //
     //  int method(int a, int b, int c) {
diff --git a/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java b/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
index 1e35475..b9eef9c 100644
--- a/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
@@ -32,7 +32,7 @@
    * Third block: Return instruction
    *
    */
-  IRCode simpleCode() {
+  IRCode simpleCode() throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String returnType = "int";
@@ -62,7 +62,7 @@
   }
 
   @Test
-  public void removeBeforeNext() {
+  public void removeBeforeNext() throws Exception {
     IRCode code = simpleCode();
 
     ListIterator<BasicBlock> blocks = code.listIterator();
@@ -72,7 +72,7 @@
   }
 
   @Test
-  public void removeTwice() {
+  public void removeTwice() throws Exception {
     IRCode code = simpleCode();
 
     ListIterator<BasicBlock> blocks = code.listIterator();
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
index 90e2089..04a55b5 100644
--- a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -29,7 +29,7 @@
 
 public class SplitBlockTest extends SmaliTestBase {
 
-  TestApplication codeWithoutCatchHandlers() {
+  TestApplication codeWithoutCatchHandlers() throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String returnType = "int";
@@ -68,7 +68,7 @@
   }
 
   @Test
-  public void noCatchHandlers() {
+  public void noCatchHandlers() throws Exception {
     final int initialBlockCount = 1;
     final int argumentInstructions = 2;
     final int firstBlockInstructions = 6;
@@ -102,7 +102,7 @@
   }
 
   @Test
-  public void noCatchHandlersSplitThree() {
+  public void noCatchHandlersSplitThree() throws Exception {
     final int initialBlockCount = 1;
     final int argumentInstructions = 2;
     final int firstBlockInstructions = 6;
@@ -136,7 +136,7 @@
     }
   }
 
-  TestApplication codeWithCatchHandlers(boolean shouldThrow, boolean twoGuards) {
+  TestApplication codeWithCatchHandlers(boolean shouldThrow, boolean twoGuards) throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String secondGuard = twoGuards ?
@@ -192,7 +192,7 @@
     assertEquals(throwing, block.hasCatchHandlers());
   }
 
-  public void runCatchHandlerTest(boolean codeThrows, boolean twoGuards) {
+  public void runCatchHandlerTest(boolean codeThrows, boolean twoGuards) throws Exception {
     final int secondBlockInstructions = 4;
     final int initialBlockCount = 5;
     // Try split between all instructions in second block.
@@ -223,14 +223,15 @@
   }
 
   @Test
-  public void catchHandlers() {
+  public void catchHandlers() throws Exception {
     runCatchHandlerTest(false, false);
     runCatchHandlerTest(true, false);
     runCatchHandlerTest(false, true);
     runCatchHandlerTest(true, true);
   }
 
-  public void runCatchHandlerSplitThreeTest(boolean codeThrows, boolean twoGuards) {
+  public void runCatchHandlerSplitThreeTest(boolean codeThrows, boolean twoGuards)
+      throws Exception {
     final int secondBlockInstructions = 4;
     final int initialBlockCount = 5;
     // Try split out all instructions in second block.
@@ -262,14 +263,14 @@
   }
 
   @Test
-  public void catchHandlersSplitThree() {
+  public void catchHandlersSplitThree() throws Exception {
     runCatchHandlerSplitThreeTest(false, false);
     runCatchHandlerSplitThreeTest(true, false);
     runCatchHandlerSplitThreeTest(false, true);
     runCatchHandlerSplitThreeTest(true, true);
   }
 
-  TestApplication codeWithIf(boolean hitTrueBranch) {
+  TestApplication codeWithIf(boolean hitTrueBranch) throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String returnType = "int";
@@ -309,7 +310,7 @@
     return new TestApplication(application, method, code, valueNumberGenerator, options);
   }
 
-  public void runWithIfTest(boolean hitTrueBranch) {
+  public void runWithIfTest(boolean hitTrueBranch) throws Exception {
     final int initialBlockCount = 4;
     final int argumentInstructions = 2;
     final int firstBlockInstructions = 3;
@@ -343,12 +344,12 @@
   }
 
   @Test
-  public void withIf() {
+  public void withIf() throws Exception {
     runWithIfTest(false);
     runWithIfTest(true);
   }
 
-  public void splitBeforeReturn(boolean hitTrueBranch) {
+  public void splitBeforeReturn(boolean hitTrueBranch) throws Exception {
     TestApplication test = codeWithIf(hitTrueBranch);
     IRCode code = test.code;
     // Locate the exit block and split before the return (the first instruction in the block).
@@ -375,12 +376,12 @@
   }
 
   @Test
-  public void splitBeforeReturn() {
+  public void splitBeforeReturn() throws Exception {
     splitBeforeReturn(false);
     splitBeforeReturn(true);
   }
 
-  TestApplication codeWithSwitch(boolean hitCase) {
+  TestApplication codeWithSwitch(boolean hitCase) throws Exception {
     SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
     String returnType = "int";
@@ -428,7 +429,7 @@
     return new TestApplication(application, method, code, valueNumberGenerator, options);
   }
 
-  public void runWithSwitchTest(boolean hitCase) {
+  public void runWithSwitchTest(boolean hitCase) throws Exception {
     final int initialBlockCount = 5;
     final int argumentInstructions = 1;
     final int firstBlockInstructions = 2;
@@ -462,7 +463,7 @@
   }
 
   @Test
-  public void withSwitch() {
+  public void withSwitch() throws Exception {
     runWithSwitchTest(false);
     runWithSwitchTest(true);
   }
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index 04d84e4..1573b34 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -5,6 +5,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.R8;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -131,7 +132,7 @@
   }
 
   protected static DexApplication process(DexApplication app, InternalOptions options)
-      throws IOException, ProguardRuleParserException, ExecutionException {
+      throws IOException, CompilationException, ExecutionException {
     return ToolHelper.optimizeWithR8(app, new AppInfoWithSubtyping(app), options);
   }
 }
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 d7d30d4..619f808 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -538,13 +538,13 @@
   }
 
   public static AndroidApp generateApplication(List<String> classes, int minApi, int methodCount)
-      throws IOException, ExecutionException {
+      throws IOException, ExecutionException, CompilationException {
     return generateApplication(classes, minApi, false, methodCount);
   }
 
   private static AndroidApp generateApplication(
       List<String> classes, int minApi, boolean intermediate, int methodCount)
-      throws IOException, ExecutionException {
+      throws IOException, ExecutionException, CompilationException {
     Timing timing = new Timing("MainDexListTests");
     InternalOptions options = new InternalOptions();
     options.minApiLevel = minApi;
diff --git a/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java b/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
index ca30f76..909f574 100644
--- a/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
+++ b/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
@@ -26,7 +26,7 @@
 public class CatchSuccessorFallthroughTest extends SmaliTestBase {
 
   @Test
-  public void catchSuccessorFallthroughTest() {
+  public void catchSuccessorFallthroughTest() throws Exception {
 
     SmaliBuilder builder = new SmaliBuilder("Test");
 
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 0322407..f35dd21 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -6,6 +6,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.R8;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
@@ -431,7 +432,7 @@
   protected DexApplication processApplication(DexApplication application, InternalOptions options) {
     try {
       return ToolHelper.optimizeWithR8(application, new AppInfoWithSubtyping(application), options);
-    } catch (IOException | ProguardRuleParserException | ExecutionException e) {
+    } catch (IOException | CompilationException | ExecutionException e) {
       throw new RuntimeException(e);
     }
   }