Merge "Add tests for default ctor of Serializable and Externalizable."
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 6fe8ad0..d28e045 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
@@ -42,10 +43,11 @@
     try {
       DexApplication application =
           new ApplicationReader(app, options, timing).read(executor).toDirect();
-      AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
+      AppView<? extends AppInfoWithSubtyping> appView =
+          new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
       RootSet mainDexRootSet =
-          new RootSetBuilder(appInfo, application, options.mainDexKeepRules, options).run(executor);
-      Enqueuer enqueuer = new Enqueuer(appInfo, GraphLense.getIdentityLense(), options, true);
+          new RootSetBuilder(appView, application, options.mainDexKeepRules, options).run(executor);
+      Enqueuer enqueuer = new Enqueuer(appView, options, true);
       AppInfoWithLiveness mainDexAppInfo = enqueuer.traceMainDex(mainDexRootSet, executor, timing);
       // LiveTypes is the result.
       Set<DexType> mainDexClasses =
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index 78cb75a..4c52b53 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -5,9 +5,11 @@
 
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.RootSetBuilder;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.ExceptionUtils;
@@ -76,21 +78,21 @@
   private static void run(
       R8Command command, Set<String> descriptors, InternalOptions options, ExecutorService executor)
       throws IOException {
+    assert !options.forceProguardCompatibility;
     Timing timing = new Timing("PrintSeeds");
     try {
       DexApplication application =
           new ApplicationReader(command.getInputApp(), options, timing).read(executor).toDirect();
-      AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
+      AppView<? extends AppInfoWithSubtyping> appView =
+          new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
       RootSet rootSet =
           new RootSetBuilder(
-                  appInfo, application, options.proguardConfiguration.getRules(), options)
+                  appView, application, options.proguardConfiguration.getRules(), options)
               .run(executor);
-      Enqueuer enqueuer = new Enqueuer(appInfo, GraphLense.getIdentityLense(), options, false);
-      appInfo = enqueuer.traceApplication(rootSet, executor, timing);
+      Enqueuer enqueuer = new Enqueuer(appView, options);
+      AppInfoWithLiveness appInfo = enqueuer.traceApplication(rootSet, executor, timing);
       RootSetBuilder.writeSeeds(
-          appInfo.withLiveness(),
-          System.out,
-          type -> descriptors.contains(type.toDescriptorString()));
+          appInfo, System.out, type -> descriptors.contains(type.toDescriptorString()));
     } catch (ExecutionException e) {
       throw R8.unwrapExecutionException(e);
     }
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 8be6306..ec863ee 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -275,24 +275,15 @@
         // kotlin metadata annotation is removed.
         computeKotlinInfoForProgramClasses(application, appView.appInfo());
 
-        final ProguardConfiguration.Builder compatibility =
+        ProguardConfiguration.Builder compatibility =
             ProguardConfiguration.builder(application.dexItemFactory, options.reporter);
 
         rootSet =
             new RootSetBuilder(
-                    appView.appInfo(),
-                    application,
-                    options.proguardConfiguration.getRules(),
-                    options)
+                    appView, application, options.proguardConfiguration.getRules(), options)
                 .run(executorService);
 
