[Compose] Wait with removal of field signatures until finish

Bug: b/241763080
Change-Id: I3911606aa43552cff1ee2515c3bc41c750e15d69
diff --git a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
index 37ae9d2..fc4fd33 100644
--- a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
+++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.naming.MappedRangeUtils.isInlineMappedRange;
 import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
 import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName;
 import com.android.tools.r8.naming.MemberNaming.FieldSignature;
@@ -128,9 +129,8 @@
     composingClassBuilder.compose(classNameMapper, classMapping);
   }
 
-
-  @Override
-  public String toString() {
+  public String finish() {
+    committed.finish();
     List<ComposingClassBuilder> classBuilders = new ArrayList<>(committed.classBuilders.values());
     classBuilders.sort(Comparator.comparing(ComposingClassBuilder::getOriginalName));
     StringBuilder sb = new StringBuilder();
@@ -167,6 +167,12 @@
     private final Map<ClassTypeNameAndMethodName, UpdateOutlineCallsiteInformation>
         outlineSourcePositionsUpdated = new HashMap<>();
 
+    /**
+     * Map of signatures that should be removed when finalizing the composed map. The key is the
+     * original name of a class.
+     */
+    private final Map<String, Set<Signature>> signaturesToRemove = new HashMap<>();
+
     private final List<String> preamble = new ArrayList<>();
 
     public void commit(ComposingData current, ClassNameMapper classNameMapper)
@@ -175,6 +181,7 @@
       commitClassBuilders(current, classNameMapper);
       commitRewriteFrameInformation(current, classNameMapper);
       commitOutlineCallsiteInformation(current, classNameMapper);
+      commitSignaturesToRemove(current);
     }
 
     private void commitClassBuilders(ComposingData current, ClassNameMapper classNameMapper)
@@ -211,6 +218,28 @@
       classBuilders = newClassBuilders;
     }
 
+    private void commitSignaturesToRemove(ComposingData current) {
+      current.signaturesToRemove.forEach(
+          (originalName, signatures) -> {
+            signaturesToRemove.merge(
+                originalName,
+                signatures,
+                (signatures1, signatures2) -> {
+                  Set<Signature> joinedSignatures = Sets.newHashSet(signatures1);
+                  joinedSignatures.addAll(signatures2);
+                  return joinedSignatures;
+                });
+          });
+    }
+
+    public void addSignatureToRemove(
+        ComposingClassBuilder composingClassBuilder, Signature signature) {
+      signaturesToRemove
+          .computeIfAbsent(
+              composingClassBuilder.getOriginalName(), ignoreArgument(Sets::newHashSet))
+          .add(signature);
+    }
+
     private void commitRewriteFrameInformation(
         ComposingData current, ClassNameMapper classNameMapper) {
       // First update the existing frame information to have new class name mappings.
@@ -313,6 +342,25 @@
         return newTypeName == null ? typeReference : Reference.classFromTypeName(newTypeName);
       }
     }
+
+    public void finish() {
+      classBuilders.forEach(
+          (ignored, classBuilder) -> {
+            Set<Signature> signatures = signaturesToRemove.get(classBuilder.getOriginalName());
+            if (signatures == null) {
+              return;
+            }
+            signatures.forEach(
+                signature -> {
+                  if (signature.isFieldSignature()) {
+                    classBuilder.fieldMembers.remove(signature.asFieldSignature());
+                  } else {
+                    // TODO(b/241763080): Define removal of methods with and without signatures.
+                    throw new Unreachable();
+                  }
+                });
+          });
+    }
   }
 
   private static class ClassTypeNameAndMethodName {
@@ -498,10 +546,13 @@
       if (composingClassBuilder == null) {
         return null;
       }
-      return composingClassBuilder.fieldMembers.remove(
-          originalSignature.isQualified()
-              ? originalSignature.toUnqualifiedSignature()
-              : originalSignature);
+      FieldSignature signature =
+          (originalSignature.isQualified()
+                  ? originalSignature.toUnqualifiedSignature()
+                  : originalSignature)
+              .asFieldSignature();
+      current.addSignatureToRemove(composingClassBuilder, signature);
+      return composingClassBuilder.fieldMembers.get(signature);
     }
 
     private void composeMethodNamings(
diff --git a/src/main/java/com/android/tools/r8/naming/MappingComposer.java b/src/main/java/com/android/tools/r8/naming/MappingComposer.java
index 636e699..2068902 100644
--- a/src/main/java/com/android/tools/r8/naming/MappingComposer.java
+++ b/src/main/java/com/android/tools/r8/naming/MappingComposer.java
@@ -23,6 +23,6 @@
     for (ClassNameMapper classNameMapper : classNameMappers) {
       builder.compose(classNameMapper);
     }
-    return builder.toString();
+    return builder.finish();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateMovedFieldTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateMovedFieldTest.java
index d6fad75..355dbf4 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateMovedFieldTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateMovedFieldTest.java
@@ -47,14 +47,13 @@
           "    boolean b.g2 -> h2");
   private static final String mappingResult =
       StringUtils.unixLines(
-          // TODO(b/241763080): We should support duplicating fields.
           "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
           "com.bar -> c:",
           "    boolean f2 -> h2",
           "    int some.other.Class.f1 -> h1",
           "com.baz -> d:",
-          "    int a.g1 -> h1", // This should be int some.other.Class.f1 -> h1
-          "    boolean b.g2 -> h2", // This should be boolean com.bar.f2 -> h2.
+          "    boolean com.bar.f2 -> h2",
+          "    int some.other.Class.f1 -> h1",
           "com.foo -> a:");
 
   @Test