Merge "Apply gradle build-scan plugin."
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
index cebcc8c..08d0cae 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
@@ -354,7 +354,8 @@
         // avoid a bug where the index variable could end up being uninitialized.
         if (code.options.canHaveBoundsCheckEliminationBug()
             && move.from.getValue().isConstNumber()
-            && move.type == MoveType.SINGLE) {
+            && move.type == MoveType.SINGLE
+            && allocator.unadjustedRealRegisterFromAllocated(move.to.getRegister()) < 256) {
           scheduler.addMove(
               new RegisterMove(move.to.getRegister(), move.type, move.from.getValue().definition));
         } else {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index c4276dc..1de3f17 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -873,6 +873,7 @@
         ruleBuilder.setName(IdentifierPatternWithWildcards.withoutWildcards("<init>"));
         ruleBuilder.setArguments(parseArgumentList());
       } else {
+        TextPosition firstStart = getPosition();
         IdentifierPatternWithWildcards first =
             acceptIdentifierWithBackreference(IdentifierType.ANY);
         if (first != null) {
@@ -885,6 +886,7 @@
               ruleBuilder.setName(first);
               ruleBuilder.setArguments(parseArgumentList());
             } else {
+              TextPosition secondStart = getPosition();
               IdentifierPatternWithWildcards second =
                   acceptIdentifierWithBackreference(IdentifierType.ANY);
               if (second != null) {
@@ -897,6 +899,12 @@
                           ProguardTypeMatcher.create(first, ClassOrType.TYPE, dexItemFactory));
                   ruleBuilder.setArguments(parseArgumentList());
                 } else {
+                  if (first.hasUnusualCharacters()) {
+                    warnUnusualCharacters("type", first.pattern, "field", firstStart);
+                  }
+                  if (second.hasUnusualCharacters()) {
+                    warnUnusualCharacters("field name", second.pattern, "field", secondStart);
+                  }
                   ruleBuilder.setRuleType(ProguardMemberType.FIELD);
                   ruleBuilder.setName(second);
                   ruleBuilder
@@ -1490,6 +1498,16 @@
           "Option -" + optionName + " overrides -" + victim, origin, getPosition(start)));
     }
 
+    private void warnUnusualCharacters(
+        String kind, String pattern, String ruleType, TextPosition start) {
+      reporter.warning(new StringDiagnostic(
+          "The " + kind + " \"" + pattern + "\" is used in a " + ruleType + " rule. The "
+              + "characters in this " + kind + " are legal for the JVM, "
+              + "but unlikely to originate from a source language. "
+              + "Maybe this is not the rule you are looking for.",
+          origin, getPosition(start)));
+    }
+
     private void failPartiallyImplementedOption(String optionName, TextPosition start) {
       throw reporter.fatalError(new StringDiagnostic(
           "Option " + optionName + " currently not supported", origin, getPosition(start)));
@@ -1528,5 +1546,25 @@
     boolean isMatchAllNames() {
       return pattern.equals("*");
     }
+
+    boolean hasUnusualCharacters() {
+      if (pattern.contains("<") || pattern.contains(">")) {
+        int angleStartCount = 0;
+        int angleEndCount = 0;
+        for (int i = 0; i < pattern.length(); i++) {
+          char c = pattern.charAt(i);
+          if (c == '<') {
+            angleStartCount++;
+          }
+          if (c == '>') {
+            angleEndCount++;
+          }
+        }
+        // Check that start/end angles are matched, and *only* used for well-formed wildcard
+        // backreferences (e.g. '<1>', but not '<<1>>', '<<*>>' or '>1<').
+        return !(angleStartCount == angleEndCount && angleStartCount == wildcards.size());
+      }
+      return false;
+    }
   }
 }
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 6f935ee..ccc1ccf 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -37,7 +37,6 @@
 import com.google.common.base.Equivalence;
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterators;
 import it.unimi.dsi.fastutil.ints.Int2IntMap;
 import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
@@ -55,7 +54,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.BiFunction;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -697,34 +695,36 @@
         deferredRenamings.map(virtualMethod.method, shadowedBy.method);
       }
 
