Patch dynamic type using NullOrAbstractValue

Bug: 172528424
Change-Id: I8691c4cd2a1b17b254b24c081650e0fd7acbdf8b
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index be89019..3ab1305 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -109,11 +109,19 @@
     // Abstract value.
     feedback.recordFieldHasAbstractValue(field, appView, getOrComputeAbstractValue(value, field));
 
+    setDynamicType(field, value, false);
+  }
+
+  private void setDynamicType(DexEncodedField field, Value value, boolean maybeNull) {
     // Dynamic upper bound type.
     TypeElement fieldType =
         TypeElement.fromDexType(field.field.type, Nullability.maybeNull(), appView);
     TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
     if (dynamicUpperBoundType.strictlyLessThan(fieldType, appView)) {
+      if (maybeNull && dynamicUpperBoundType.isDefinitelyNotNull()) {
+        assert dynamicUpperBoundType.isReferenceType();
+        dynamicUpperBoundType = dynamicUpperBoundType.asReferenceType().asMaybeNull();
+      }
       feedback.markFieldHasDynamicUpperBoundType(field, dynamicUpperBoundType);
     }
 
@@ -121,6 +129,9 @@
     ClassTypeElement dynamicLowerBoundType = value.getDynamicLowerBoundType(appView);
     if (dynamicLowerBoundType != null) {
       assert dynamicLowerBoundType.lessThanOrEqual(dynamicUpperBoundType, appView);
+      if (maybeNull && dynamicLowerBoundType.isDefinitelyNotNull()) {
+        dynamicLowerBoundType = dynamicLowerBoundType.asMaybeNull().asClassType();
+      }
       feedback.markFieldHasDynamicLowerBoundType(field, dynamicLowerBoundType);
     }
   }
@@ -134,7 +145,8 @@
     }
     feedback.recordFieldHasAbstractValue(
         field, appView, NullOrAbstractValue.create(getOrComputeAbstractValue(valuePut, field)));
-    // TODO(b/172528424): investigate if we can set the dynamic type if it's only null vs a value.
+
+    setDynamicType(field, valuePut, true);
   }
 
   private AbstractValue getOrComputeAbstractValue(Value value, DexEncodedField field) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
index 8bfecf4..e53f27b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -106,6 +106,12 @@
     if (equals(other)) {
       return this;
     }
+    if (isNull()) {
+      return NullOrAbstractValue.create(other);
+    }
+    if (other.isNull()) {
+      return NullOrAbstractValue.create(this);
+    }
     return UnknownValue.getInstance();
   }