Fix join of concrete and unused value state

Change-Id: I6be651ea3dbd5863fb89dc900a82a938440ccbd9
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeValueState.java
index ca98915..91fbd6d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeValueState.java
@@ -65,6 +65,11 @@
   }
 
   @Override
+  public UnusedValueState getCorrespondingUnused() {
+    return unusedArrayTypeState();
+  }
+
+  @Override
   public ConcreteParameterStateKind getKind() {
     return ConcreteParameterStateKind.ARRAY;
   }
@@ -124,7 +129,8 @@
     if (widenInFlow(appView)) {
       return unknown();
     }
-    if (nullabilityChanged || inFlowChanged) {
+    boolean unusedChanged = mutableJoinUnused(inState);
+    if (nullabilityChanged || inFlowChanged || unusedChanged) {
       onChangedAction.execute();
     }
     return this;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeValueState.java
index 96e476b..4bbc3ff 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeValueState.java
@@ -82,6 +82,11 @@
   }
 
   @Override
+  public UnusedValueState getCorrespondingUnused() {
+    return unusedClassTypeState();
+  }
+
+  @Override
   public DynamicType getDynamicType() {
     return dynamicType;
   }
@@ -155,7 +160,8 @@
     if (widenInFlow(appView)) {
       return unknown();
     }