-      Collection<DexEncodedMethod> mergedDirectMethods =
-          mergeItems(
-              directMethods.values().iterator(),
-              target.directMethods(),
-              MethodSignatureEquivalence.get(),
-              this::resolveMethodConflict);
-      Collection<DexEncodedMethod> mergedVirtualMethods =
-          mergeItems(
-              virtualMethods.values().iterator(),
-              target.virtualMethods(),
-              MethodSignatureEquivalence.get(),
-              this::resolveMethodConflict);
       if (abortMerge) {
         return false;
       }
+
+      DexEncodedMethod[] mergedDirectMethods =
+          mergeMethods(directMethods.values(), target.directMethods());
+      DexEncodedMethod[] mergedVirtualMethods =
+          mergeMethods(virtualMethods.values(), target.virtualMethods());
+
       // Step 2: Merge fields
-      Collection<DexEncodedField> mergedStaticFields =
-          mergeItems(
-              Iterators.forArray(source.staticFields()),
-              target.staticFields(),
-              FieldSignatureEquivalence.get(),
-              this::resolveFieldConflict);
-      Collection<DexEncodedField> mergedInstanceFields =
-          mergeItems(
-              Iterators.forArray(source.instanceFields()),
+      Set<Wrapper<DexField>> existingFieldSignatures = new HashSet<>();
+      addAll(existingFieldSignatures, target.fields(), FieldSignatureEquivalence.get());
+
+      Predicate<DexField> availableFieldSignatures =
+          field -> !existingFieldSignatures.contains(FieldSignatureEquivalence.get().wrap(field));
+
+      DexEncodedField[] mergedInstanceFields =
+          mergeFields(
+              source.instanceFields(),
               target.instanceFields(),
-              FieldSignatureEquivalence.get(),
-              this::resolveFieldConflict);
+              availableFieldSignatures,
+              existingFieldSignatures);
+
+      DexEncodedField[] mergedStaticFields =
+          mergeFields(
+              source.staticFields(),
+              target.staticFields(),
+              availableFieldSignatures,
+              existingFieldSignatures);
+
       // Step 3: Merge interfaces
       Set<DexType> interfaces = mergeArrays(target.interfaces.values, source.interfaces.values);
       // Now destructively update the class.
@@ -739,14 +739,10 @@
           ? DexTypeList.empty()
           : new DexTypeList(interfaces.toArray(new DexType[interfaces.size()]));
       // Step 2: replace fields and methods.
-      target.setDirectMethods(mergedDirectMethods
-          .toArray(new DexEncodedMethod[mergedDirectMethods.size()]));
-      target.setVirtualMethods(mergedVirtualMethods
-          .toArray(new DexEncodedMethod[mergedVirtualMethods.size()]));
-      target.setStaticFields(mergedStaticFields
-          .toArray(new DexEncodedField[mergedStaticFields.size()]));
-      target.setInstanceFields(mergedInstanceFields
-          .toArray(new DexEncodedField[mergedInstanceFields.size()]));
+      target.setDirectMethods(mergedDirectMethods);
+      target.setVirtualMethods(mergedVirtualMethods);
+      target.setInstanceFields(mergedInstanceFields);
+      target.setStaticFields(mergedStaticFields);
       // Step 3: Unlink old class to ease tree shaking.
       source.superType = application.dexItemFactory.objectType;
       source.setDirectMethods(null);
@@ -889,41 +885,38 @@
       return merged;
     }
 
