Account for synthetics with restricted package private merging in D8

Bug: b/269651270
Change-Id: I61aa088e68e6e4ec9870f924c1f0a686a372f1a6
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 2d11359..1e3e943 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -57,7 +57,7 @@
 import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields;
 import com.android.tools.r8.horizontalclassmerging.policies.SameMainDexGroup;
 import com.android.tools.r8.horizontalclassmerging.policies.SameNestHost;
-import com.android.tools.r8.horizontalclassmerging.policies.SamePackageForApiOutline;
+import com.android.tools.r8.horizontalclassmerging.policies.SamePackageForNonGlobalMergeSynthetic;
 import com.android.tools.r8.horizontalclassmerging.policies.SameParentClass;
 import com.android.tools.r8.horizontalclassmerging.policies.SyntheticItemsPolicy;
 import com.android.tools.r8.horizontalclassmerging.policies.VerifyMultiClassPolicyAlwaysSatisfied;
@@ -265,7 +265,7 @@
         new SameParentClass(),
         new SyntheticItemsPolicy(appView, mode),
         new NoApiOutlineWithNonApiOutline(appView),
-        new SamePackageForApiOutline(appView, mode),
+        new SamePackageForNonGlobalMergeSynthetic(appView),
         new NoDifferentApiReferenceLevel(appView),
         new LimitClassGroups(appView));
     assert verifyMultiClassPoliciesIrrelevantForMergingSyntheticsInD8(appView, mode, builder);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SamePackageForApiOutline.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SamePackageForNonGlobalMergeSynthetic.java
similarity index 83%
rename from src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SamePackageForApiOutline.java
rename to src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SamePackageForNonGlobalMergeSynthetic.java
index 30a3200..db6d282 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SamePackageForApiOutline.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SamePackageForNonGlobalMergeSynthetic.java
@@ -9,23 +9,21 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.MergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
 import com.android.tools.r8.synthesis.SyntheticItems;