-    if (abstractValueChanged || dynamicTypeChanged || inFlowChanged) {
+    boolean unusedChanged = mutableJoinUnused(inState);
+    if (abstractValueChanged || dynamicTypeChanged || inFlowChanged || unusedChanged) {
       onChangedAction.execute();
     }
     return this;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeValueState.java
index 118c05d..4283477 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeValueState.java
@@ -74,7 +74,8 @@
     if (widenInFlow(appView)) {
       return unknown();
     }
-    if (abstractValueChanged || inFlowChanged) {
+    boolean unusedChanged = mutableJoinUnused(state);
+    if (abstractValueChanged || inFlowChanged || unusedChanged) {
       onChangedAction.execute();
     }
     return this;
@@ -105,6 +106,11 @@
   }
 
   @Override
+  public UnusedValueState getCorrespondingUnused() {
+    return unusedPrimitiveTypeState();
+  }
+
+  @Override
   public ConcreteParameterStateKind getKind() {
     return ConcreteParameterStateKind.PRIMITIVE;
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverValueState.java
index 04711fe..b7bf979 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverValueState.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
@@ -57,6 +58,12 @@
   }
 
   @Override
+  public UnusedValueState getCorrespondingUnused() {
+    // We currently don't generate unused states for receivers.
+    throw new Unreachable();
+  }
+
+  @Override
   public DynamicType getDynamicType() {
     assert !dynamicType.isUnknown();
     return dynamicType;
@@ -117,7 +124,8 @@
     if (widenInFlow(appView)) {
       return unknown();
     }
-    if (dynamicTypeChanged || inFlowChanged) {
+    boolean unusedChanged = mutableJoinUnused(inState);
+    if (dynamicTypeChanged || inFlowChanged || unusedChanged) {
       onChangedAction.execute();
     }
     return this;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteValueState.java
index 065159e..9833d99 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteValueState.java
@@ -27,6 +27,7 @@
   }
 
   private Set<InFlow> inFlow;
+  private boolean unused;
 
   ConcreteValueState(Set<InFlow> inFlow) {
     this.inFlow = inFlow;
@@ -60,6 +61,9 @@
       if (isEffectivelyBottom()) {
         return getCorrespondingBottom();
       }
+      if (isEffectivelyUnused()) {
+        return getCorrespondingUnused();
+      }
     }
     assert !isEffectivelyBottom();
     return this;
@@ -98,12 +102,22 @@
     return traversalContinuation;
   }
 
+  public boolean hasSeenUnused() {
+    return unused;
+  }
+
   public abstract BottomValueState getCorrespondingBottom();
 
+  public abstract UnusedValueState getCorrespondingUnused();
+
   public abstract ConcreteParameterStateKind getKind();
 
   public final boolean isEffectivelyBottom() {
-    return isEffectivelyBottomIgnoringInFlow() && !hasInFlow();
+    return !hasInFlow() && isEffectivelyBottomIgnoringInFlow() && !hasSeenUnused();
+  }
+
+  public final boolean isEffectivelyUnused() {
+    return !hasInFlow() && isEffectivelyBottomIgnoringInFlow() && hasSeenUnused();
   }
 
   public abstract boolean isEffectivelyBottomIgnoringInFlow();
@@ -145,12 +159,15 @@
       DexType outStaticType,
       StateCloner cloner,
       Action onChangedAction) {
-    if (inState.isBottom() || inState.isUnused()) {
+    if (inState.isBottom()) {
       return this;
     }
     if (inState.isUnknown()) {
       return unknown();
     }
+    if (inState.isUnused()) {
+      return mutableJoinUnused(inState.asUnused());
+    }
     ConcreteValueState concreteState = inState.asConcrete();
     if (isReferenceState()) {
       assert concreteState.isReferenceState();
@@ -181,6 +198,20 @@
     return inFlow.addAll(otherInFlow);
   }
 
+  public boolean mutableJoinUnused(ConcreteValueState other) {
+    if (!unused && other.unused) {
+      unused = true;
+      return true;
+    }
+    return false;
+  }
+
+  public ConcreteValueState mutableJoinUnused(UnusedValueState witness) {
+    assert witness != null;
+    unused = true;
+    return this;
+  }
+
   /**
    * Returns true if the in-parameters set should be widened to unknown, in which case the entire
    * parameter state must be widened to unknown.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/StateCloner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/StateCloner.java
index 6843b58..0359b74 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/StateCloner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/StateCloner.java
@@ -27,9 +27,10 @@
           return methodState.mutableCopy();
         }
 
+        @SuppressWarnings("unchecked")
         @Override
-        public ValueState mutableCopy(ValueState parameterState) {
-          return parameterState.mutableCopy();
+        public <T extends ValueState> T mutableCopy(T parameterState) {
+          return (T) parameterState.mutableCopy();
         }
       };
 
@@ -41,7 +42,7 @@
         }
 
         @Override
-        public ValueState mutableCopy(ValueState parameterState) {
+        public <T extends ValueState> T mutableCopy(T parameterState) {
           return parameterState;
         }
       };
@@ -56,5 +57,5 @@
 
   public abstract MethodState mutableCopy(MethodState methodState);
 
-  public abstract ValueState mutableCopy(ValueState parameterState);
+  public abstract <T extends ValueState> T mutableCopy(T parameterState);
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedArrayTypeValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedArrayTypeValueState.java
index e912bca..237b053 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedArrayTypeValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedArrayTypeValueState.java
@@ -34,8 +34,15 @@
     }
     assert inState.isConcrete();
     assert inState.asConcrete().isReferenceState();
-    return ValueState.bottomArrayTypeState()
-        .mutableJoin(appView, inState, inStaticType, outStaticType, cloner, onChangedAction);
+    ValueState result =
+        bottomArrayTypeState()
+            .mutableJoin(appView, inState, inStaticType, outStaticType, cloner, onChangedAction);
+    if (result.isConcrete()) {
+      return result.asConcrete().mutableJoinUnused(this);
+    } else {
+      assert result.isUnknown();
+      return result;
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedClassTypeValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedClassTypeValueState.java
index 9e55e43..77b8e6d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedClassTypeValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedClassTypeValueState.java
@@ -34,8 +34,15 @@
     }
     assert inState.isConcrete();
     assert inState.asConcrete().isReferenceState();
-    return ValueState.bottomClassTypeState()
-        .mutableJoin(appView, inState, inStaticType, outStaticType, cloner, onChangedAction);
+    ValueState result =
+        bottomClassTypeState()
+            .mutableJoin(appView, inState, inStaticType, outStaticType, cloner, onChangedAction);
+    if (result.isConcrete()) {
+      return result.asConcrete().mutableJoinUnused(this);
+    } else {
+      assert result.isUnknown();
+      return result;
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedPrimitiveTypeValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedPrimitiveTypeValueState.java
index 1ea268a..b61b267 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedPrimitiveTypeValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedPrimitiveTypeValueState.java
@@ -34,8 +34,7 @@
     }
     assert inState.isConcrete();
     assert inState.asConcrete().isPrimitiveState();
-    return ValueState.bottomPrimitiveTypeState()
-        .mutableJoin(appView, inState, inStaticType, outStaticType, cloner, onChangedAction);
+    return cloner.mutableCopy(inState.asPrimitiveState()).mutableJoinUnused(this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedValueState.java
index e4d7a81..26560ff 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnusedValueState.java
@@ -22,6 +22,11 @@
   }
 
   @Override
+  public final UnusedValueState asUnused() {
+    return this;
+  }
+
+  @Override
   public final ValueState mutableCopy() {
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ValueState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ValueState.java
index 9876db3..215b468 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ValueState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ValueState.java
@@ -147,6 +147,10 @@
     return false;
   }
 
+  public UnusedValueState asUnused() {
+    return null;
+  }
+
   public abstract ValueState mutableCopy();
 
   public abstract ValueState mutableCopyWithoutInFlow();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
index 840297c..752e59d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
@@ -258,7 +258,7 @@
               appView, flowGraphStateProvider, stateToPropagate, successorNode.getStaticType());
       ValueState oldSuccessorStateForDebugging =
           successorNode.getDebug() ? successorNode.getState().mutableCopy() : null;
-      if (transferState.isBottom()) {
+      if (transferState.isBottom() || transferState.isUnused()) {
         // Nothing to propagate.
       } else if (transferState.isUnknown()) {
         successorNode.setStateToUnknown();
@@ -359,6 +359,8 @@
                     appView.abstractValueFactory().createZeroValue());
           }
           monomorphicMethodState.setParameterState(i, replacement);
+        } else {
+          assert !parameterState.isConcrete() || !parameterState.asConcrete().isEffectivelyUnused();
         }
       }
     }