-    private <T extends PresortedComparable<T>, S extends KeyedDexItem<T>> Collection<S> mergeItems(
-        Iterator<S> fromItems,
-        S[] toItems,
-        Equivalence<T> equivalence,
-        BiFunction<S, Predicate<T>, S> onConflict) {
-      Map<Wrapper<T>, S> map = new HashMap<>();
-      // First add everything from the target class. These items are not preprocessed.
-      for (S item : toItems) {
-        map.put(equivalence.wrap(item.getKey()), item);
+    private DexEncodedField[] mergeFields(
+        DexEncodedField[] sourceFields,
+        DexEncodedField[] targetFields,
+        Predicate<DexField> availableFieldSignatures,
+        Set<Wrapper<DexField>> existingFieldSignatures) {
+      DexEncodedField[] result = new DexEncodedField[sourceFields.length + targetFields.length];
+      // Add fields from source
+      int i = 0;
+      for (DexEncodedField field : sourceFields) {
+        DexEncodedField resultingField = renameFieldIfNeeded(field, availableFieldSignatures);
+        existingFieldSignatures.add(FieldSignatureEquivalence.get().wrap(resultingField.field));
+        deferredRenamings.map(field.field, resultingField.field);
+        result[i] = resultingField;
+        i++;
       }
-      // Now add the new items, resolving shadowing.
-      addNonShadowed(fromItems, map, equivalence, onConflict);
-      return map.values();
+      // Add fields from target.
+      System.arraycopy(targetFields, 0, result, i, targetFields.length);
+      return result;
     }
 
-    private <T extends PresortedComparable<T>, S extends KeyedDexItem<T>> void addNonShadowed(
-        Iterator<S> items,
-        Map<Wrapper<T>, S> map,
-        Equivalence<T> equivalence,
-        BiFunction<S, Predicate<T>, S> onConflict) {
-      Predicate<T> availableSignatures = key -> !map.containsKey(equivalence.wrap(key));
-      while (items.hasNext()) {
-        S item = items.next();
-        assert item != null;
-        Wrapper<T> wrapped = equivalence.wrap(item.getKey());
-        if (map.containsKey(wrapped)) {
-          S resolved = onConflict.apply(item, availableSignatures);
-          assert availableSignatures.test(resolved.getKey());
-          wrapped = equivalence.wrap(resolved.getKey());
-          map.put(wrapped, resolved);
-          assert !availableSignatures.test(resolved.getKey());
-        } else {
-          map.put(wrapped, item);
-        }
+    private DexEncodedMethod[] mergeMethods(
+        Collection<DexEncodedMethod> sourceMethods, DexEncodedMethod[] targetMethods) {
+      DexEncodedMethod[] result = new DexEncodedMethod[sourceMethods.size() + targetMethods.length];
+      // Add methods from source.
+      int i = 0;
+      for (DexEncodedMethod method : sourceMethods) {
+        result[i] = method;
+        i++;
       }
+      // Add methods from target.
+      System.arraycopy(targetMethods, 0, result, i, targetMethods.length);
+      return result;
     }
 
     // Note that names returned by this function are not necessarily unique. Clients should
@@ -936,13 +929,6 @@
       return application.dexItemFactory.createString(freshName);
     }
 
-    private DexEncodedMethod resolveMethodConflict(
-        DexEncodedMethod method, Predicate<DexMethod> availableMethodSignatures) {
-      assert false;
-      abortMerge = true;
-      return method;
-    }
-
     private DexEncodedMethod renameConstructor(
         DexEncodedMethod method, Predicate<DexMethod> availableMethodSignatures) {
       assert method.isInstanceInitializer();
@@ -1008,23 +994,24 @@
       return method.toTypeSubstitutedMethod(newSignature);
     }
 
-    private DexEncodedField resolveFieldConflict(
+    private DexEncodedField renameFieldIfNeeded(
         DexEncodedField field, Predicate<DexField> availableFieldSignatures) {
       DexString oldName = field.field.name;
       DexType oldHolder = field.field.clazz;
 
-      DexField newSignature;
-      int count = 1;
-      do {
-        DexString newName = getFreshName(oldName.toSourceString(), count, oldHolder);
-        newSignature =
-            application.dexItemFactory.createField(target.type, field.field.type, newName);
-        count++;
-      } while (!availableFieldSignatures.test(newSignature));
+      DexField newSignature =
+          application.dexItemFactory.createField(target.type, field.field.type, oldName);
+      if (!availableFieldSignatures.test(newSignature)) {
+        int count = 1;
+        do {
+          DexString newName = getFreshName(oldName.toSourceString(), count, oldHolder);
+          newSignature =
+              application.dexItemFactory.createField(target.type, field.field.type, newName);
+          count++;
+        } while (!availableFieldSignatures.test(newSignature));
+      }
 
-      DexEncodedField result = field.toTypeSubstitutedField(newSignature);
-      deferredRenamings.map(field.field, result.field);
-      return result;
+      return field.toTypeSubstitutedField(newSignature);
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
index 2f45ae2..d79aaf0 100644
--- a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
+++ b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
@@ -116,9 +116,14 @@
         messageParts);
   }
 
+  public static Diagnostic checkDiagnostics(List<Diagnostic> diagnostics, int index, Path path,
+      int lineStart, int columnStart, String... messageParts) {
+    return checkDiagnostic(diagnostics.get(index), path, lineStart, columnStart, messageParts);
+  }
+
   public static Diagnostic checkDiagnostics(List<Diagnostic> diagnostics, Path path,
       int lineStart, int columnStart, String... messageParts) {
     assertEquals(1, diagnostics.size());
-    return checkDiagnostic(diagnostics.get(0), path, lineStart, columnStart, messageParts);
+    return checkDiagnostics(diagnostics, 0, path, lineStart, columnStart, messageParts);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index f96dc45..f484bc8 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -1485,7 +1485,7 @@
     ProguardConfigurationParser parser =
         new ProguardConfigurationParser(new DexItemFactory(), reporter);
     parser.parse(proguardConfig);
-    verifyParserEndsCleanly();
+    checkDiagnostics(handler.warnings, proguardConfig, 3, 7, "The field name \"id<<*>>\" is");
 
     verifyWithProguard6(proguardConfig);
   }
@@ -1731,7 +1731,7 @@
     ProguardConfigurationParser parser =
         new ProguardConfigurationParser(new DexItemFactory(), reporter);
     parser.parse(proguardConfig);
-    verifyParserEndsCleanly();
+    checkDiagnostics(handler.warnings, proguardConfig, 2, 5, "The field name \"<fields>\" is");
     verifyWithProguard(proguardConfig);
   }
 
@@ -1753,6 +1753,26 @@
     verifyWithProguard(proguardConfig);
   }
 
+  @Test
+  public void parse_regress110021323() throws Exception {
+    Path proguardConfig = writeTextToTempFile(
+      "-keepclassmembernames class A {",
+      "  <public methods>;",
+      "  <public fields>;",
+      "}"
+    );
+    ProguardConfigurationParser parser =
+        new ProguardConfigurationParser(new DexItemFactory(), reporter);
+    parser.parse(proguardConfig);
+    assertEquals(4, handler.warnings.size());
+    checkDiagnostics(handler.warnings, 0, proguardConfig, 2, 3, "The type \"<public\" is");
+    checkDiagnostics(handler.warnings, 1, proguardConfig, 2, 11, "The field name \"methods>\" is");
+    checkDiagnostics(handler.warnings, 2, proguardConfig, 3, 3, "The type \"<public\" is");
+    checkDiagnostics(handler.warnings, 3, proguardConfig, 3, 11, "The field name \"fields>\" is");
+
+    verifyWithProguard(proguardConfig);
+  }
+
   public void testNotSupported(String option) {
     try {
       reset();
diff --git a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
index 5845dd3..e543d46 100644
--- a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
@@ -86,18 +86,24 @@
         "  public int method();",
         "}"
     ), this::defaultMethodKept);
-    runTest(ImmutableList.of(
-        "-keep class " + ClassImplementingInterface.class.getCanonicalName() + "{",
-        "  <methods>;",
-        "}"
-    ), this::defaultMethodNotKept);
-    runTest(ImmutableList.of(
-        "-keep class " + ClassImplementingInterface.class.getCanonicalName() + "{",
-        "  <methods>;",
-        "}",
-        "-keep class " + TestClass.class.getCanonicalName() + "{",
-        "  public void useInterfaceMethod();",
-        "}"
-    ), this::defaultMethodAbstract);
+    runTest(
+        ImmutableList.of(
+            "-keep class " + ClassImplementingInterface.class.getCanonicalName() + "{",
+            "  <methods>;",
+            "}",
+            // Prevent InterfaceWithDefaultMethods from being merged into ClassImplementingInterface
+            "-keep class " + InterfaceWithDefaultMethods.class.getCanonicalName()),
+        this::defaultMethodNotKept);
+    runTest(
+        ImmutableList.of(
+            "-keep class " + ClassImplementingInterface.class.getCanonicalName() + "{",
+            "  <methods>;",
+            "}",
+            "-keep class " + TestClass.class.getCanonicalName() + "{",
+            "  public void useInterfaceMethod();",
+            "}",
+            // Prevent InterfaceWithDefaultMethods from being merged into ClassImplementingInterface
+            "-keep class " + InterfaceWithDefaultMethods.class.getCanonicalName()),
+        this::defaultMethodAbstract);
   }
 }