Disallow class merging between classes in features and base

Bug: 122902374,141452765

Change-Id: I2c72079e5107967b71e216ab71556a13a1f11a78
diff --git a/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java b/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
index b7f6158..22e819e 100644
--- a/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
+++ b/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
@@ -59,7 +59,12 @@
     return result;
   }
 
-  public boolean inSameFeatureOrBase(DexMethod a, DexMethod b) {
+  public boolean isInFeature(DexProgramClass clazz) {
+    return javaTypeToFeatureSplitMapping.containsKey(
+        DescriptorUtils.descriptorToJavaType(clazz.type.toDescriptorString()));
+  }
+
+  public boolean inSameFeatureOrBase(DexMethod a, DexMethod b){
     return inSameFeatureOrBase(a.holder, b.holder);
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index 7f16e07..487afa2 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -256,6 +256,11 @@
     if (appView.appInfo().neverMerge.contains(clazz.type)) {
       return MergeGroup.DONT_MERGE;
     }
+    if (appView.options().featureSplitConfiguration != null &&
+        appView.options().featureSplitConfiguration.isInFeature(clazz)) {
+      // TODO(b/141452765): Allow class merging between classes in features.
+      return MergeGroup.DONT_MERGE;
+    }
     if (clazz.staticFields().size() + clazz.directMethods().size() + clazz.virtualMethods().size()
         == 0) {
       return MergeGroup.DONT_MERGE;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index cd77544..b8b8482 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -374,6 +374,12 @@
         || appInfo.neverMerge.contains(clazz.type)) {
       return false;
     }
+    if (appView.options().featureSplitConfiguration != null &&
+        appView.options().featureSplitConfiguration.isInFeature(clazz)) {
+      // TODO(b/141452765): Allow class merging between classes in features.
+      return false;
+    }
+
     // Note that the property "singleSubtype == null" cannot change during merging, since we visit
     // classes in a top-down order.
     DexType singleSubtype = appInfo.getSingleSubtype(clazz.type);
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
index 9ea52d0..25cbe9a 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -4,9 +4,13 @@
 
 package com.android.tools.r8.dexsplitter;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.FeatureSplit;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NeverMerge;
 import com.android.tools.r8.R8FullTestBuilder;
@@ -77,6 +81,26 @@
     assertTrue(processResult.stderr.contains("NoClassDefFoundError"));
   }
 
+  @Test
+  public void testOnR8Splitter() throws IOException, CompilationFailedException,
+      ExecutionException {
+    assumeTrue(parameters.isDexRuntime());
+    Consumer<R8FullTestBuilder> configurator =
+        r8FullTestBuilder -> r8FullTestBuilder.enableMergeAnnotations().noMinification();
+    ProcessResult processResult =
+        testR8Splitter(
+            parameters,
+            ImmutableSet.of(BaseClass.class, BaseWithStatic.class),
+            ImmutableSet.of(FeatureClass.class, AFeatureWithStatic.class),
+            FeatureClass.class,
+            EXPECTED,
+            a -> true,
+            configurator);
+
+    assertEquals(processResult.exitCode, 0);
+    assertTrue(processResult.stdout.equals(StringUtils.lines("42", "foobar")));
+  }
+
   @NeverMerge
   public static class BaseClass implements RunInterface {
 
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
index 9d8c12b..b8154cc 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
@@ -1,6 +1,5 @@
 package com.android.tools.r8.dexsplitter;
 
-import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
@@ -66,7 +65,6 @@
                   ByteDataView data,
                   Set<String> descriptors,
                   DiagnosticsHandler handler) {
-                assertEquals(classNames.size(), descriptors.size());
                 for (String descriptor : descriptors) {
                   assertTrue(classNames.contains(DescriptorUtils.descriptorToJavaType(descriptor)));
                 }