Fix class inliner bug that leads to unnecessary bail-outs for candidates not inheriting from Object

Change-Id: I2b35965264493083bd0e56e868c90c60a9cd227f
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 c2fdaaf..7ef077a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1094,12 +1094,23 @@
     private TrivialInitializer() {
     }
 
+    public boolean isTrivialInstanceInitializer() {
+      return false;
+    }
+
     // Defines instance trivial initialized, see details in comments
     // to CodeRewriter::computeInstanceInitializerInfo(...)
     public static final class TrivialInstanceInitializer extends TrivialInitializer {
 
       public static final TrivialInstanceInitializer INSTANCE =
           new TrivialInstanceInitializer();
+
+      private TrivialInstanceInitializer() {}
+
+      @Override
+      public boolean isTrivialInstanceInitializer() {
+        return true;
+      }
     }
 
     // Defines class trivial initialized, see details in comments
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index be28c03..2883353 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
 import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialClassInitializer;
-import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialInstanceInitializer;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
@@ -557,10 +556,23 @@
     //       any class initializers in superclass chain or in superinterfaces, see
     //       details in ClassInliner::computeClassEligible(...).
     if (eligibleClassDefinition.superType != appView.dexItemFactory().objectType) {
-      TrivialInitializer info = singleTarget.getOptimizationInfo().getTrivialInitializerInfo();
-      if (!(info instanceof TrivialInstanceInitializer)) {
+      DexClass superClass = appView.definitionFor(eligibleClassDefinition.superType);
+      if (superClass == null || !superClass.isProgramClass()) {
         return null;
       }
+
+      // At this point, we don't know which constructor in the super type that is invoked from the
+      // method. Therefore, we just check if all of the constructors in the super type are trivial.
+      for (DexEncodedMethod method : superClass.directMethods()) {
+        if (method.isInstanceInitializer()) {
+          TrivialInitializer trivialInitializerInfo =
+              method.getOptimizationInfo().getTrivialInitializerInfo();
+          if (trivialInitializerInfo == null
+              || !trivialInitializerInfo.isTrivialInstanceInitializer()) {
+            return null;
+          }
+        }
+      }
     }
 
     return singleTarget.getOptimizationInfo().getClassInlinerEligibility() != null
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerWithSimpleSuperTypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerWithSimpleSuperTypeTest.java
new file mode 100644
index 0000000..4cdbc84
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerWithSimpleSuperTypeTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.classinliner;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ClassInlinerWithSimpleSuperTypeTest extends TestBase {
+
+  private final boolean enableClassInlining;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{1}, enable class inlining: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+  }
+
+  public ClassInlinerWithSimpleSuperTypeTest(
+      boolean enableClassInlining, TestParameters parameters) {
+    this.enableClassInlining = enableClassInlining;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(ClassInlinerWithSimpleSuperTypeTest.class)
+        .addKeepMainRule(TestClass.class)
+        .addOptionsModification(options -> options.enableClassInlining = enableClassInlining)
+        .enableInliningAnnotations()
+        .enableMergeAnnotations()
+        .setMinApi(parameters.getRuntime())
+        .compile()
+        .inspect(this::verifyCandidateIsClassInlined)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void verifyCandidateIsClassInlined(CodeInspector inspector) {
+    ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+    assertThat(testClassSubject, isPresent());
+
+    MethodSubject mainMethodSubject = testClassSubject.mainMethod();
+    assertThat(mainMethodSubject, isPresent());
+    assertNotEquals(
+        enableClassInlining,
+        mainMethodSubject.streamInstructions().anyMatch(InstructionSubject::isNewInstance));
+
+    ClassSubject aClassSubject = inspector.clazz(A.class);
+    assertNotEquals(enableClassInlining, aClassSubject.isPresent());
+
+    ClassSubject bClassSubject = inspector.clazz(B.class);
+    assertNotEquals(enableClassInlining, bClassSubject.isPresent());
+
+    ClassSubject cClassSubject = inspector.clazz(C.class);
+    assertNotEquals(enableClassInlining, cClassSubject.isPresent());
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      new C("Hello world!").method();
+    }
+  }
+
+  @NeverMerge
+  static class A {}
+
+  @NeverMerge
+  static class B extends A {}
+
+  static class C extends B {
+
+    String greeting;
+
+    C(String greeting) {
+      if (greeting == null) {
+        throw new RuntimeException();
+      }
+      this.greeting = greeting;
+    }
+
+    @NeverInline
+    void method() {
+      System.out.println(greeting);
+    }
+  }
+}