Merge "Avoid that class merging affects field resolution"
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 5da6f06..4b57209 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -320,6 +320,11 @@
         }
         continue;
       }
+      // Field resolution first considers the direct interfaces of [targetClass] before it proceeds
+      // to the super class.
+      if (fieldResolutionMayChange(clazz, targetClass)) {
+        continue;
+      }
       // Guard against the case where we have two methods that may get the same signature
       // if we replace types. This is rare, so we approximate and err on the safe side here.
       if (new CollisionDetector(clazz.type, targetClass.type, getInvokes(), mergedClasses)
@@ -365,6 +370,35 @@
     return renamedMembersLense.build(graphLense);
   }
 
+  private boolean fieldResolutionMayChange(DexClass source, DexClass target) {
+    if (source.type == target.superType) {
+      // If there is a "iget Target.f" or "iput Target.f" instruction in target, and the class
+      // Target implements an interface that declares a static final field f, this should yield an
+      // IncompatibleClassChangeError.
+      // TODO(christofferqa): In the following we only check if a static field from an interface
+      // shadows an instance field from [source]. We could actually check if there is an iget/iput
+      // instruction whose resolution would be affected by the merge. The situation where a static
+      // field shadows an instance field is probably not widespread in practice, though.
+      FieldSignatureEquivalence equivalence = FieldSignatureEquivalence.get();
+      Set<Wrapper<DexField>> staticFieldsInInterfacesOfTarget = new HashSet<>();
+      for (DexType interfaceType : target.interfaces.values) {
+        DexClass clazz = appInfo.definitionFor(interfaceType);
+        for (DexEncodedField staticField : clazz.staticFields()) {
+          staticFieldsInInterfacesOfTarget.add(equivalence.wrap(staticField.field));
+        }
+      }
+      for (DexEncodedField instanceField : source.instanceFields()) {
+        if (staticFieldsInInterfacesOfTarget.contains(equivalence.wrap(instanceField.field))) {
+          // An instruction "iget Target.f" or "iput Target.f" that used to hit a static field in an
+          // interface would now hit an instance field from [source], so that an IncompatibleClass-
+          // ChangeError would no longer be thrown. Abort merge.
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
   private class ClassMerger {
 
     private static final String CONSTRUCTOR_NAME = "constructor";