Ensure that merged parameter is api safe

Bug: b/289361079
Change-Id: Ifa30fed27c283906b9ce89835ba8c268c9430190
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java b/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
index 62531ba..625b127 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.utils.AndroidApiLevelUtils;
 import com.google.common.collect.Iterables;
 
 public class DexTypeUtils {
@@ -15,7 +16,7 @@
       AppView<? extends AppInfoWithClassHierarchy> appView, Iterable<DexType> types) {
     TypeElement join =
         TypeElement.join(Iterables.transform(types, type -> type.toTypeElement(appView)), appView);
-    return toDexType(appView, join);
+    return findApiSafeUpperBound(appView, toDexType(appView, join));
   }
 
   public static DexType toDexType(
@@ -39,4 +40,23 @@
     }
     return dexItemFactory.objectType;
   }
+
+  public static DexType findApiSafeUpperBound(
+      AppView<? extends AppInfoWithClassHierarchy> appView, DexType type) {
+    DexItemFactory factory = appView.dexItemFactory();
+    if (type.toBaseType(factory).isPrimitiveType()) {
+      return type;
+    }
+    DexClass clazz = appView.definitionFor(type.isArrayType() ? type.toBaseType(factory) : type);
+    if (clazz == null) {
+      assert false : "We should not have found an upper bound if the hierarchy is missing";
+      return type;
+    }
+    if (!clazz.isLibraryClass()
+        || AndroidApiLevelUtils.isApiSafeForReference(clazz.asLibraryClass(), appView)) {
+      return type;
+    }
+    // Always just return the object type since this is safe for all api versions.
+    return factory.objectType;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClInitMergeSuperTypeApiLevelTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClInitMergeSuperTypeApiLevelTest.java
index 54bf153..27890cf 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClInitMergeSuperTypeApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClInitMergeSuperTypeApiLevelTest.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import java.lang.reflect.Constructor;
@@ -38,11 +39,18 @@
     return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
+  private TypeReference getMergeReferenceForApiLevel() {
+    boolean canUseExecutable =
+        parameters.isDexRuntime()
+            && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O);
+    return Reference.typeFromTypeName(typeName(canUseExecutable ? Executable.class : Object.class));
+  }
+
   @Test
   public void testR8() throws Exception {
     testForR8(parameters.getBackend())
         // Emulate a standard AGP setup where we compile with a new android jar on boot classpath.
-        .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
         .addInnerClasses(getClass())
         .setMinApi(parameters)
         .addKeepClassAndMembersRules(Main.class)
@@ -56,17 +64,11 @@
               assertThat(clazz, isPresent());
               MethodSubject init = clazz.uniqueInstanceInitializer();
               assertThat(init, isPresent());
-              TypeReference executableTypeRef =
-                  Reference.typeFromTypeName(typeName(Executable.class));
-              // TODO(b/289361079): This should not be of type Executable since this was introduced
-              //  at api 26.
-              assertEquals(executableTypeRef, init.getParameter(0).getTypeReference());
-              // TODO(b/289361079): This should not be of type Executable since this was introduced
-              //  at api 26.
+              TypeReference mergeTypeRef = getMergeReferenceForApiLevel();
+              assertEquals(mergeTypeRef, init.getParameter(0).getTypeReference());
               assertTrue(
                   clazz.allFields().stream()
-                      .anyMatch(
-                          f -> executableTypeRef.equals(f.getFinalReference().getFieldType())));
+                      .anyMatch(f -> mergeTypeRef.equals(f.getFinalReference().getFieldType())));
             })
         .run(parameters.getRuntime(), Main.class)
         // The test succeeds for some unknown reason.