-        Enqueuer enqueuer =
-            new Enqueuer(
-                appView.appInfo(),
-                appView.graphLense(),
-                options,
-                options.forceProguardCompatibility,
-                compatibility);
+        Enqueuer enqueuer = new Enqueuer(appView, options, compatibility);
         appView.setAppInfo(enqueuer.traceApplication(rootSet, executorService, timing));
         if (options.proguardConfiguration.isPrintSeeds()) {
           ByteArrayOutputStream bytes = new ByteArrayOutputStream();
@@ -416,10 +407,10 @@
 
       if (!options.mainDexKeepRules.isEmpty()) {
         appView.setAppInfo(new AppInfoWithSubtyping(application));
-        Enqueuer enqueuer = new Enqueuer(appView.appInfo(), appView.graphLense(), options, true);
+        Enqueuer enqueuer = new Enqueuer(appView, options, true);
         // Lets find classes which may have code executed before secondary dex files installation.
         RootSet mainDexRootSet =
-            new RootSetBuilder(appView.appInfo(), application, options.mainDexKeepRules, options)
+            new RootSetBuilder(appView, application, options.mainDexKeepRules, options)
                 .run(executorService);
         AppInfoWithLiveness mainDexAppInfo =
             enqueuer.traceMainDex(mainDexRootSet, executorService, timing);
@@ -448,12 +439,7 @@
       if (options.enableTreeShaking || options.enableMinification) {
         timing.begin("Post optimization code stripping");
         try {
-          Enqueuer enqueuer =
-              new Enqueuer(
-                  appView.appInfo(),
-                  appView.graphLense(),
-                  options,
-                  options.forceProguardCompatibility);
+          Enqueuer enqueuer = new Enqueuer(appView, options);
           appView.setAppInfo(enqueuer.traceApplication(rootSet, executorService, timing));
 
           AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index aeaac60..3dbaa56 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.4.1-dev";
+  public static final String LABEL = "1.4.2-dev";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/code/CheckCast.java b/src/main/java/com/android/tools/r8/code/CheckCast.java
index 9416a7b..57151fa 100644
--- a/src/main/java/com/android/tools/r8/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/code/CheckCast.java
@@ -38,6 +38,11 @@
   }
 
   @Override
+  public boolean isCheckCast() {
+    return true;
+  }
+
+  @Override
   public void registerUse(UseRegistry registry) {
     registry.registerCheckCast(getType());
   }
diff --git a/src/main/java/com/android/tools/r8/code/ConstString.java b/src/main/java/com/android/tools/r8/code/ConstString.java
index d165de5..ed7847a 100644
--- a/src/main/java/com/android/tools/r8/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/code/ConstString.java
@@ -45,6 +45,11 @@
   }
 
   @Override
+  public boolean isConstString() {
+    return true;
+  }
+
+  @Override
   public String toString(ClassNameMapper naming) {
     return formatString("v" + AA + ", \"" + BBBB.toString() + "\"");
   }
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 c8b5154..fbb93ca 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -122,6 +122,14 @@
     this.offset = offset;
   }
 
+  public boolean isCheckCast() {
+    return false;
+  }
+
+  public boolean isConstString() {
+    return false;
+  }
+
   public boolean isSimpleNop() {
     return !isPayload() && this instanceof Nop;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
index 0c80f96..6b368b3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
@@ -31,7 +31,7 @@
   }
 
   @Override
-  TypeLatticeElement asNullable() {
+  public TypeLatticeElement asNullable() {
     return isNullable() ? this : new ArrayTypeLatticeElement(type, true);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/BottomTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/BottomTypeLatticeElement.java
index b9a45d2..dba0a2a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/BottomTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/BottomTypeLatticeElement.java
@@ -14,7 +14,7 @@
   }
 
   @Override
-  TypeLatticeElement asNullable() {
+  public TypeLatticeElement asNullable() {
     return this;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
index 72825a9..de75d5f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeLatticeElement.java
@@ -27,7 +27,7 @@
   }
 
   @Override
-  TypeLatticeElement asNullable() {
+  public TypeLatticeElement asNullable() {
     return isNullable() ? this : new ClassTypeLatticeElement(type, true, interfaces);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/NullLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/NullLatticeElement.java
new file mode 100644
index 0000000..caad0a2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/NullLatticeElement.java
@@ -0,0 +1,65 @@
+// 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.analysis.type;
+
+/**
+ * Encodes the following lattice.
+ *
+ * <pre>
+ *          MAYBE NULL
+ *          /        \
+ *   DEFINITELY     DEFINITELY
+ *      NULL         NOT NULL
+ *          \        /
+ *            BOTTOM
+ * </pre>
+ */
+public class NullLatticeElement {
+
+  private static final NullLatticeElement BOTTOM = new NullLatticeElement();
+  private static final NullLatticeElement DEFINITELY_NULL = new NullLatticeElement();
+  private static final NullLatticeElement DEFINITELY_NOT_NULL = new NullLatticeElement();
+  private static final NullLatticeElement MAYBE_NULL = new NullLatticeElement();
+
+  private NullLatticeElement() {}
+
+  public boolean isDefinitelyNull() {
+    return this == DEFINITELY_NULL;
+  }
+
+  public boolean isDefinitelyNotNull() {
+    return this == DEFINITELY_NOT_NULL;
+  }
+
+  public NullLatticeElement leastUpperBound(NullLatticeElement other) {
+    if (this == BOTTOM) {
+      return other;
+    }
+    if (this == other || other == BOTTOM) {
+      return this;
+    }
+    return MAYBE_NULL;
+  }
+
+  public boolean lessThanOrEqual(NullLatticeElement other) {
+    return leastUpperBound(other) == other;
+  }
+
+  static NullLatticeElement bottom() {
+    return BOTTOM;
+  }
+
+  static NullLatticeElement definitelyNull() {
+    return DEFINITELY_NULL;
+  }
+
+  static NullLatticeElement definitelyNotNull() {
+    return DEFINITELY_NOT_NULL;
+  }
+
+  static NullLatticeElement maybeNull() {
+    return MAYBE_NULL;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java
index 7cd6a6a..53c67e4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java
@@ -18,7 +18,7 @@
   }
 
   @Override
-  TypeLatticeElement asNullable() {
+  public TypeLatticeElement asNullable() {
     return TypeLatticeElement.TOP;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
index 387d6c5..ef36c4e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
@@ -49,7 +49,7 @@
   }
 
   @Override
-  TypeLatticeElement asNullable() {
+  public TypeLatticeElement asNullable() {
     assert isNull() || isReferenceInstance();
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TopTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TopTypeLatticeElement.java
index b028963..7aba6dc 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TopTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TopTypeLatticeElement.java
@@ -14,7 +14,7 @@
   }
 
   @Override
-  TypeLatticeElement asNullable() {
+  public TypeLatticeElement asNullable() {
     return this;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
index ed30610..a09039c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
@@ -37,6 +37,7 @@
   public static final ReferenceTypeLatticeElement REFERENCE =
       ReferenceTypeLatticeElement.getReferenceTypeLatticeElement();
 
+  // TODO(b/72693244): Switch to NullLatticeElement.
   private final boolean isNullable;
 
   TypeLatticeElement(boolean isNullable) {
@@ -47,12 +48,22 @@
     return isNullable;
   }
 
+  public NullLatticeElement nullElement() {
+    if (isNull()) {
+      return NullLatticeElement.definitelyNull();
+    }
+    if (!isNullable()) {
+      return NullLatticeElement.definitelyNotNull();
+    }
+    return NullLatticeElement.maybeNull();
+  }
+
   /**
    * Defines how to join with null or switch to nullable lattice element.
    *
    * @return {@link TypeLatticeElement} a result of joining with null.
    */
-  abstract TypeLatticeElement asNullable();
+  public abstract TypeLatticeElement asNullable();
 
   /**
    * Defines how to switch to non-nullable lattice element.
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index ae508e8..a8c9b2f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.ir.code.IRCode.INSTRUCTION_NUMBER_DELTA;
 
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -69,6 +70,11 @@
     return true;
   }
 
+  public boolean verifyTypes(AppInfo appInfo) {
+    assert instructions.stream().allMatch(instruction -> instruction.verifyTypes(appInfo));
+    return true;
+  }
+
   public void setLocalsAtEntry(Int2ReferenceMap<DebugLocalInfo> localsAtEntry) {
     this.localsAtEntry = localsAtEntry;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index 4c1824b..8d461bd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -123,6 +123,47 @@
   }
 
   @Override
+  public boolean verifyTypes(AppInfo appInfo) {
+    TypeLatticeElement inType = object().getTypeLattice();
+
+    // TODO(b/72693244): There should never be a value with imprecise type lattice.
+    if (!inType.isPreciseType()) {
+      return true;
+    }
+
+    TypeLatticeElement outType = outValue().getTypeLattice();
+    TypeLatticeElement castType =
+        TypeLatticeElement.fromDexType(getType(), appInfo, inType.isNullable());
+
+    if (TypeLatticeElement.lessThanOrEqual(appInfo, inType, castType)) {
+      // Cast can be removed. Check that it is sound to replace all users of the out-value by the
+      // in-value.
+      assert TypeLatticeElement.lessThanOrEqual(appInfo, inType, outType);
+
+      // TODO(b/72693244): Consider checking equivalence. This requires that the types are always
+      // as precise as possible, though, meaning that almost all changes to the IR must be followed
+      // by a fix-point analysis.
+      // assert outType.equals(inType);
+    } else {
+      // We don't have enough information to remove the cast. Check that the out-value does not
+      // have a more precise type than the cast-type.
+      assert castType.asNullable().equals(outType.asNullable());
+
+      // Check soundness of null information.
+      assert inType.nullElement().lessThanOrEqual(outType.nullElement());
+
+      // Since we cannot remove the cast the in-value must be different from null.
+      assert !inType.isNull();
+
+      // TODO(b/72693244): Consider checking equivalence. This requires that the types are always
+      // as precise as possible, though, meaning that almost all changes to the IR must be followed
+      // by a fix-point analysis.
+      // assert outType.equals(castType);
+    }
+    return true;
+  }
+
+  @Override
   public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
     helper.loadInValues(this, it);
     helper.storeOutValue(this, it);
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index b1e4e28..92e2ddb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.ir.code;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -23,6 +24,7 @@
 import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 public class IRCode {
@@ -408,6 +410,7 @@
     assert consistentDefUseChains();
     assert validThrowingInstructions();
     assert noCriticalEdges();
+    assert noBottomTypeLatticeLeft();
     return true;
   }
 
@@ -421,6 +424,11 @@
     return true;
   }
 
+  public boolean verifyTypes(AppInfo appInfo) {
+    assert blocks.stream().allMatch(block -> block.verifyTypes(appInfo));
+    return true;
+  }
+
   private boolean noCriticalEdges() {
     for (BasicBlock block : blocks) {
       List<BasicBlock> predecessors = block.getPredecessors();
@@ -606,6 +614,29 @@
     return true;
   }
 
+  private boolean noBottomTypeLatticeLeft() {
+    return verifySSATypeLattice(lattice -> !lattice.isBottom());
+  }
+
+  private boolean noImpreciseTypeLatticeLeft() {
+    return verifySSATypeLattice(TypeLatticeElement::isPreciseType);
+  }
+
+  private boolean verifySSATypeLattice(Predicate<TypeLatticeElement> tester) {
+    for (BasicBlock block : blocks) {
+      for (Instruction instruction : block.getInstructions()) {
+        Value outValue = instruction.outValue();
+        if (outValue != null) {
+          assert tester.test(outValue.getTypeLattice());
+        }
+      }
+      for (Phi phi : block.getPhis()) {
+        assert tester.test(phi.getTypeLattice());
+      }
+    }
+    return true;
+  }
+
   public InstructionIterator instructionIterator() {
     return new IRCodeInstructionsIterator(this);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 8f1a004..d3355b4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -1089,6 +1089,11 @@
         "Implement type lattice evaluation for: " + getInstructionName());
   }
 
+  public boolean verifyTypes(AppInfo appInfo) {
+    // TODO(b/72693244): for instructions with invariant out type, we can verify type directly here.
+    return true;
+  }
+
   /**
    * Indicates whether the instruction throws a NullPointerException if the object denoted by the
    * given value is null at runtime execution.
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 7e5759a..3c657ec 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
@@ -790,6 +790,9 @@
     if (devirtualizer != null) {
       devirtualizer.devirtualizeInvokeInterface(code, method.method.getHolder());
     }
+
+    assert code.verifyTypes(appInfo);
+
     codeRewriter.removeCasts(code);
     codeRewriter.rewriteLongCompareAndRequireNonNull(code, options);
     codeRewriter.commonSubexpressionElimination(code);
@@ -834,6 +837,8 @@
       assert code.isConsistentSSA();
     }
 
+    assert code.verifyTypes(appInfo);
+
     if (classInliner != null) {
       // Class inliner should work before lambda merger, so if it inlines the
       // lambda, it does not get collected by merger.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 02c06da..f8b85de 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1661,25 +1661,29 @@
         TypeLatticeElement outTypeLattice = outValue.getTypeLattice();
         TypeLatticeElement castTypeLattice =
             TypeLatticeElement.fromDexType(castType, appInfo, inTypeLattice.isNullable());
-        // 1) Trivial cast.
-        //   A a = ...
-        //   A a' = (A) a;
-        // 2) Up-cast: we already have finer type info.
-        //   A < B
-        //   A a = ...
-        //   B b = (B) a;
+
+        assert inTypeLattice.nullElement().lessThanOrEqual(outTypeLattice.nullElement());
+
         if (TypeLatticeElement.lessThanOrEqual(appInfo, inTypeLattice, castTypeLattice)) {
-          assert outTypeLattice.equals(inTypeLattice);
+          // 1) Trivial cast.
+          //   A a = ...
+          //   A a' = (A) a;
+          // 2) Up-cast: we already have finer type info.
+          //   A < B
+          //   A a = ...
+          //   B b = (B) a;
+          assert TypeLatticeElement.lessThanOrEqual(appInfo, inTypeLattice, outTypeLattice);
           needToRemoveTrivialPhis = needToRemoveTrivialPhis || outValue.numberOfPhiUsers() != 0;
           removeOrReplaceByDebugLocalWrite(checkCast, it, inValue, outValue);
-          continue;
+        } else {
+          // Otherwise, keep the checkcast to preserve verification errors. E.g., down-cast:
+          // A < B < C
+          // c = ...        // Even though we know c is of type A,
+          // a' = (B) c;    // (this could be removed, since chained below.)
+          // a'' = (A) a';  // this should remain for runtime verification.
+          assert !inTypeLattice.isNull();
+          assert outTypeLattice.asNullable().equals(castTypeLattice.asNullable());
         }
-        // Otherwise, keep the checkcast to preserve verification errors. E.g., down-cast:
-        // A < B < C
-        // c = ...        // Even though we know c is of type A,
-        // a' = (B) c;    // (this could be removed, since chained below.)
-        // a'' = (A) a';  // this should remain for runtime verification.
-        assert outTypeLattice.equals(castTypeLattice);
       }
     }
     // ... v1
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 0ef4597..6999f9a 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
@@ -137,6 +137,7 @@
     // of roots to avoid infinite inlining. Looping makes possible for some roots to
     // become eligible after other roots are inlined.
 
+    boolean anyInlinedMethods = false;
     boolean repeat;
     do {
       repeat = false;
@@ -169,18 +170,24 @@
         }
 
         // Inline the class instance.
-        boolean anyInlinedMethods = processor.processInlining(code, inliner);
+        anyInlinedMethods |= processor.processInlining(code, inliner);
 
         // Restore normality.
         code.removeAllTrivialPhis();
         assert code.isConsistentSSA();
-        if (anyInlinedMethods) {
-          codeRewriter.simplifyIf(code);
-        }
         rootsIterator.remove();
         repeat = true;
       }
     } while (repeat);
+
+    if (anyInlinedMethods) {
+      // If a method was inlined we may be able to remove check-cast instructions because we may
+      // have more information about the types of the arguments at the call site. This is
+      // particularly important for bridge methods.
+      codeRewriter.removeCasts(code);
+      // If a method was inlined we may be able to prune additional branches.
+      codeRewriter.simplifyIf(code);
+    }
   }
 
   private boolean isClassEligible(AppInfo appInfo, DexClass clazz) {
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 9145de1..bfb2b95 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo.ResolutionResult;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.Descriptor;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexApplication;
@@ -95,7 +96,7 @@
   private boolean tracingMainDex = false;
 
   private final AppInfoWithSubtyping appInfo;
-  private final GraphLense graphLense;
+  private final AppView<? extends AppInfoWithSubtyping> appView;
   private final InternalOptions options;
   private RootSet rootSet;
 
@@ -220,25 +221,34 @@
    */
   private final ProguardConfiguration.Builder compatibility;
 
-  public Enqueuer(
-      AppInfoWithSubtyping appInfo,
-      GraphLense graphLense,
-      InternalOptions options,
-      boolean forceProguardCompatibility) {
-    this(appInfo, graphLense, options, forceProguardCompatibility, null);
+  public Enqueuer(AppView<? extends AppInfoWithSubtyping> appView, InternalOptions options) {
+    this(appView, options, options.forceProguardCompatibility, null);
   }
 
   public Enqueuer(
-      AppInfoWithSubtyping appInfo,
-      GraphLense graphLense,
+      AppView<? extends AppInfoWithSubtyping> appView,
+      InternalOptions options,
+      ProguardConfiguration.Builder compatibility) {
+    this(appView, options, options.forceProguardCompatibility, compatibility);
+  }
+
+  public Enqueuer(
+      AppView<? extends AppInfoWithSubtyping> appView,
+      InternalOptions options,
+      boolean forceProguardCompatibility) {
+    this(appView, options, forceProguardCompatibility, null);
+  }
+
+  public Enqueuer(
+      AppView<? extends AppInfoWithSubtyping> appView,
       InternalOptions options,
       boolean forceProguardCompatibility,
       ProguardConfiguration.Builder compatibility) {
-    this.appInfo = appInfo;
-    this.graphLense = graphLense;
+    this.appInfo = appView.appInfo();
+    this.appView = appView;
     this.compatibility = compatibility;
-    this.options = options;
     this.forceProguardCompatibility = forceProguardCompatibility;
+    this.options = options;
   }
 
   private void enqueueRootItems(Map<DexDefinition, ProguardKeepRule> items) {
@@ -1272,7 +1282,7 @@
         numOfLiveItemsAfterProcessing += (long) liveFields.items.size();
         if (numOfLiveItemsAfterProcessing > numOfLiveItems) {
           RootSetBuilder consequentSetBuilder =
-              new RootSetBuilder(appInfo, rootSet.ifRules, options);
+              new RootSetBuilder(appView, rootSet.ifRules, options);
           ConsequentRootSet consequentRootSet = consequentSetBuilder.runForIfRules(
               executorService, liveTypes, liveMethods.getItems(), liveFields.getItems());
           enqueueRootItems(consequentRootSet.noShrinking);
@@ -1518,7 +1528,7 @@
   private void handleReflectiveBehavior(DexEncodedMethod method) {
     DexType originHolder = method.method.holder;
     Origin origin = appInfo.originFor(originHolder);
-    IRCode code = method.buildIR(appInfo, graphLense, options, origin);
+    IRCode code = method.buildIR(appInfo, appView.graphLense(), options, origin);
     code.instructionIterator().forEachRemaining(this::handleReflectiveBehavior);
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
index 5d610a8..2aff5df 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -320,11 +320,12 @@
   }
 
   protected StringBuilder append(StringBuilder builder, boolean includeMemberRules) {
-    StringUtils.appendNonEmpty(builder, "@", classAnnotation, null);
-    StringUtils.appendNonEmpty(builder, "", classAccessFlags, null);
-    StringUtils.appendNonEmpty(builder, "!", negatedClassAccessFlags.toString().replace(" ", " !"),
-        null);
-    if (builder.length() > 0) {
+    boolean needsSpaceBeforeClassType =
+        StringUtils.appendNonEmpty(builder, "@", classAnnotation, null)
+            | StringUtils.appendNonEmpty(builder, "", classAccessFlags, null)
+            | StringUtils.appendNonEmpty(
+                builder, "!", negatedClassAccessFlags.toString().replace(" ", " !"), null);
+    if (needsSpaceBeforeClassType) {
       builder.append(' ');
     }
     if (classTypeNegated) {
@@ -339,12 +340,12 @@
       builder.append(' ');
       builder.append(inheritanceClassName);
     }
-    if (includeMemberRules) {
-      builder.append(" {\n");
+    if (includeMemberRules && !memberRules.isEmpty()) {
+      builder.append(" {").append(System.lineSeparator());
       memberRules.forEach(memberRule -> {
         builder.append("  ");
         builder.append(memberRule);
-        builder.append(";\n");
+        builder.append(";").append(System.lineSeparator());
       });
       builder.append("}");
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 30def9e..e6f6a17 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
@@ -51,7 +52,7 @@
 
 public class RootSetBuilder {
 
-  private final AppInfo appInfo;
+  private final AppView<? extends AppInfo> appView;
   private final DirectMappedDexApplication application;
   private final Collection<ProguardConfigurationRule> rules;
   private final Map<DexDefinition, ProguardKeepRule> noShrinking = new IdentityHashMap<>();
@@ -76,22 +77,20 @@
   private final Set<ProguardIfRule> ifRules = Sets.newIdentityHashSet();
 
   public RootSetBuilder(
-      AppInfo appInfo,
+      AppView<? extends AppInfo> appView,
       DexApplication application,
       List<ProguardConfigurationRule> rules,
       InternalOptions options) {
-    this.appInfo = appInfo;
+    this.appView = appView;
     this.application = application.asDirect();
     this.rules = rules == null ? null : Collections.unmodifiableCollection(rules);
     this.options = options;
   }
 
   RootSetBuilder(
-      AppInfo appInfo,
-      Set<ProguardIfRule> ifRules,
-      InternalOptions options) {
-    this.appInfo = appInfo;
-    this.application = appInfo.app.asDirect();
+      AppView<? extends AppInfo> appView, Set<ProguardIfRule> ifRules, InternalOptions options) {
+    this.appView = appView;
+    this.application = appView.appInfo().app.asDirect();
     this.rules = Collections.unmodifiableCollection(ifRules);
     this.options = options;
   }
@@ -341,13 +340,14 @@
       Set<DexEncodedMethod> liveMethods,
       Set<DexEncodedField> liveFields) throws ExecutionException {
     application.timing.begin("Find consequent items for -if rules...");
-    Function<DexType, DexClass> definitionForWithLiveTypes = type -> {
-      DexClass clazz = appInfo.definitionFor(type);
-      if (clazz != null && liveTypes.contains(clazz.type)) {
-        return clazz;
-      }
-      return null;
-    };
+    Function<DexType, DexClass> definitionForWithLiveTypes =
+        type -> {
+          DexClass clazz = appView.appInfo().definitionFor(type);
+          if (clazz != null && liveTypes.contains(clazz.type)) {
+            return clazz;
+          }
+          return null;
+        };
     try {
       List<Future<?>> futures = new ArrayList<>();
       if (rules != null) {
@@ -358,7 +358,7 @@
           // -keep rule may vary (due to back references). So, we need to try all pairs of -if rule
           // and live types.
           for (DexType currentLiveType : liveTypes) {
-            DexClass currentLiveClass = appInfo.definitionFor(currentLiveType);
+            DexClass currentLiveClass = appView.appInfo().definitionFor(currentLiveType);
             if (currentLiveClass == null) {
               continue;
             }
@@ -550,7 +550,7 @@
     out.close();
   }
 
-  private static boolean satisfyClassType(ProguardConfigurationRule rule, DexClass clazz) {
+  private boolean satisfyClassType(ProguardConfigurationRule rule, DexClass clazz) {
     return rule.getClassType().matches(clazz) != rule.getClassTypeNegated();
   }
 
@@ -723,7 +723,7 @@
     if (type.isPrimitiveType()) {
       return;
     }
-    DexClass definition = appInfo.definitionFor(type);
+    DexClass definition = appView.appInfo().definitionFor(type);
     if (definition == null || definition.isLibraryClass()) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index 4f0aa2c..244639e 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -59,9 +59,10 @@
     return builder.toString();
   }
 
-  public static void appendNonEmpty(StringBuilder builder, String pre, Object item, String post) {
+  public static boolean appendNonEmpty(
+      StringBuilder builder, String pre, Object item, String post) {
     if (item == null) {
-      return;
+      return false;
     }
     String text = item.toString();
     if (!text.isEmpty()) {
@@ -72,7 +73,9 @@
       if (post != null) {
         builder.append(post);
       }
+      return true;
     }
+    return false;
   }
 
   public static StringBuilder appendIndent(StringBuilder builder, String subject, int indent) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/checkcast/RemoveCheckCastAfterClassInlining.java b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/RemoveCheckCastAfterClassInlining.java
new file mode 100644
index 0000000..d52725e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/checkcast/RemoveCheckCastAfterClassInlining.java
@@ -0,0 +1,79 @@
+// 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.optimize.checkcast;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class RemoveCheckCastAfterClassInlining extends TestBase {
+
+  @Test
+  public void test() throws Exception {
+    AndroidApp input = readClasses(Lambda.class, Lambda.Consumer.class);
+    AndroidApp output =
+        compileWithR8(
+            input,
+            keepMainProguardConfiguration(Lambda.class),
+            options -> options.enableMinification = false);
+
+    // Extract main method.
+    CodeInspector inspector = new CodeInspector(output);
+    ClassSubject classSubject = inspector.clazz(Lambda.class);
+    MethodSubject methodSubject = classSubject.mainMethod();
+    assertThat(methodSubject, isPresent());
+
+    DexEncodedMethod method = methodSubject.getMethod();
+    assertTrue(method.hasCode());
+
+    DexCode code = method.getCode().asDexCode();
+    int numberOfConstStringInstructions = 0;
+    for (Instruction instruction : code.instructions) {
+      // Make sure that we do not load a const-string and then subsequently use a check-cast
+      // instruction to check if it is actually a string.
+      assertFalse(instruction.isCheckCast());
+      if (instruction.isConstString()) {
+        numberOfConstStringInstructions++;
+      }
+    }
+
+    // Sanity check that load() was actually inlined.
+    assertThat(
+        classSubject.method("void", "load", ImmutableList.of(Lambda.Consumer.class.getName())),
+        not(isPresent()));
+    assertEquals(2, numberOfConstStringInstructions);
+  }
+}
+
+class Lambda {
+
+  interface Consumer<T> {
+    void accept(T value);
+  }
+
+  public static void main(String... args) {
+    load(s -> System.out.println(s));
+    // Other code…
+    load(s -> System.out.println(s));
+  }
+
+  public static void load(Consumer<String> c) {
+    c.accept("Hello!");
+  }
+}
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 932c09b..e340c98 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -75,20 +75,17 @@
 
     ExecutorService executor = ThreadUtils.getExecutorService(1);
 
-    AppInfoWithSubtyping appInfo = appView.appInfo();
-    RootSet rootSet = new RootSetBuilder(appInfo, program, configuration.getRules(), options)
-        .run(executor);
+    RootSet rootSet =
+        new RootSetBuilder(appView, program, configuration.getRules(), options).run(executor);
 
     if (options.proguardConfiguration.isAccessModificationAllowed()) {
       ClassAndMemberPublicizer.run(executor, timing, program, appView, rootSet);
       rootSet =
-          new RootSetBuilder(appInfo, program, configuration.getRules(), options).run(executor);
+          new RootSetBuilder(appView, program, configuration.getRules(), options).run(executor);
     }
 
-    Enqueuer enqueuer =
-        new Enqueuer(
-            appInfo, GraphLense.getIdentityLense(), options, options.forceProguardCompatibility);
-    appInfo = enqueuer.traceApplication(rootSet, executor, timing);
+    Enqueuer enqueuer = new Enqueuer(appView, options, options.forceProguardCompatibility);
+    AppInfoWithSubtyping appInfo = enqueuer.traceApplication(rootSet, executor, timing);
     return new Minifier(appInfo.withLiveness(), rootSet, Collections.emptySet(), options)
         .run(timing);
   }
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index fbf4e68..deb91d8 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.AsmTestBase;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -102,18 +103,18 @@
     InternalOptions options = new InternalOptions();
     AndroidApp app = readClassesAndAsmDump(CLASSES, ASM_CLASSES);
     DexApplication application = new ApplicationReader(app, options, timing).read().toDirect();
-    AppInfoWithSubtyping appInfoWithSubtyping = new AppInfoWithSubtyping(application);
+    AppView<? extends AppInfoWithSubtyping> appView =
+        new AppView<>(new AppInfoWithSubtyping(application), GraphLense.getIdentityLense());
 
     ExecutorService executor = Executors.newSingleThreadExecutor();
-    RootSet rootSet = new RootSetBuilder(appInfoWithSubtyping, application,
-        buildKeepRuleForClass(Main.class, application.dexItemFactory), options).run(executor);
-    appInfo =
-        new Enqueuer(
-                appInfoWithSubtyping,
-                GraphLense.getIdentityLense(),
-                options,
-                options.forceProguardCompatibility)
-            .traceApplication(rootSet, executor, timing);
+    RootSet rootSet =
+        new RootSetBuilder(
+                appView,
+                application,
+                buildKeepRuleForClass(Main.class, application.dexItemFactory),
+                options)
+            .run(executor);
+    appInfo = new Enqueuer(appView, options).traceApplication(rootSet, executor, timing);
     // We do not run the tree pruner to ensure that the hierarchy is as designed and not modified
     // due to liveness.
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
index 1a74018..1f06e72 100644
--- a/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/whyareyoukeeping/WhyAreYouKeepingTest.java
@@ -33,7 +33,7 @@
     String expected = String.join(System.lineSeparator(), ImmutableList.of(
         "com.android.tools.r8.shaking.whyareyoukeeping.A",
         "|- is live because referenced in keep rule:",
-        "|    -keep  class com.android.tools.r8.shaking.whyareyoukeeping.A {",
+        "|    -keep class com.android.tools.r8.shaking.whyareyoukeeping.A {",
         "|      *;",
         "|    };",
         ""));
diff --git a/tools/test.py b/tools/test.py
index 6c15ea5..b57d00f 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -23,32 +23,32 @@
 
 def ParseOptions():
   result = optparse.OptionParser()
-  result.add_option('--no_internal',
+  result.add_option('--no-internal', '--no_internal',
       help='Do not run Google internal tests.',
       default=False, action='store_true')
-  result.add_option('--archive_failures',
+  result.add_option('--archive-failures', '--archive_failures',
       help='Upload test results to cloud storage on failure.',
       default=False, action='store_true')
-  result.add_option('--only_internal',
+  result.add_option('--only-internal', '--only_internal',
       help='Only run Google internal tests.',
       default=False, action='store_true')
-  result.add_option('--all_tests',
+  result.add_option('--all-tests', '--all_tests',
       help='Run tests in all configurations.',
       default=False, action='store_true')
   result.add_option('-v', '--verbose',
       help='Print test stdout to, well, stdout.',
       default=False, action='store_true')
-  result.add_option('--dex_vm',
+  result.add_option('--dex-vm', '--dex_vm',
       help='The android version of the vm to use. "all" will run the tests on '
            'all art vm versions (stopping after first failed execution)',
       default="default",
       choices=ALL_ART_VMS + ["all"])
-  result.add_option('--dex_vm_kind',
+  result.add_option('--dex-vm-kind', '--dex_vm_kind',
                     help='Whether to use host or target version of runtime',
                     default="host",
                     nargs=1,
                     choices=["host", "target"])
-  result.add_option('--one_line_per_test',
+  result.add_option('--one-line-per-test', '--one_line_per_test',
       help='Print a line before a tests starts and after it ends to stdout.',
       default=False, action='store_true')
   result.add_option('--tool',
@@ -58,32 +58,32 @@
   result.add_option('--jctf',
       help='Run JCTF tests with: "r8" (default) or "d8".',
       default=False, action='store_true')
-  result.add_option('--only_jctf',
+  result.add_option('--only-jctf', '--only_jctf',
       help='Run only JCTF tests with: "r8" (default) or "d8".',
       default=False, action='store_true')
-  result.add_option('--jctf_compile_only',
+  result.add_option('--jctf-compile-only', '--jctf_compile_only',
       help="Don't run, only compile JCTF tests.",
       default=False, action='store_true')
-  result.add_option('--aosp_jar',
+  result.add_option('--aosp-jar', '--aosp_jar',
       help='Run aosp_jar test.',
       default=False, action='store_true')
-  result.add_option('--disable_assertions',
+  result.add_option('--disable-assertions', '--disable_assertions',
       help='Disable assertions when running tests.',
       default=False, action='store_true')
-  result.add_option('--with_code_coverage',
+  result.add_option('--with-code-coverage', '--with_code_coverage',
       help='Enable code coverage with Jacoco.',
       default=False, action='store_true')
-  result.add_option('--test_dir',
+  result.add_option('--test-dir', '--test_dir',
       help='Use a custom directory for the test artifacts instead of a'
           ' temporary (which is automatically removed after the test).'
           ' Note that the directory will not be cleared before the test.')
-  result.add_option('--java_home',
+  result.add_option('--java-home', '--java_home',
       help='Use a custom java version to run tests.')
-  result.add_option('--generate_golden_files_to',
+  result.add_option('--generate-golden-files-to', '--generate_golden_files_to',
       help='Store dex files produced by tests in the specified directory.'
            ' It is aimed to be read on platforms with no host runtime available'
            ' for comparison.')
-  result.add_option('--use_golden_files_in',
+  result.add_option('--use-golden-files-in', '--use_golden_files_in',
       help='Download golden files hierarchy for this commit in the specified'
            ' location and use them instead of executing on host runtime.')