+import com.google.common.collect.Iterables;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
-public class SamePackageForApiOutline extends MultiClassPolicy {
+public class SamePackageForNonGlobalMergeSynthetic extends MultiClassPolicy {
 
   private final AppView<AppInfo> appView;
-  private final Mode mode;
 
-  public SamePackageForApiOutline(AppView<AppInfo> appView, Mode mode) {
+  public SamePackageForNonGlobalMergeSynthetic(AppView<AppInfo> appView) {
     this.appView = appView;
-    this.mode = mode;
   }
 
   /** Sort unrestricted classes into restricted classes if they are in the same package. */
@@ -50,7 +48,12 @@
 
     // Sort all restricted classes into packages.
     for (DexProgramClass clazz : group) {
-      if (syntheticItems.isSyntheticOfKind(clazz.getType(), k -> k.API_MODEL_OUTLINE)) {
+      assert syntheticItems.isSynthetic(clazz.getType());
+      if (Iterables.any(
+          syntheticItems.getSyntheticKinds(clazz.getType()),
+          kind ->
+              !kind.isSyntheticMethodKind()
+                  || !kind.asSyntheticMethodKind().isAllowGlobalMerging())) {
         restrictedClasses
             .computeIfAbsent(
                 clazz.getType().getPackageDescriptor(), ignoreArgument(MergeGroup::new))
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
index 292d844..5f65c95 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
@@ -94,25 +94,8 @@
     if (context.getDefinition().isD8R8Synthesized()) {
       return appView.computedMinApiLevel();
     }
-    DexReference reference;
-    if (instruction.isInvoke()) {
-      CfInvoke cfInvoke = instruction.asInvoke();
-      if (cfInvoke.isInvokeSpecial()) {
-        return appView.computedMinApiLevel();
-      }
-      reference = cfInvoke.getMethod();
-    } else if (instruction.isFieldInstruction()) {
-      reference = instruction.asFieldInstruction().getField();
-    } else if (instruction.isCheckCast()) {
-      reference = instruction.asCheckCast().getType();
-    } else if (instruction.isInstanceOf()) {
-      reference = instruction.asInstanceOf().getType();
-    } else if (instruction.isConstClass()) {
-      reference = instruction.asConstClass().getType();
-    } else {
-      return appView.computedMinApiLevel();
-    }
-    if (!reference.getContextType().isClassType()) {
+    DexReference reference = getReferenceFromInstruction(instruction);
+    if (reference == null || !reference.getContextType().isClassType()) {
       return appView.computedMinApiLevel();
     }
     DexClass holder = appView.definitionFor(reference.getContextType());
@@ -158,6 +141,22 @@
     }
   }
 
+  private DexReference getReferenceFromInstruction(CfInstruction instruction) {
+    if (instruction.isFieldInstruction()) {
+      return instruction.asFieldInstruction().getField();
+    } else if (instruction.isCheckCast()) {
+      return instruction.asCheckCast().getType();
+    } else if (instruction.isInstanceOf()) {
+      return instruction.asInstanceOf().getType();
+    } else if (instruction.isConstClass()) {
+      return instruction.asConstClass().getType();
+    } else if (instruction.isInvoke() && !instruction.asInvoke().isInvokeSpecial()) {
+      return instruction.asInvoke().getMethod();
+    } else {
+      return null;
+    }
+  }
+
   private DexEncodedMember<?, ?> simpleLookupInClassHierarchy(
       DexLibraryClass holder, Function<DexClass, DexEncodedMember<?, ?>> lookup) {
     DexEncodedMember<?, ?> result = lookup.apply(holder);
@@ -208,10 +207,20 @@
       ComputedApiLevel apiLevel,
       DexItemFactory factory,
       ProgramMethod programContext) {
+    DexReference reference = getReferenceFromInstruction(instruction);
+    assert reference != null;
+    DexClass holder = appView.definitionFor(reference.getContextType());
+    assert holder != null;
     return appView
         .getSyntheticItems()
         .createMethod(
-            kinds -> kinds.API_MODEL_OUTLINE,
+            kinds ->
+                // We've already checked that the definition the reference is targeting is public
+                // when computing the api-level for desugaring. We still have to ensure that the
+                // class cannot be merged globally if it is package private.
+                holder.isPublic()
+                    ? kinds.API_MODEL_OUTLINE
+                    : kinds.API_MODEL_OUTLINE_WITHOUT_GLOBAL_MERGING,
             context,
             appView,
             syntheticMethodBuilder -> {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index d8ae369..631231c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -466,6 +466,16 @@
     return pending.containsTypeOfKind(type, kind) || committed.containsTypeOfKind(type, kind);
   }
 
+  public Iterable<SyntheticKind> getSyntheticKinds(DexType type) {
+    Iterable<SyntheticKind> references =
+        IterableUtils.transform(committed.getItems(type), SyntheticReference::getKind);
+    SyntheticDefinition<?, ?, ?> definition = pending.definitions.get(type);
+    if (definition != null) {
+      references = Iterables.concat(references, IterableUtils.singleton(definition.getKind()));
+    }
+    return references;
+  }
+
   boolean isSyntheticInput(DexProgramClass clazz) {
     return committed.containsSyntheticInput(clazz.getType());
   }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 59aff72..5c81e4d 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -68,21 +68,25 @@
 
   // Method synthetics.
   public final SyntheticKind ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD =
-      generator.forSingleMethod("CheckNotZero");
-  public final SyntheticKind RECORD_HELPER = generator.forSingleMethod("Record");
-  public final SyntheticKind BACKPORT = generator.forSingleMethod("Backport");
+      generator.forSingleMethodWithGlobalMerging("CheckNotZero");
+  public final SyntheticKind RECORD_HELPER = generator.forSingleMethodWithGlobalMerging("Record");
+  public final SyntheticKind BACKPORT = generator.forSingleMethodWithGlobalMerging("Backport");
   public final SyntheticKind BACKPORT_WITH_FORWARDING =
       generator.forSingleMethod("BackportWithForwarding");
   public final SyntheticKind STATIC_INTERFACE_CALL =
       generator.forSingleMethod("StaticInterfaceCall");
-  public final SyntheticKind TO_STRING_IF_NOT_NULL = generator.forSingleMethod("ToStringIfNotNull");
-  public final SyntheticKind THROW_CCE_IF_NOT_NULL = generator.forSingleMethod("ThrowCCEIfNotNull");
-  public final SyntheticKind THROW_IAE = generator.forSingleMethod("ThrowIAE");
-  public final SyntheticKind THROW_ICCE = generator.forSingleMethod("ThrowICCE");
-  public final SyntheticKind THROW_NSME = generator.forSingleMethod("ThrowNSME");
-  public final SyntheticKind THROW_RTE = generator.forSingleMethod("ThrowRTE");
-  public final SyntheticKind TWR_CLOSE_RESOURCE = generator.forSingleMethod("TwrCloseResource");
-  public final SyntheticKind SERVICE_LOADER = generator.forSingleMethod("ServiceLoad");
+  public final SyntheticKind TO_STRING_IF_NOT_NULL =
+      generator.forSingleMethodWithGlobalMerging("ToStringIfNotNull");
+  public final SyntheticKind THROW_CCE_IF_NOT_NULL =
+      generator.forSingleMethodWithGlobalMerging("ThrowCCEIfNotNull");
+  public final SyntheticKind THROW_IAE = generator.forSingleMethodWithGlobalMerging("ThrowIAE");
+  public final SyntheticKind THROW_ICCE = generator.forSingleMethodWithGlobalMerging("ThrowICCE");
+  public final SyntheticKind THROW_NSME = generator.forSingleMethodWithGlobalMerging("ThrowNSME");
+  public final SyntheticKind THROW_RTE = generator.forSingleMethodWithGlobalMerging("ThrowRTE");
+  public final SyntheticKind TWR_CLOSE_RESOURCE =
+      generator.forSingleMethodWithGlobalMerging("TwrCloseResource");
+  public final SyntheticKind SERVICE_LOADER =
+      generator.forSingleMethodWithGlobalMerging("ServiceLoad");
   public final SyntheticKind OUTLINE = generator.forSingleMethod("Outline");
   public final SyntheticKind COVARIANT_OUTLINE = generator.forSingleMethod("CovariantOutline");
   public final SyntheticKind API_CONVERSION = generator.forSingleMethod("APIConversion");
@@ -90,7 +94,10 @@
       generator.forSingleMethod("APIConversionParameters");
   public final SyntheticKind COLLECTION_CONVERSION =
       generator.forSingleMethod("$CollectionConversion");
-  public final SyntheticKind API_MODEL_OUTLINE = generator.forSingleMethod("ApiModelOutline");
+  public final SyntheticKind API_MODEL_OUTLINE =
+      generator.forSingleMethodWithGlobalMerging("ApiModelOutline");
+  public final SyntheticKind API_MODEL_OUTLINE_WITHOUT_GLOBAL_MERGING =
+      generator.forSingleMethod("ApiModelOutline");
 
   private final List<SyntheticKind> ALL_KINDS;
   private String lazyVersionHash = null;
@@ -144,7 +151,11 @@
     }
 
     SyntheticKind forSingleMethod(String descriptor) {
-      return register(new SyntheticMethodKind(getNextId(), descriptor));
+      return register(new SyntheticMethodKind(getNextId(), descriptor, false));
+    }
+
+    SyntheticKind forSingleMethodWithGlobalMerging(String descriptor) {
+      return register(new SyntheticMethodKind(getNextId(), descriptor, true));
     }
 
     // TODO(b/214901256): Remove once fixed.
@@ -217,6 +228,14 @@
       return descriptor;
     }
 
+    public boolean isSyntheticMethodKind() {
+      return false;
+    }
+
+    public SyntheticMethodKind asSyntheticMethodKind() {
+      return null;
+    }
+
     public abstract boolean isShareable();
 
     public abstract boolean isSingleSyntheticMethod();
@@ -236,10 +255,13 @@
     public abstract void internalHash(Hasher hasher);
   }
 
-  private static class SyntheticMethodKind extends SyntheticKind {
+  public static class SyntheticMethodKind extends SyntheticKind {
 
-    public SyntheticMethodKind(int id, String descriptor) {
+    private final boolean allowGlobalMerging;
+
+    public SyntheticMethodKind(int id, String descriptor, boolean allowGlobalMerging) {
       super(id, descriptor);
+      this.allowGlobalMerging = allowGlobalMerging;
     }
 
     @Override
@@ -268,6 +290,20 @@
       return false;
     }
 
+    public boolean isAllowGlobalMerging() {
+      return allowGlobalMerging;
+    }
+
+    @Override
+    public boolean isSyntheticMethodKind() {
+      return true;
+    }
+
+    @Override
+    public SyntheticMethodKind asSyntheticMethodKind() {
+      return this;
+    }
+
     @Override
     public void internalHash(Hasher hasher) {
       hasher.putString("method", StandardCharsets.UTF_8);