Additional tests for api modeling, merging and class inlining

Bug: 138781768
Bug: 188388130
Change-Id: I4ffdac3127e515a4eb8a024f2b2497f89b55ef6e
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index e08fe12..3a11cbd 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -51,8 +51,10 @@
 import com.android.tools.r8.naming.MapVersion;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.repackaging.Repackaging.DefaultRepackagingConfiguration;
 import com.android.tools.r8.repackaging.Repackaging.RepackagingConfiguration;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -1299,8 +1301,10 @@
 
   public static class ApiModelTestingOptions {
 
-    // A mapping from methods to the api-level introducing them.
+    // A mapping from references to the api-level introducing them.
     public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>();
+    public Map<FieldReference, AndroidApiLevel> fieldApiMapping = new HashMap<>();
+    public Map<TypeReference, AndroidApiLevel> typeApiMapping = new HashMap<>();
 
     public boolean enableApiCallerIdentification = false;
   }
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelClassMergingWithDifferentApiFieldsTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelClassMergingWithDifferentApiFieldsTest.java
new file mode 100644
index 0000000..8045b48
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelClassMergingWithDifferentApiFieldsTest.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2021, 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.apimodeling;
+
+import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForType;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.reflect.Method;
+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 ApiModelClassMergingWithDifferentApiFieldsTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final String[] EXPECTED = new String[] {"Api::foo", "B::bar"};
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public ApiModelClassMergingWithDifferentApiFieldsTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, B.class, ApiSetter.class, Main.class)
+        .addLibraryClasses(Api.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        // TODO(b/138781768): Should not be merged
+        .addHorizontallyMergedClassesInspector(
+            inspector -> inspector.assertClassesMerged(A.class, B.class))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .apply(setMockApiLevelForType(Api.class, AndroidApiLevel.L_MR1))
+        .compile()
+        .addRunClasspathClasses(Api.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  public static class Api {
+
+    public void foo() {
+      System.out.println("Api::foo");
+    }
+  }
+
+  static class ApiSetter {
+    static void set() {
+      A.api = new Api();
+    }
+  }
+
+  static class A {
+
+    private static Api api;
+
+    public static void callApi() throws Exception {
+      // The reflective call here is to ensure that the setting of A's api level is not based on
+      // a method reference to `Api` and only because of the type reference in the field `api`.
+      Class<?> aClass =
+          Class.forName(
+              "com.android.tools.r8.apimodeling.ApiModelClassMergingWithDifferentApiFieldsTest_Api"
+                  .replace("_", "$"));
+      Method foo = aClass.getDeclaredMethod("foo");
+      foo.invoke(api);
+    }
+  }
+
+  public static class B {
+
+    @NeverInline
+    public static void bar() {
+      System.out.println("B::bar");
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) throws Exception {
+      ApiSetter.set();
+      A.callApi();
+      B.bar();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelClassMergingWithDifferentApiMethodsTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelClassMergingWithDifferentApiMethodsTest.java
new file mode 100644
index 0000000..b56d6ae
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelClassMergingWithDifferentApiMethodsTest.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2021, 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.apimodeling;
+
+import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForMethod;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.reflect.Method;
+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 ApiModelClassMergingWithDifferentApiMethodsTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final String[] EXPECTED = new String[] {"Api::apiLevel22", "B::bar"};
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public ApiModelClassMergingWithDifferentApiMethodsTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    Method apiMethod = Api.class.getDeclaredMethod("apiLevel22");
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, B.class, Main.class)
+        .addLibraryClasses(Api.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        // TODO(b/138781768): Should not be merged
+        .addHorizontallyMergedClassesInspector(
+            inspector -> inspector.assertClassesMerged(A.class, B.class))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
+        .compile()
+        .addRunClasspathClasses(Api.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  public static class Api {
+
+    public static void apiLevel22() {
+      System.out.println("Api::apiLevel22");
+    }
+  }
+
+  public static class A {
+
+    @NeverInline
+    public static void callFoo() {
+      Api.apiLevel22();
+    }
+  }
+
+  public static class B {
+
+    @NeverInline
+    public static void bar() {
+      System.out.println("B::bar");
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      A.callFoo();
+      B.bar();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelInlineMethodWithApiTypeTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelInlineMethodWithApiTypeTest.java
new file mode 100644
index 0000000..2e24790
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelInlineMethodWithApiTypeTest.java
@@ -0,0 +1,98 @@
+// Copyright (c) 2021, 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.apimodeling;
+
+import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForType;
+import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.verifyThat;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.reflect.Method;
+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 ApiModelInlineMethodWithApiTypeTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public ApiModelInlineMethodWithApiTypeTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    Method apiCallerApiLevel1 = ApiCaller.class.getDeclaredMethod("apiLevel22");
+    Method apiCallerCallerApiLevel1 = ApiCallerCaller.class.getDeclaredMethod("apiLevel22");
+    Method otherCallerApiLevel1 = OtherCaller.class.getDeclaredMethod("apiLevel1");
+    testForR8(parameters.getBackend())
+        .addProgramClasses(ApiCaller.class, ApiCallerCaller.class, OtherCaller.class, Main.class)
+        .addLibraryClasses(ApiType.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .enableNoHorizontalClassMergingAnnotations()
+        .apply(setMockApiLevelForType(ApiType.class, AndroidApiLevel.L_MR1))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .compile()
+        .addRunClasspathClasses(ApiType.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(ApiType.class.getName())
+        .inspect(verifyThat(parameters, apiCallerApiLevel1).inlinedInto(apiCallerCallerApiLevel1))
+        // TODO(b/138781768): Should not be inlined
+        .inspect(
+            verifyThat(parameters, apiCallerCallerApiLevel1).inlinedInto(otherCallerApiLevel1));
+  }
+
+  public static class ApiType {}
+
+  @NoHorizontalClassMerging
+  public static class ApiCaller {
+    public static ApiType apiLevel22() throws Exception {
+      // The reflective call here is to ensure that the setting of A's api level is not based on
+      // a method reference to `Api` and only because of the type reference in the field `api`.
+      Class<?> reflectiveCall =
+          Class.forName(
+              "com.android.tools.r8.apimodeling.ApiModelInlineMethodWithApiTypeTest_ApiType"
+                  .replace("_", "$"));
+      return (ApiType) reflectiveCall.getDeclaredConstructor().newInstance();
+    }
+  }
+
+  @NoHorizontalClassMerging
+  public static class ApiCallerCaller {
+
+    public static void apiLevel22() throws Exception {
+      // This is referencing the proto of ApiCaller.foo and thus have a reference to ApiType. It is
+      // therefore OK to inline ApiCaller.foo() into ApiCallerCaller.bar().
+      System.out.println(ApiCaller.apiLevel22().getClass().getName());
+    }
+  }
+
+  public static class OtherCaller {
+
+    public static void apiLevel1() throws Exception {
+      // ApiCallerCaller.apiLevel22 should never be inlined here.
+      ApiCallerCaller.apiLevel22();
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) throws Exception {
+      OtherCaller.apiLevel1();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoClassInliningFieldTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoClassInliningFieldTest.java
new file mode 100644
index 0000000..3bd5b8a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoClassInliningFieldTest.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2021, 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.apimodeling;
+
+import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForField;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.reflect.Field;
+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 ApiModelNoClassInliningFieldTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public ApiModelNoClassInliningFieldTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    Field apiField = Api.class.getDeclaredField("foo");
+    testForR8(parameters.getBackend())
+        .addProgramClasses(ApiCaller.class, ApiCallerCaller.class, ApiBuilder.class, Main.class)
+        .addLibraryClasses(Api.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .enableNoHorizontalClassMergingAnnotations()
+        .apply(setMockApiLevelForField(apiField, AndroidApiLevel.L_MR1))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .compile()
+        .inspect(
+            inspector -> {
+              // TODO(b/138781768): Should not be class inlined.
+              assertThat(inspector.clazz(ApiCaller.class), not(isPresent()));
+            })
+        .addRunClasspathClasses(Api.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Api::apiLevel22");
+  }
+
+  public static class Api {
+
+    public String foo = "Api::apiLevel22";
+  }
+
+  public static class ApiBuilder {
+
+    public static Api build() {
+      return new Api();
+    }
+  }
+
+  @NoHorizontalClassMerging
+  public static class ApiCaller {
+
+    private Api api;
+
+    public ApiCaller(Api api) {
+      this.api = api;
+      System.out.println(api.foo);
+    }
+  }
+
+  @NoHorizontalClassMerging
+  public static class ApiCallerCaller {
+
+    @NeverInline
+    public static void callCallApi() {
+      new ApiCaller(ApiBuilder.build());
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      ApiCallerCaller.callCallApi();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoClassInliningMethodTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoClassInliningMethodTest.java
new file mode 100644
index 0000000..a7804e0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoClassInliningMethodTest.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2021, 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.apimodeling;
+
+import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.verifyThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.reflect.Method;
+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 ApiModelNoClassInliningMethodTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public ApiModelNoClassInliningMethodTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    Method apiMethod = Api.class.getDeclaredMethod("apiLevel22");
+    Method apiCaller = ApiCaller.class.getDeclaredMethod("callApi");
+    Method apiCallerCaller = ApiCallerCaller.class.getDeclaredMethod("callCallApi");
+    testForR8(parameters.getBackend())
+        .addProgramClasses(ApiCaller.class, ApiCallerCaller.class, Main.class)
+        .addLibraryClasses(Api.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .enableNoHorizontalClassMergingAnnotations()
+        .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .compile()
+        .inspect(
+            verifyThat(parameters, apiCaller)
+                .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
+        .addRunClasspathClasses(Api.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Api::apiLevel22");
+  }
+
+  public static class Api {
+
+    public static void apiLevel22() {
+      System.out.println("Api::apiLevel22");
+    }
+  }
+
+  @NoHorizontalClassMerging
+  public static class ApiCaller {
+
+    public void callApi() {
+      Api.apiLevel22();
+    }
+  }
+
+  @NoHorizontalClassMerging
+  public static class ApiCallerCaller {
+
+    @NeverInline
+    public static void callCallApi() {
+      new ApiCaller().callApi();
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      ApiCallerCaller.callCallApi();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
new file mode 100644
index 0000000..f210e6f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2021, 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.apimodeling;
+
+import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForField;
+import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.verifyThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+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 ApiModelNoInliningOfHigherApiLevelInstanceFieldTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public ApiModelNoInliningOfHigherApiLevelInstanceFieldTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    Field apiField = Api.class.getDeclaredField("apiLevel22");
+    Method apiCaller = ApiCaller.class.getDeclaredMethod("getInstanceField");
+    Method apiCallerCaller = ApiCallerCaller.class.getDeclaredMethod("noApiCall");
+    testForR8(parameters.getBackend())
+        .addProgramClasses(ApiCaller.class, ApiCallerCaller.class, Main.class)
+        .addLibraryClasses(Api.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .enableNoHorizontalClassMergingAnnotations()
+        .apply(setMockApiLevelForField(apiField, AndroidApiLevel.L_MR1))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .compile()
+        // TODO(b/138781768): Should not be inlined
+        .inspect(verifyThat(parameters, apiCaller).inlinedInto(apiCallerCaller))
+        .addRunClasspathClasses(Api.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello World!");
+  }
+
+  // The api class does not have an api level to ensure it is not the instance initializer that is
+  // keeping us from inlining.
+  public static class Api {
+
+    public String apiLevel22 = "Hello World!";
+  }
+
+  @NoHorizontalClassMerging
+  public static class ApiCaller {
+
+    public static void getInstanceField() {
+      System.out.println(new Api().apiLevel22);
+    }
+  }
+
+  @NoHorizontalClassMerging
+  public static class ApiCallerCaller {
+
+    @NeverInline
+    public static void noApiCall() {
+      ApiCaller.getInstanceField();
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      ApiCallerCaller.noApiCall();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelStaticFieldTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelStaticFieldTest.java
new file mode 100644
index 0000000..0087275
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelStaticFieldTest.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2021, 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.apimodeling;
+
+import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForField;
+import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.verifyThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+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 ApiModelNoInliningOfHigherApiLevelStaticFieldTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public ApiModelNoInliningOfHigherApiLevelStaticFieldTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    Field apiField = Api.class.getDeclaredField("apiLevel22");
+    Method apiCaller = ApiCaller.class.getDeclaredMethod("getStaticField");
+    Method apiCallerCaller = ApiCallerCaller.class.getDeclaredMethod("noApiCall");
+    testForR8(parameters.getBackend())
+        .addProgramClasses(ApiCaller.class, ApiCallerCaller.class, Main.class)
+        .addLibraryClasses(Api.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .enableNoHorizontalClassMergingAnnotations()
+        .apply(setMockApiLevelForField(apiField, AndroidApiLevel.L_MR1))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .compile()
+        // TODO(b/138781768): Should not be inlined
+        .inspect(verifyThat(parameters, apiCaller).inlinedInto(apiCallerCaller))
+        .addRunClasspathClasses(Api.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello World!");
+  }
+
+  // The api class does not have an api level to ensure it is not the class reference is keeping us
+  // from inlining.
+  public static class Api {
+
+    public static String apiLevel22 = "Hello World!";
+  }
+
+  @NoHorizontalClassMerging
+  public static class ApiCaller {
+
+    public static void getStaticField() {
+      System.out.println(Api.apiLevel22);
+    }
+  }
+
+  @NoHorizontalClassMerging
+  public static class ApiCallerCaller {
+
+    @NeverInline
+    public static void noApiCall() {
+      ApiCaller.getStaticField();
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      ApiCallerCaller.noApiCall();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelVerticalMergingOfSuperClassTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelVerticalMergingOfSuperClassTest.java
new file mode 100644
index 0000000..a04774b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelVerticalMergingOfSuperClassTest.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2021, 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.apimodeling;
+
+import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForType;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+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 ApiModelVerticalMergingOfSuperClassTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public ApiModelVerticalMergingOfSuperClassTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, B.class, Main.class)
+        .addLibraryClasses(Api.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .apply(setMockApiLevelForType(Api.class, AndroidApiLevel.L_MR1))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .compile()
+        .inspect(
+            inspector -> {
+              // TODO(b/138781768): We should not merge A into B.
+              assertThat(inspector.clazz(A.class), not(isPresent()));
+            })
+        .addRunClasspathClasses(Api.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello World!");
+  }
+
+  public static class Api {}
+
+  public static class A extends Api {
+
+    @NeverInline
+    public void bar() {
+      System.out.println("Hello World!");
+    }
+  }
+
+  @NeverClassInline
+  public static class B extends A {
+
+    public void foo() {
+      bar();
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new B().foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelingTestHelper.java
index 5b1a2f7..b20e395 100644
--- a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelingTestHelper.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.CodeMatchers;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
 public abstract class ApiModelingTestHelper {
@@ -34,6 +35,32 @@
     };
   }
 
+  static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
+      ThrowableConsumer<T> setMockApiLevelForField(Field field, AndroidApiLevel apiLevel) {
+    return compilerBuilder -> {
+      compilerBuilder.addOptionsModification(
+          options -> {
+            options
+                .apiModelingOptions()
+                .fieldApiMapping
+                .put(Reference.fieldFromField(field), apiLevel);
+          });
+    };
+  }
+
+  static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>> ThrowableConsumer<T> setMockApiLevelForType(
+      Class<?> clazz, AndroidApiLevel apiLevel) {
+    return compilerBuilder -> {
+      compilerBuilder.addOptionsModification(
+          options -> {
+            options
+                .apiModelingOptions()
+                .typeApiMapping
+                .put(Reference.classFromClass(clazz), apiLevel);
+          });
+    };
+  }
+
   static void enableApiCallerIdentification(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
     compilerBuilder.addOptionsModification(
         options -> {