Widen parameter states to unknown when dynamic type is trivial

This passes the static type of a given parameter to the join methods in ParameterState. This allows the join method in BottomClassTypeParameterState and ConcreteClassTypeParameterState to check if the resulting computed dynamic type is trivial according to the static type, in which case the state can be replaced by 'unknown' (if the abstract value is also unknown).

Change-Id: I927cdc06a281b5680128875e134a4e130d8eec00
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index b39a1a7..765fef3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -59,6 +59,10 @@
     return getDefinition().getOptimizationInfo();
   }
 
+  public DexType getArgumentType(int index) {
+    return getDefinition().getArgumentType(index);
+  }
+
   public DexType getParameter(int index) {
     return getReference().getParameter(index);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodSignature.java b/src/main/java/com/android/tools/r8/graph/DexMethodSignature.java
index 562cee4..0ac4558 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodSignature.java
@@ -29,6 +29,10 @@
     return getProto().getArity();
   }
 
+  public DexType getParameter(int index) {
+    return getProto().getParameter(index);
+  }
+
   public DexType getReturnType() {
     return getProto().getReturnType();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index 9e83183..65e9670 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -135,6 +135,11 @@
   }
 
   @Override
+  public ClassTypeElement asDefinitelyNotNull() {
+    return getOrCreateVariant(Nullability.definitelyNotNull());
+  }
+
+  @Override
   public ClassTypeElement asMeetWithNotNull() {
     return getOrCreateVariant(nullability.meet(Nullability.definitelyNotNull()));
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
index cae552a..1a32390 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
@@ -107,6 +107,10 @@
     return null;
   }
 
+  public Nullability getNullability() {
+    return getDynamicUpperBoundType().nullability();
+  }
+
   public boolean isBottom() {
     return getDynamicUpperBoundType().isBottom();
   }
@@ -115,10 +119,6 @@
     return getDynamicUpperBoundType().isNullType();
   }
 
-  public boolean isTrivial(TypeElement staticType) {
-    return staticType.equals(getDynamicUpperBoundType()) || isUnknown();
-  }
-
   public boolean isUnknown() {
     return getDynamicUpperBoundType().isTop();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java
index 0983de7..e4190bc 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java
@@ -49,11 +49,6 @@
   }
 
   @Override
-  public boolean isTrivial(TypeElement staticType) {
-    return false;
-  }
-
-  @Override
   public boolean equals(Object other) {
     if (other == null || getClass() != other.getClass()) {
       return false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java
index ed9f300..d66cfcc 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ExactDynamicType.java
@@ -26,11 +26,6 @@
   }
 
   @Override
-  public boolean isTrivial(TypeElement staticType) {
-    return false;
-  }
-
-  @Override
   public boolean equals(Object other) {
     if (other == null || getClass() != other.getClass()) {
       return false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/Nullability.java b/src/main/java/com/android/tools/r8/ir/analysis/type/Nullability.java
index e45ac9a..c3a5298 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/Nullability.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/Nullability.java
@@ -49,6 +49,10 @@
     return isMaybeNull() || isDefinitelyNull();
   }
 
+  public boolean isUnknown() {
+    return isMaybeNull();
+  }
+
   public Nullability join(Nullability other) {
     if (this == BOTTOM) {
       return other;
@@ -79,6 +83,10 @@
     return join(other) == other;
   }
 
+  public boolean strictlyLessThan(Nullability other) {
+    return !equals(other) && other == join(other);
+  }
+
   public static Nullability definitelyNull() {
     return DEFINITELY_NULL;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
index d7d7047..ae702f9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -250,11 +250,8 @@
             assert false;
           }
         } else if (staticType.isClassType()) {
-          DynamicType dynamicType =
-              method.getDefinition().isInstance() && argumentIndex == 0
-                  ? concreteParameterState.asReceiverParameter().getDynamicType()
-                  : concreteParameterState.asClassParameter().getDynamicType();
-          if (!dynamicType.isTrivial(staticTypeElement)) {
+          DynamicType dynamicType = concreteParameterState.asReferenceParameter().getDynamicType();
+          if (!dynamicType.isUnknown()) {
             newCallSiteInfo.dynamicUpperBoundTypes.put(
                 argumentIndex, dynamicType.getDynamicUpperBoundType());
             isTop = false;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index ad6e869..f8edd1e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -23,7 +23,6 @@
 import java.util.concurrent.ExecutorService;
 
 /** Optimization that propagates information about arguments from call sites to method entries. */
-// TODO(b/190154391): Add timing information for performance tracking.
 public class ArgumentPropagator {
 
   private final AppView<AppInfoWithLiveness> appView;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index 2922594..6a4f31c 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -9,9 +9,9 @@
 import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -39,6 +39,7 @@
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.UnknownMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.Iterables;
@@ -217,7 +218,7 @@
       assert existingMethodState.isBottom() || existingMethodState.isMonomorphic();
       result =
           computeMonomorphicMethodState(
-              invoke, context, existingMethodState.asMonomorphicOrBottom());
+              invoke, resolvedMethod, context, existingMethodState.asMonomorphicOrBottom());
     }
     timing.end();
     return result;
@@ -258,6 +259,7 @@
     ConcreteMonomorphicMethodStateOrUnknown methodStateForBounds =
         computeMonomorphicMethodState(
             invoke,
+            resolvedMethod,
             context,
             existingMethodStateForBounds.asMonomorphicOrBottom(),
             dynamicReceiverType);
@@ -301,10 +303,12 @@
 
   private ConcreteMonomorphicMethodStateOrUnknown computeMonomorphicMethodState(
       InvokeMethod invoke,
+      ProgramMethod resolvedMethod,
       ProgramMethod context,
       ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
     return computeMonomorphicMethodState(
         invoke,
+        resolvedMethod,
         context,
         existingMethodState,
         invoke.isInvokeMethodWithReceiver()
@@ -314,6 +318,7 @@
 
   private ConcreteMonomorphicMethodStateOrUnknown computeMonomorphicMethodState(
       InvokeMethod invoke,
+      ProgramMethod resolvedMethod,
       ProgramMethod context,
       ConcreteMonomorphicMethodStateOrBottom existingMethodState,
       DynamicType dynamicReceiverType) {
@@ -324,7 +329,10 @@
       assert dynamicReceiverType != null;
       parameterStates.add(
           computeParameterStateForReceiver(
-              invoke.asInvokeMethodWithReceiver(), dynamicReceiverType, existingMethodState));
+              invoke.asInvokeMethodWithReceiver(),
+              resolvedMethod,
+              dynamicReceiverType,
+              existingMethodState));
       argumentIndex++;
     }
 
@@ -353,6 +361,7 @@
   //  computeParameterStateForNonReceiver() for receivers.
   private ParameterState computeParameterStateForReceiver(
       InvokeMethodWithReceiver invoke,
+      ProgramMethod resolvedMethod,
       DynamicType dynamicReceiverType,
       ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
     // Don't compute a state for this parameter if the stored state is already unknown.
@@ -361,14 +370,9 @@
       return ParameterState.unknown();
     }
 
-    ClassTypeElement staticReceiverType =
-        invoke
-            .getInvokedMethod()
-            .getHolderType()
-            .toTypeElement(appView)
-            .asClassType()
-            .asMeetWithNotNull();
-    return dynamicReceiverType.isTrivial(staticReceiverType)
+    DynamicType widenedDynamicReceiverType =
+        WideningUtils.widenDynamicReceiverType(appView, resolvedMethod, dynamicReceiverType);
+    return widenedDynamicReceiverType.isUnknown()
         ? ParameterState.unknown()
         : new ConcreteReceiverParameterState(dynamicReceiverType);
   }
@@ -386,11 +390,9 @@
     }
 
     Value argumentRoot = argument.getAliasedValue(aliasedValueConfiguration);
-    TypeElement parameterType =
-        invoke
-            .getInvokedMethod()
-            .getArgumentType(argumentIndex, invoke.isInvokeStatic())
-            .toTypeElement(appView);
+    DexType parameterType =
+        invoke.getInvokedMethod().getArgumentType(argumentIndex, invoke.isInvokeStatic());
+    TypeElement parameterTypeElement = parameterType.toTypeElement(appView);
 
     // If the value is an argument of the enclosing method, then clearly we have no information
     // about its abstract value. Instead of treating this as having an unknown runtime value, we
@@ -401,18 +403,18 @@
       MethodParameter forwardedParameter =
           new MethodParameter(
               context.getReference(), argumentRoot.getDefinition().asArgument().getIndex());
-      if (parameterType.isClassType()) {
+      if (parameterTypeElement.isClassType()) {
         return new ConcreteClassTypeParameterState(forwardedParameter);
-      } else if (parameterType.isArrayType()) {
+      } else if (parameterTypeElement.isArrayType()) {
         return new ConcreteArrayTypeParameterState(forwardedParameter);
       } else {
-        assert parameterType.isPrimitiveType();
+        assert parameterTypeElement.isPrimitiveType();
         return new ConcretePrimitiveTypeParameterState(forwardedParameter);
       }
     }
 
     // Only track the nullability for array types.
-    if (parameterType.isArrayType()) {
+    if (parameterTypeElement.isArrayType()) {
       Nullability nullability = argument.getType().nullability();
       return nullability.isMaybeNull()
           ? ParameterState.unknown()
@@ -423,16 +425,18 @@
 
     // For class types, we track both the abstract value and the dynamic type. If both are unknown,
     // then use UnknownParameterState.
-    if (parameterType.isClassType()) {
+    if (parameterTypeElement.isClassType()) {
       DynamicType dynamicType = argument.getDynamicType(appView);
-      return abstractValue.isUnknown() && dynamicType.isTrivial(parameterType)
+      DynamicType widenedDynamicType =
+          WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, parameterType);
+      return abstractValue.isUnknown() && widenedDynamicType.isUnknown()
           ? ParameterState.unknown()
-          : new ConcreteClassTypeParameterState(abstractValue, dynamicType);
+          : new ConcreteClassTypeParameterState(abstractValue, widenedDynamicType);
     }
 
     // For primitive types, we only track the abstract value, thus if the abstract value is unknown,
     // we use UnknownParameterState.
-    assert parameterType.isPrimitiveType();
+    assert parameterTypeElement.isPrimitiveType();
     return abstractValue.isUnknown()
         ? ParameterState.unknown()
         : new ConcretePrimitiveTypeParameterState(abstractValue);
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
index 3d8ccb4..4548f35 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -7,8 +7,10 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMethodState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
@@ -19,6 +21,7 @@
 import com.android.tools.r8.optimize.argumentpropagation.propagation.InParameterFlowPropagator;
 import com.android.tools.r8.optimize.argumentpropagation.propagation.InterfaceMethodArgumentPropagator;
 import com.android.tools.r8.optimize.argumentpropagation.propagation.VirtualDispatchMethodArgumentPropagator;
+import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
@@ -29,6 +32,7 @@
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.stream.IntStream;
 
 /**
  * Propagates the argument flow information collected by the {@link ArgumentPropagatorCodeScanner}.
@@ -199,6 +203,27 @@
         .map(ParameterState::asConcrete)
         .noneMatch(ConcreteParameterState::hasInParameters);
 
+    // Verify that the dynamic type information is correct.
+    assert IntStream.range(0, monomorphicMethodState.getParameterStates().size())
+        .filter(
+            index -> {
+              ParameterState parameterState = monomorphicMethodState.getParameterState(index);
+              return parameterState.isConcrete() && parameterState.asConcrete().isClassParameter();
+            })
+        .allMatch(
+            index -> {
+              DynamicType dynamicType =
+                  monomorphicMethodState
+                      .getParameterState(index)
+                      .asConcrete()
+                      .asClassParameter()
+                      .getDynamicType();
+              DexType staticType = method.getArgumentType(index);
+              assert dynamicType
+                  == WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, staticType);
+              return true;
+            });
+
     method
         .getDefinition()
         .joinCallSiteOptimizationInfo(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomArrayTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomArrayTypeParameterState.java
index 1ffdd50..666c87b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomArrayTypeParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomArrayTypeParameterState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Action;
@@ -21,7 +22,10 @@
 
   @Override
   public ParameterState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, ParameterState parameterState, Action onChangedAction) {
+      AppView<AppInfoWithLiveness> appView,
+      ParameterState parameterState,
+      DexType parameterType,
+      Action onChangedAction) {
     if (parameterState.isBottom()) {
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomClassTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomClassTypeParameterState.java
index 0c91acb..d1df0ac 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomClassTypeParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomClassTypeParameterState.java
@@ -5,8 +5,10 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Action;
 
@@ -22,7 +24,10 @@
 
   @Override
   public ParameterState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, ParameterState parameterState, Action onChangedAction) {
+      AppView<AppInfoWithLiveness> appView,
+      ParameterState parameterState,
+      DexType parameterType,
+      Action onChangedAction) {
     if (parameterState.isBottom()) {
       return this;
     }
@@ -35,10 +40,11 @@
         parameterState.asConcrete().asReferenceParameter();
     AbstractValue abstractValue = concreteParameterState.getAbstractValue(appView);
     DynamicType dynamicType = concreteParameterState.getDynamicType();
-    if (abstractValue.isUnknown() && dynamicType.isUnknown()) {
-      return unknown();
-    }
-    return new ConcreteClassTypeParameterState(
-        abstractValue, dynamicType, concreteParameterState.copyInParameters());
+    DynamicType widenedDynamicType =
+        WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, parameterType);
+    return abstractValue.isUnknown() && widenedDynamicType.isUnknown()
+        ? unknown()
+        : new ConcreteClassTypeParameterState(
+            abstractValue, widenedDynamicType, concreteParameterState.copyInParameters());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomMethodState.java
index 4487eea..f5f117e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomMethodState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.function.Function;
 
@@ -40,14 +41,18 @@
   }
 
   @Override
-  public MethodState mutableJoin(AppView<AppInfoWithLiveness> appView, MethodState methodState) {
+  public MethodState mutableJoin(
+      AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
+      MethodState methodState) {
     return methodState.mutableCopy();
   }
 
   @Override
   public MethodState mutableJoin(
       AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
       Function<MethodState, MethodState> methodStateSupplier) {
-    return mutableJoin(appView, methodStateSupplier.apply(this));
+    return mutableJoin(appView, methodSignature, methodStateSupplier.apply(this));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomPrimitiveTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomPrimitiveTypeParameterState.java
index 48908bd..fc88145 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomPrimitiveTypeParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomPrimitiveTypeParameterState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Action;
 
@@ -21,7 +22,10 @@
 
   @Override
   public ParameterState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, ParameterState parameterState, Action onChangedAction) {
+      AppView<AppInfoWithLiveness> appView,
+      ParameterState parameterState,
+      DexType parameterType,
+      Action onChangedAction) {
     if (parameterState.isBottom()) {
       assert parameterState == bottomPrimitiveTypeParameter();
       return this;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomReceiverParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomReceiverParameterState.java
index f00974c..3a94858 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomReceiverParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomReceiverParameterState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Action;
@@ -21,7 +22,10 @@
 
   @Override
   public ParameterState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, ParameterState parameterState, Action onChangedAction) {
+      AppView<AppInfoWithLiveness> appView,
+      ParameterState parameterState,
+      DexType parameterType,
+      Action onChangedAction) {
     if (parameterState.isBottom()) {
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeParameterState.java
index 90a7208..eb75ac9 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeParameterState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -96,10 +97,12 @@
   public ParameterState mutableJoin(
       AppView<AppInfoWithLiveness> appView,
       ConcreteReferenceTypeParameterState parameterState,
+      DexType parameterType,
       Action onChangedAction) {
-    assert !nullability.isMaybeNull();
+    assert parameterType.isArrayType();
+    assert !nullability.isUnknown();
     nullability = nullability.join(parameterState.getNullability());
-    if (nullability.isMaybeNull()) {
+    if (nullability.isUnknown()) {
       return unknown();
     }
     boolean inParametersChanged = mutableJoinInParameters(parameterState);
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
index 2cd935a..6dc6c5c 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
@@ -5,9 +5,11 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Action;
 import com.android.tools.r8.utils.SetUtils;
@@ -96,7 +98,9 @@
   public ParameterState mutableJoin(
       AppView<AppInfoWithLiveness> appView,
       ConcreteReferenceTypeParameterState parameterState,
+      DexType parameterType,
       Action onChangedAction) {
+    assert parameterType.isClassType();
     boolean allowNullOrAbstractValue = true;
     boolean allowNonConstantNumbers = false;
     AbstractValue oldAbstractValue = abstractValue;
@@ -106,10 +110,11 @@
             appView.abstractValueFactory(),
             allowNullOrAbstractValue,
             allowNonConstantNumbers);
-    // TODO(b/190154391): Take in the static type as an argument, and unset the dynamic type if it
-    //  equals the static type.
     DynamicType oldDynamicType = dynamicType;
-    dynamicType = dynamicType.join(appView, parameterState.getDynamicType());
+    DynamicType joinedDynamicType = dynamicType.join(appView, parameterState.getDynamicType());
+    DynamicType widenedDynamicType =
+        WideningUtils.widenDynamicNonReceiverType(appView, joinedDynamicType, parameterType);
+    dynamicType = widenedDynamicType;
     if (abstractValue.isUnknown() && dynamicType.isUnknown()) {
       return unknown();
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMethodState.java
index bfb7a89..ecd3e08 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMethodState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.function.Function;
 
@@ -21,30 +22,36 @@
   }
 
   @Override
-  public MethodState mutableJoin(AppView<AppInfoWithLiveness> appView, MethodState methodState) {
+  public MethodState mutableJoin(
+      AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
+      MethodState methodState) {
     if (methodState.isBottom()) {
       return this;
     }
     if (methodState.isUnknown()) {
       return methodState;
     }
-    return mutableJoin(appView, methodState.asConcrete());
+    return mutableJoin(appView, methodSignature, methodState.asConcrete());
   }
 
   @Override
   public MethodState mutableJoin(
       AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
       Function<MethodState, MethodState> methodStateSupplier) {
-    return mutableJoin(appView, methodStateSupplier.apply(this));
+    return mutableJoin(appView, methodSignature, methodStateSupplier.apply(this));
   }
 
   private MethodState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, ConcreteMethodState methodState) {
+      AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
+      ConcreteMethodState methodState) {
     if (isMonomorphic() && methodState.isMonomorphic()) {
-      return asMonomorphic().mutableJoin(appView, methodState.asMonomorphic());
+      return asMonomorphic().mutableJoin(appView, methodSignature, methodState.asMonomorphic());
     }
     if (isPolymorphic() && methodState.isPolymorphic()) {
-      return asPolymorphic().mutableJoin(appView, methodState.asPolymorphic());
+      return asPolymorphic().mutableJoin(appView, methodSignature, methodState.asPolymorphic());
     }
     assert false;
     return unknown();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
index 908d97d..b49e48f 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
@@ -5,6 +5,8 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethodSignature;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Streams;
@@ -42,19 +44,33 @@
   }
 
   public ConcreteMonomorphicMethodStateOrUnknown mutableJoin(
-      AppView<AppInfoWithLiveness> appView, ConcreteMonomorphicMethodState methodState) {
+      AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
+      ConcreteMonomorphicMethodState methodState) {
     if (size() != methodState.size()) {
       assert false;
       return unknown();
     }
 
-    for (int i = 0; i < size(); i++) {
-      ParameterState parameterState = parameterStates.get(i);
-      ParameterState otherParameterState = methodState.parameterStates.get(i);
-      parameterStates.set(i, parameterState.mutableJoin(appView, otherParameterState));
-      assert i == 0
-          || !parameterStates.get(i).isConcrete()
-          || !parameterStates.get(i).asConcrete().isReceiverParameter();
+    int argumentIndex = 0;
+    if (size() > methodSignature.getArity()) {
+      assert size() == methodSignature.getArity() + 1;
+      ParameterState parameterState = parameterStates.get(0);
+      ParameterState otherParameterState = methodState.parameterStates.get(0);
+      DexType parameterType = null;
+      parameterStates.set(
+          0, parameterState.mutableJoin(appView, otherParameterState, parameterType));
+      argumentIndex++;
+    }
+
+    for (int parameterIndex = 0; argumentIndex < size(); argumentIndex++, parameterIndex++) {
+      ParameterState parameterState = parameterStates.get(argumentIndex);
+      ParameterState otherParameterState = methodState.parameterStates.get(argumentIndex);
+      DexType parameterType = methodSignature.getParameter(parameterIndex);
+      parameterStates.set(
+          argumentIndex, parameterState.mutableJoin(appView, otherParameterState, parameterType));
+      assert !parameterStates.get(argumentIndex).isConcrete()
+          || !parameterStates.get(argumentIndex).asConcrete().isReceiverParameter();
     }
 
     if (Iterables.all(parameterStates, ParameterState::isUnknown)) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteParameterState.java
index 101c543..3b9da36 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteParameterState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Action;
 import java.util.Collections;
@@ -103,7 +104,10 @@
 
   @Override
   public final ParameterState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, ParameterState parameterState, Action onChangedAction) {
+      AppView<AppInfoWithLiveness> appView,
+      ParameterState parameterState,
+      DexType parameterType,
+      Action onChangedAction) {
     if (parameterState.isBottom()) {
       return this;
     }
@@ -114,10 +118,15 @@
     if (isReferenceParameter()) {
       assert concreteParameterState.isReferenceParameter();
       return asReferenceParameter()
-          .mutableJoin(appView, concreteParameterState.asReferenceParameter(), onChangedAction);
+          .mutableJoin(
+              appView,
+              concreteParameterState.asReferenceParameter(),
+              parameterType,
+              onChangedAction);
     }
     return asPrimitiveParameter()
-        .mutableJoin(appView, concreteParameterState.asPrimitiveParameter(), onChangedAction);
+        .mutableJoin(
+            appView, concreteParameterState.asPrimitiveParameter(), parameterType, onChangedAction);
   }
 
   boolean mutableJoinInParameters(ConcreteParameterState parameterState) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java
index 5b1def8..d847c84 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.HashMap;
@@ -41,6 +42,7 @@
 
   private ConcretePolymorphicMethodStateOrUnknown add(
       AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
       DynamicType bounds,
       ConcreteMonomorphicMethodStateOrUnknown methodState) {
     assert !isEffectivelyBottom();
@@ -55,7 +57,7 @@
     } else {
       assert methodState.isMonomorphic();
       ConcreteMonomorphicMethodStateOrUnknown newMethodStateForBounds =
-          joinInner(appView, receiverBoundsToState.get(bounds), methodState);
+          joinInner(appView, methodSignature, receiverBoundsToState.get(bounds), methodState);
       if (bounds.isUnknown() && newMethodStateForBounds.isUnknown()) {
         return unknown();
       } else {
@@ -67,6 +69,7 @@
 
   private static ConcreteMonomorphicMethodStateOrUnknown joinInner(
       AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
       ConcreteMonomorphicMethodStateOrUnknown methodState,
       ConcreteMonomorphicMethodStateOrUnknown other) {
     if (methodState == null) {
@@ -76,7 +79,7 @@
       return unknown();
     }
     assert methodState.isMonomorphic();
-    return methodState.asMonomorphic().mutableJoin(appView, other.asMonomorphic());
+    return methodState.asMonomorphic().mutableJoin(appView, methodSignature, other.asMonomorphic());
   }
 
   public void forEach(
@@ -112,7 +115,9 @@
   }
 
   public MethodState mutableCopyWithRewrittenBounds(
-      AppView<AppInfoWithLiveness> appView, Function<DynamicType, DynamicType> boundsRewriter) {
+      AppView<AppInfoWithLiveness> appView,
+      Function<DynamicType, DynamicType> boundsRewriter,
+      DexMethodSignature methodSignature) {
     assert !isEffectivelyBottom();
     assert !isEffectivelyUnknown();
     Map<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> rewrittenReceiverBoundsToState =
@@ -126,7 +131,7 @@
       ConcreteMonomorphicMethodStateOrUnknown existingMethodStateForBounds =
           rewrittenReceiverBoundsToState.get(rewrittenBounds);
       ConcreteMonomorphicMethodStateOrUnknown newMethodStateForBounds =
-          joinInner(appView, existingMethodStateForBounds, entry.getValue());
+          joinInner(appView, methodSignature, existingMethodStateForBounds, entry.getValue());
       if (rewrittenBounds.isUnknown() && newMethodStateForBounds.isUnknown()) {
         return unknown();
       }
@@ -138,7 +143,9 @@
   }
 
   public MethodState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, ConcretePolymorphicMethodState methodState) {
+      AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
+      ConcretePolymorphicMethodState methodState) {
     assert !isEffectivelyBottom();
     assert !isEffectivelyUnknown();
     assert !methodState.isEffectivelyBottom();
@@ -146,7 +153,7 @@
     for (Entry<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> entry :
         methodState.receiverBoundsToState.entrySet()) {
       ConcretePolymorphicMethodStateOrUnknown result =
-          add(appView, entry.getKey(), entry.getValue());
+          add(appView, methodSignature, entry.getKey(), entry.getValue());
       if (result.isUnknown()) {
         return result;
       }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeParameterState.java
index 4f48f07..3f1baff 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeParameterState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Action;
@@ -52,7 +53,9 @@
   public ParameterState mutableJoin(
       AppView<AppInfoWithLiveness> appView,
       ConcretePrimitiveTypeParameterState parameterState,
+      DexType parameterType,
       Action onChangedAction) {
+    assert parameterType.isPrimitiveType();
     boolean allowNullOrAbstractValue = false;
     boolean allowNonConstantNumbers = false;
     AbstractValue oldAbstractValue = abstractValue;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverParameterState.java
index 9a94286..d7f7bbc 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverParameterState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -88,9 +89,11 @@
   public ParameterState mutableJoin(
       AppView<AppInfoWithLiveness> appView,
       ConcreteReferenceTypeParameterState parameterState,
+      DexType parameterType,
       Action onChangedAction) {
-    // TODO(b/190154391): Take in the static type as an argument, and unset the dynamic type if it
-    //  equals the static type.
+    // TODO(b/190154391): Always take in the static type as an argument, and unset the dynamic type
+    //  if it equals the static type.
+    assert parameterType == null || parameterType.isClassType();
     DynamicType oldDynamicType = dynamicType;
     dynamicType = dynamicType.join(appView, parameterState.getDynamicType());
     if (dynamicType.isUnknown()) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReferenceTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReferenceTypeParameterState.java
index 016a0c7..30506a0 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReferenceTypeParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReferenceTypeParameterState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -34,5 +35,6 @@
   public abstract ParameterState mutableJoin(
       AppView<AppInfoWithLiveness> appView,
       ConcreteReferenceTypeParameterState parameterState,
+      DexType parameterType,
       Action onChangedAction);
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodState.java
index 97124a8..0de2d3e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.function.Function;
 
@@ -40,8 +41,13 @@
 
   MethodState mutableCopy();
 
-  MethodState mutableJoin(AppView<AppInfoWithLiveness> appView, MethodState methodState);
+  MethodState mutableJoin(
+      AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
+      MethodState methodState);
 
   MethodState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, Function<MethodState, MethodState> methodStateSupplier);
+      AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
+      Function<MethodState, MethodState> methodStateSupplier);
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java
index 3a54fe8..50f02c9 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Timing;
@@ -24,6 +25,8 @@
 
   abstract K getKey(ProgramMethod method);
 
+  abstract DexMethodSignature getSignature(K method);
+
   public void addMethodState(
       AppView<AppInfoWithLiveness> appView, ProgramMethod method, MethodState methodState) {
     addMethodState(appView, getKey(method), methodState);
@@ -41,7 +44,8 @@
             if (existingMethodState == null) {
               newMethodState = methodState.mutableCopy();
             } else {
-              newMethodState = existingMethodState.mutableJoin(appView, methodState);
+              newMethodState =
+                  existingMethodState.mutableJoin(appView, getSignature(method), methodState);
             }
             assert !newMethodState.isBottom();
             return newMethodState;
@@ -68,7 +72,8 @@
           }
           assert !existingMethodState.isBottom();
           timing.begin("Join temporary method state");
-          MethodState joinResult = existingMethodState.mutableJoin(appView, methodStateSupplier);
+          MethodState joinResult =
+              existingMethodState.mutableJoin(appView, getSignature(method), methodStateSupplier);
           assert !joinResult.isBottom();
           timing.end();
           return joinResult;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionByReference.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionByReference.java
index ea48bfb..a715205 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionByReference.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionByReference.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.graph.ProgramMethod;
 import java.util.IdentityHashMap;
 import java.util.Map;
@@ -28,4 +29,9 @@
   DexMethod getKey(ProgramMethod method) {
     return method.getReference();
   }
+
+  @Override
+  DexMethodSignature getSignature(DexMethod method) {
+    return method.getSignature();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionBySignature.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionBySignature.java
index 2113be0..a244616 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionBySignature.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionBySignature.java
@@ -28,4 +28,9 @@
   DexMethodSignature getKey(ProgramMethod method) {
     return method.getMethodSignature();
   }
+
+  @Override
+  DexMethodSignature getSignature(DexMethodSignature method) {
+    return method;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ParameterState.java
index 0aa7d09..ad7b97c 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ParameterState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Action;
@@ -56,10 +57,13 @@
   public abstract ParameterState mutableCopy();
 
   public final ParameterState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, ParameterState parameterState) {
-    return mutableJoin(appView, parameterState, Action.empty());
+      AppView<AppInfoWithLiveness> appView, ParameterState parameterState, DexType parameterType) {
+    return mutableJoin(appView, parameterState, parameterType, Action.empty());
   }
 
   public abstract ParameterState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, ParameterState parameterState, Action onChangedAction);
+      AppView<AppInfoWithLiveness> appView,
+      ParameterState parameterState,
+      DexType parameterType,
+      Action onChangedAction);
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownMethodState.java
index e8b2ca1..b4bb41f 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownMethodState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import java.util.function.Function;
 
@@ -31,13 +32,17 @@
   }
 
   @Override
-  public MethodState mutableJoin(AppView<AppInfoWithLiveness> appView, MethodState methodState) {
+  public MethodState mutableJoin(
+      AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
+      MethodState methodState) {
     return this;
   }
 
   @Override
   public MethodState mutableJoin(
       AppView<AppInfoWithLiveness> appView,
+      DexMethodSignature methodSignature,
       Function<MethodState, MethodState> methodStateSupplier) {
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownParameterState.java
index 8a49ef7..bcb5317 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownParameterState.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.value.AbstractValue;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Action;
@@ -36,7 +37,10 @@
 
   @Override
   public ParameterState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, ParameterState parameterState, Action onChangedAction) {
+      AppView<AppInfoWithLiveness> appView,
+      ParameterState parameterState,
+      DexType parameterType,
+      Action onChangedAction) {
     return this;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java
index ed1bdce..b551b68 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMethodState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
@@ -227,7 +228,8 @@
           (ignore, parameterNode) ->
               parameterNode != null
                   ? parameterNode
-                  : new ParameterNode(methodState, parameterIndex));
+                  : new ParameterNode(
+                      methodState, parameterIndex, method.getArgumentType(parameterIndex)));
     }
 
     private ProgramMethod getEnclosingMethod(MethodParameter methodParameter) {
@@ -250,15 +252,18 @@
 
     private final ConcreteMonomorphicMethodState methodState;
     private final int parameterIndex;
+    private final DexType parameterType;
 
     private final Set<ParameterNode> predecessors = Sets.newIdentityHashSet();
     private final Set<ParameterNode> successors = Sets.newIdentityHashSet();
 
     private boolean pending = true;
 
-    ParameterNode(ConcreteMonomorphicMethodState methodState, int parameterIndex) {
+    ParameterNode(
+        ConcreteMonomorphicMethodState methodState, int parameterIndex, DexType parameterType) {
       this.methodState = methodState;
       this.parameterIndex = parameterIndex;
+      this.parameterType = parameterType;
     }
 
     void addPredecessor(ParameterNode predecessor) {
@@ -295,7 +300,8 @@
         Action onChangedAction) {
       ParameterState oldParameterState = getState();
       ParameterState newParameterState =
-          oldParameterState.mutableJoin(appView, parameterStateToAdd, onChangedAction);
+          oldParameterState.mutableJoin(
+              appView, parameterStateToAdd, parameterType, onChangedAction);
       if (newParameterState != oldParameterState) {
         setState(newParameterState);
         onChangedAction.execute();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
index 9e696ad..434bc17 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
@@ -183,7 +183,8 @@
                     .asClassType());
           }
           return null;
-        });
+        },
+        resolvedMethod.getMethodSignature());
   }
 
   private boolean verifyAllInterfacesFinished(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
index 43a5693..6df20f0 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
@@ -9,6 +9,7 @@
 import static com.android.tools.r8.utils.MapUtils.ignoreKey;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
@@ -140,8 +141,11 @@
     private MethodState computeMethodStateForPolymorhicMethod(ProgramMethod method) {
       assert method.getDefinition().isNonPrivateVirtualMethod();
       MethodState methodState = active.get(method).mutableCopy();
-      for (MethodStateCollectionBySignature methodStates : activeUntilLowerBound.values()) {
-        methodState = methodState.mutableJoin(appView, methodStates.get(method));
+      if (!activeUntilLowerBound.isEmpty()) {
+        DexMethodSignature methodSignature = method.getMethodSignature();
+        for (MethodStateCollectionBySignature methodStates : activeUntilLowerBound.values()) {
+          methodState = methodState.mutableJoin(appView, methodSignature, methodStates.get(method));
+        }
       }
       return methodState;
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/WideningUtils.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/WideningUtils.java
new file mode 100644
index 0000000..f30aa8e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/WideningUtils.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.utils;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class WideningUtils {
+
+  public static DynamicType widenDynamicReceiverType(
+      AppView<AppInfoWithLiveness> appView,
+      ProgramMethod resolvedMethod,
+      DynamicType dynamicReceiverType) {
+    return shouldWidenDynamicType(
+            appView,
+            dynamicReceiverType,
+            resolvedMethod.getHolderType(),
+            Nullability.definitelyNotNull())
+        ? DynamicType.unknown()
+        : dynamicReceiverType;
+  }
+
+  public static DynamicType widenDynamicNonReceiverType(
+      AppView<AppInfoWithLiveness> appView, DynamicType dynamicType, DexType staticType) {
+    return widenDynamicNonReceiverType(appView, dynamicType, staticType, Nullability.maybeNull());
+  }
+
+  public static DynamicType widenDynamicNonReceiverType(
+      AppView<AppInfoWithLiveness> appView,
+      DynamicType dynamicType,
+      DexType staticType,
+      Nullability staticNullability) {
+    return shouldWidenDynamicType(appView, dynamicType, staticType, staticNullability)
+        ? DynamicType.unknown()
+        : dynamicType;
+  }
+
+  private static boolean shouldWidenDynamicType(
+      AppView<AppInfoWithLiveness> appView,
+      DynamicType dynamicType,
+      DexType staticType,
+      Nullability staticNullability) {
+    assert staticType.isClassType();
+    if (dynamicType.isUnknown()) {
+      return true;
+    }
+    if (dynamicType.isBottom()
+        || dynamicType.isNullType()
+        || dynamicType.getNullability().strictlyLessThan(staticNullability)) {
+      return false;
+    }
+    ClassTypeElement staticTypeElement =
+        staticType.toTypeElement(appView).asClassType().getOrCreateVariant(staticNullability);
+    if (!dynamicType.getDynamicUpperBoundType().equals(staticTypeElement)) {
+      return false;
+    }
+    if (!dynamicType.hasDynamicLowerBoundType()) {
+      return true;
+    }
+
+    DexClass staticTypeClass = appView.definitionFor(staticType);
+    if (staticTypeClass == null || !staticTypeClass.isProgramClass()) {
+      // TODO(b/190154391): If this is a library class with no program subtypes, then we might as
+      //  well widen to 'unknown'.
+      return false;
+    }
+
+    // If the static type does not have any program subtypes, then widen the dynamic type to
+    // unknown.
+    //
+    // Note that if the static type is pinned, it could have subtypes outside the set of program
+    // classes, but in this case it is still unlikely that we can use the dynamic lower bound type
+    // information for anything, so we intentionally also widen to 'unknown' in this case.
+    ObjectAllocationInfoCollection objectAllocationInfoCollection =
+        appView.appInfo().getObjectAllocationInfoCollection();
+    return !objectAllocationInfoCollection.hasInstantiatedStrictSubtype(
+        staticTypeClass.asProgramClass());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterWithUnknownArgumentInformationWidenedToUnknownTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterWithUnknownArgumentInformationWidenedToUnknownTest.java
index 2b87e0b..ade398c 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterWithUnknownArgumentInformationWidenedToUnknownTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterWithUnknownArgumentInformationWidenedToUnknownTest.java
@@ -6,7 +6,6 @@
 
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -30,8 +29,7 @@
     return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
-  // TODO(b/190154391): The state for Main.test() should be unknown.
-  @Test(expected = CompilationFailedException.class)
+  @Test
   public void test() throws Exception {
     BooleanBox inspected = new BooleanBox();
     testForR8(parameters.getBackend())