Account for indirect definitely unsafe casts in class inliner

Change-Id: Ic02b6216f933f6fa7320d20bbea98adb9cb9e8c0
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index e6494f3..514eae8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -1046,12 +1046,12 @@
     int parameter = 0;
     if (root.isNewInstance()) {
       return classInlinerMethodConstraint.isEligibleForNewInstanceClassInlining(
-          singleTarget, parameter);
+          appView, eligibleClass, singleTarget, parameter);
     }
 
     assert root.isStaticGet();
     return classInlinerMethodConstraint.isEligibleForStaticGetClassInlining(
-        appView, parameter, objectState, method);
+        appView, eligibleClass, parameter, objectState, method);
   }
 
   // Analyzes if a method invoke the eligible instance is passed to is eligible. In short,
@@ -1155,13 +1155,13 @@
         singleTarget.getDefinition().getOptimizationInfo().getClassInlinerMethodConstraint();
     if (root.isNewInstance()) {
       if (!classInlinerMethodConstraint.isEligibleForNewInstanceClassInlining(
-          singleTarget, parameter)) {
+          appView, eligibleClass, singleTarget, parameter)) {
         return false;
       }
     } else {
       assert root.isStaticGet();
       if (!classInlinerMethodConstraint.isEligibleForStaticGetClassInlining(
-          appView, parameter, objectState, method)) {
+          appView, eligibleClass, parameter, objectState, method)) {
         return false;
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java
index 930edc5..5d7f1e3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.optimize.classinliner.analysis;
 
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 
 class BottomParameterUsage extends ParameterUsage {
@@ -18,6 +19,11 @@
   }
 
   @Override
+  ParameterUsage addCastWithParameter(DexType castType) {
+    return InternalNonEmptyParameterUsage.builder().addCastWithParameter(castType).build();
+  }
+
+  @Override
   ParameterUsage addFieldReadFromParameter(DexField field) {
     return InternalNonEmptyParameterUsage.builder().addFieldReadFromParameter(field).build();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java
index 4bba181..29274b2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.google.common.collect.ImmutableMultiset;
@@ -23,6 +24,7 @@
  */
 public class InternalNonEmptyParameterUsage extends ParameterUsage {
 
+  private Set<DexType> castsWithParameter;
   private Set<DexField> fieldsReadFromParameter;
   private Set<InvokeMethodWithReceiver> methodCallsWithParameterAsReceiver;
 
@@ -31,16 +33,19 @@
   private boolean isParameterUsedAsLock;
 
   InternalNonEmptyParameterUsage(
+      Set<DexType> castsWithParameter,
       Set<DexField> fieldsReadFromParameter,
       Set<InvokeMethodWithReceiver> methodCallsWithParameterAsReceiver,
       boolean isParameterMutated,
       boolean isParameterReturned,
       boolean isParameterUsedAsLock) {
-    assert !fieldsReadFromParameter.isEmpty()
+    assert !castsWithParameter.isEmpty()
+        || !fieldsReadFromParameter.isEmpty()
         || !methodCallsWithParameterAsReceiver.isEmpty()
         || isParameterMutated
         || isParameterReturned
         || isParameterUsedAsLock;
+    this.castsWithParameter = castsWithParameter;
     this.fieldsReadFromParameter = fieldsReadFromParameter;
     this.methodCallsWithParameterAsReceiver = methodCallsWithParameterAsReceiver;
     this.isParameterMutated = isParameterMutated;
@@ -57,11 +62,26 @@
   }
 
   @Override
+  ParameterUsage addCastWithParameter(DexType castType) {
+    ImmutableSet.Builder<DexType> newCastsWithParameter = ImmutableSet.builder();
+    newCastsWithParameter.addAll(castsWithParameter);
+    newCastsWithParameter.add(castType);
+    return new InternalNonEmptyParameterUsage(
+        newCastsWithParameter.build(),
+        fieldsReadFromParameter,
+        methodCallsWithParameterAsReceiver,
+        isParameterMutated,
+        isParameterReturned,
+        isParameterUsedAsLock);
+  }
+
+  @Override
   InternalNonEmptyParameterUsage addFieldReadFromParameter(DexField field) {
     ImmutableSet.Builder<DexField> newFieldsReadFromParameterBuilder = ImmutableSet.builder();
     newFieldsReadFromParameterBuilder.addAll(fieldsReadFromParameter);
     newFieldsReadFromParameterBuilder.add(field);
     return new InternalNonEmptyParameterUsage(
+        castsWithParameter,
         newFieldsReadFromParameterBuilder.build(),
         methodCallsWithParameterAsReceiver,
         isParameterMutated,
@@ -77,6 +97,7 @@
     newMethodCallsWithParameterAsReceiverBuilder.addAll(methodCallsWithParameterAsReceiver);
     newMethodCallsWithParameterAsReceiverBuilder.add(invoke);
     return new InternalNonEmptyParameterUsage(
+        castsWithParameter,
         fieldsReadFromParameter,
         newMethodCallsWithParameterAsReceiverBuilder.build(),
         isParameterMutated,
@@ -96,6 +117,7 @@
     methodCallsWithParameterAsReceiver.forEach(
         invoke -> methodCallsWithParameterAsReceiverBuilder.add(invoke.getInvokedMethod()));
     return new NonEmptyParameterUsage(
+        castsWithParameter,
         fieldsReadFromParameter,
         methodCallsWithParameterAsReceiverBuilder.build(),
         isParameterMutated,
@@ -120,6 +142,7 @@
 
   InternalNonEmptyParameterUsage join(InternalNonEmptyParameterUsage other) {
     return builderFromInstance()
+        .addCastsWithParameter(other.castsWithParameter)
         .addFieldsReadFromParameter(other.fieldsReadFromParameter)
         .addMethodCallsWithParameterAsReceiver(other.methodCallsWithParameterAsReceiver)
         .joinIsReceiverMutated(other.isParameterMutated)
@@ -134,6 +157,7 @@
       return this;
     }
     return new InternalNonEmptyParameterUsage(
+        castsWithParameter,
         fieldsReadFromParameter,
         methodCallsWithParameterAsReceiver,
         true,
@@ -147,6 +171,7 @@
       return this;
     }
     return new InternalNonEmptyParameterUsage(
+        castsWithParameter,
         fieldsReadFromParameter,
         methodCallsWithParameterAsReceiver,
         isParameterMutated,
@@ -160,6 +185,7 @@
       return this;
     }
     return new InternalNonEmptyParameterUsage(
+        castsWithParameter,
         fieldsReadFromParameter,
         methodCallsWithParameterAsReceiver,
         isParameterMutated,
@@ -169,6 +195,9 @@
 
   @Override
   public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    }
     if (obj == null || obj.getClass() != getClass()) {
       return false;
     }
@@ -176,6 +205,7 @@
     return isParameterMutated == knownParameterUsage.isParameterMutated
         && isParameterReturned == knownParameterUsage.isParameterReturned
         && isParameterUsedAsLock == knownParameterUsage.isParameterUsedAsLock
+        && castsWithParameter.equals(knownParameterUsage.castsWithParameter)
         && fieldsReadFromParameter.equals(knownParameterUsage.fieldsReadFromParameter)
         && methodCallsWithParameterAsReceiver.equals(
             knownParameterUsage.methodCallsWithParameterAsReceiver);
@@ -184,9 +214,11 @@
   @Override
   public int hashCode() {
     int hash =
-        31 * (31 + fieldsReadFromParameter.hashCode())
+        31 * (31 * (31 + castsWithParameter.hashCode()) + fieldsReadFromParameter.hashCode())
             + methodCallsWithParameterAsReceiver.hashCode();
-    assert hash == Objects.hash(fieldsReadFromParameter, methodCallsWithParameterAsReceiver);
+    assert hash
+        == Objects.hash(
+            castsWithParameter, fieldsReadFromParameter, methodCallsWithParameterAsReceiver);
     hash = (hash << 1) | BooleanUtils.intValue(isParameterMutated);
     hash = (hash << 1) | BooleanUtils.intValue(isParameterReturned);
     hash = (hash << 1) | BooleanUtils.intValue(isParameterUsedAsLock);
@@ -195,6 +227,7 @@
 
   static class Builder {
 
+    private ImmutableSet.Builder<DexType> castsWithParameterBuilder;
     private ImmutableSet.Builder<DexField> fieldsReadFromParameterBuilder;
     private ImmutableSet.Builder<InvokeMethodWithReceiver>
         methodCallsWithParameterAsReceiverBuilder;
@@ -203,11 +236,14 @@
     private boolean isParameterUsedAsLock;
 
     Builder() {
+      castsWithParameterBuilder = ImmutableSet.builder();
       fieldsReadFromParameterBuilder = ImmutableSet.builder();
       methodCallsWithParameterAsReceiverBuilder = ImmutableSet.builder();
     }
 
     Builder(InternalNonEmptyParameterUsage methodBehavior) {
+      castsWithParameterBuilder =
+          ImmutableSet.<DexType>builder().addAll(methodBehavior.castsWithParameter);
       fieldsReadFromParameterBuilder =
           ImmutableSet.<DexField>builder().addAll(methodBehavior.fieldsReadFromParameter);
       methodCallsWithParameterAsReceiverBuilder =
@@ -218,6 +254,16 @@
       isParameterUsedAsLock = methodBehavior.isParameterUsedAsLock;
     }
 
+    Builder addCastWithParameter(DexType castType) {
+      castsWithParameterBuilder.add(castType);
+      return this;
+    }
+
+    Builder addCastsWithParameter(Collection<DexType> castTypes) {
+      castsWithParameterBuilder.addAll(castTypes);
+      return this;
+    }
+
     Builder addFieldReadFromParameter(DexField fieldReadFromParameter) {
       fieldsReadFromParameterBuilder.add(fieldReadFromParameter);
       return this;
@@ -272,6 +318,7 @@
 
     InternalNonEmptyParameterUsage build() {
       return new InternalNonEmptyParameterUsage(
+          castsWithParameterBuilder.build(),
           fieldsReadFromParameterBuilder.build(),
           methodCallsWithParameterAsReceiverBuilder.build(),
           isParameterMutated,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java
index d377305..38056c8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.google.common.collect.Multiset;
@@ -15,6 +16,7 @@
 
 public class NonEmptyParameterUsage extends ParameterUsage {
 
+  private Set<DexType> castsWithParameter;
   private Set<DexField> fieldsReadFromParameter;
   private Multiset<DexMethod> methodCallsWithParameterAsReceiver;
 
@@ -23,16 +25,19 @@
   private boolean isParameterUsedAsLock;
 
   NonEmptyParameterUsage(
+      Set<DexType> castsWithParameter,
       Set<DexField> fieldsReadFromParameter,
       Multiset<DexMethod> methodCallsWithParameterAsReceiver,
       boolean isParameterMutated,
       boolean isParameterReturned,
       boolean isParameterUsedAsLock) {
-    assert !fieldsReadFromParameter.isEmpty()
+    assert !castsWithParameter.isEmpty()
+        || !fieldsReadFromParameter.isEmpty()
         || !methodCallsWithParameterAsReceiver.isEmpty()
         || isParameterMutated
         || isParameterReturned
         || isParameterUsedAsLock;
+    this.castsWithParameter = castsWithParameter;
     this.fieldsReadFromParameter = fieldsReadFromParameter;
     this.methodCallsWithParameterAsReceiver = methodCallsWithParameterAsReceiver;
     this.isParameterMutated = isParameterMutated;
@@ -41,6 +46,11 @@
   }
 
   @Override
+  ParameterUsage addCastWithParameter(DexType castType) {
+    throw new Unreachable();
+  }
+
+  @Override
   ParameterUsage addFieldReadFromParameter(DexField field) {
     throw new Unreachable();
   }
@@ -64,6 +74,10 @@
     return !getFieldsReadFromParameter().isEmpty();
   }
 
+  public Set<DexType> getCastsWithParameter() {
+    return castsWithParameter;
+  }
+
   public Set<DexField> getFieldsReadFromParameter() {
     return fieldsReadFromParameter;
   }
@@ -104,6 +118,9 @@
 
   @Override
   public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    }
     if (obj == null || obj.getClass() != getClass()) {
       return false;
     }
@@ -111,6 +128,7 @@
     return isParameterMutated == knownParameterUsage.isParameterMutated
         && isParameterReturned == knownParameterUsage.isParameterReturned
         && isParameterUsedAsLock == knownParameterUsage.isParameterUsedAsLock
+        && castsWithParameter.equals(knownParameterUsage.castsWithParameter)
         && fieldsReadFromParameter.equals(knownParameterUsage.fieldsReadFromParameter)
         && methodCallsWithParameterAsReceiver.equals(
             knownParameterUsage.methodCallsWithParameterAsReceiver);
@@ -119,9 +137,11 @@
   @Override
   public int hashCode() {
     int hash =
-        31 * (31 + fieldsReadFromParameter.hashCode())
+        31 * (31 * (31 + castsWithParameter.hashCode()) + fieldsReadFromParameter.hashCode())
             + methodCallsWithParameterAsReceiver.hashCode();
-    assert hash == Objects.hash(fieldsReadFromParameter, methodCallsWithParameterAsReceiver);
+    assert hash
+        == Objects.hash(
+            castsWithParameter, fieldsReadFromParameter, methodCallsWithParameterAsReceiver);
     hash = (hash << 1) | BooleanUtils.intValue(isParameterMutated);
     hash = (hash << 1) | BooleanUtils.intValue(isParameterReturned);
     hash = (hash << 1) | BooleanUtils.intValue(isParameterUsedAsLock);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java
index e2ad8da..2822d6f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java
@@ -5,10 +5,13 @@
 package com.android.tools.r8.ir.optimize.classinliner.analysis;
 
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 
 public abstract class ParameterUsage {
 
+  abstract ParameterUsage addCastWithParameter(DexType castType);
+
   abstract ParameterUsage addFieldReadFromParameter(DexField field);
 
   abstract ParameterUsage addMethodCallWithParameterAsReceiver(InvokeMethodWithReceiver invoke);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
index 152c212..4d45a08 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
@@ -171,7 +171,11 @@
 
   private ParameterUsages analyzeCheckCast(CheckCast checkCast, NonEmptyParameterUsages state) {
     // Mark the value as ineligible for class inlining if it has phi users.
-    return checkCast.outValue().hasPhiUsers() ? fail(checkCast, state) : state;
+    if (checkCast.outValue().hasPhiUsers()) {
+      return fail(checkCast, state);
+    }
+    return state.rebuildParameter(
+        checkCast.object(), (context, usage) -> usage.addCastWithParameter(checkCast.getType()));
   }
 
   private ParameterUsages analyzeIf(If theIf, NonEmptyParameterUsages state) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java
index 93577ad..bf49e63 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.optimize.classinliner.analysis;
 
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 
 class UnknownParameterUsage extends ParameterUsage {
@@ -18,6 +19,11 @@
   }
 
   @Override
+  ParameterUsage addCastWithParameter(DexType castType) {
+    return this;
+  }
+
+  @Override
   UnknownParameterUsage addFieldReadFromParameter(DexField field) {
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
index 1826502..f46ae29 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.optimize.classinliner.constraint;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
@@ -34,13 +35,18 @@
   }
 
   @Override
-  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter) {
+  public boolean isEligibleForNewInstanceClassInlining(
+      AppView<AppInfoWithLiveness> appView,
+      DexProgramClass candidateClass,
+      ProgramMethod method,
+      int parameter) {
     return false;
   }
 
   @Override
   public boolean isEligibleForStaticGetClassInlining(
       AppView<AppInfoWithLiveness> appView,
+      DexProgramClass candidateClass,
       int parameter,
       ObjectState objectState,
       ProgramMethod context) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
index 1f33962..8efec57 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.optimize.classinliner.constraint;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
@@ -34,13 +35,18 @@
   }
 
   @Override
-  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter) {
+  public boolean isEligibleForNewInstanceClassInlining(
+      AppView<AppInfoWithLiveness> appView,
+      DexProgramClass candidateClass,
+      ProgramMethod method,
+      int parameter) {
     return true;
   }
 
   @Override
   public boolean isEligibleForStaticGetClassInlining(
       AppView<AppInfoWithLiveness> appView,
+      DexProgramClass candidateClass,
       int parameter,
       ObjectState objectState,
       ProgramMethod context) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
index 5bc6317..994cdaa 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.optimize.classinliner.constraint;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
@@ -18,10 +19,15 @@
 
   ParameterUsage getParameterUsage(int parameter);
 
-  boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter);
+  boolean isEligibleForNewInstanceClassInlining(
+      AppView<AppInfoWithLiveness> appView,
+      DexProgramClass candidateClass,
+      ProgramMethod method,
+      int parameter);
 
   boolean isEligibleForStaticGetClassInlining(
       AppView<AppInfoWithLiveness> appView,
+      DexProgramClass candidateClass,
       int parameter,
       ObjectState objectState,
       ProgramMethod context);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
index eaae1fe..bd524a5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
@@ -8,6 +8,8 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexField;
+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.graph.proto.ArgumentInfo;
 import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
@@ -68,15 +70,30 @@
   }
 
   @Override
-  public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter) {
+  public boolean isEligibleForNewInstanceClassInlining(
+      AppView<AppInfoWithLiveness> appView,
+      DexProgramClass candidateClass,
+      ProgramMethod method,
+      int parameter) {
     AnalysisContext defaultContext = AnalysisContext.getDefaultContext();
     ParameterUsage usage = usages.get(parameter).get(defaultContext);
-    return !usage.isTop();
+    if (usage.isBottom()) {
+      return true;
+    }
+    if (usage.isTop()) {
+      return false;
+    }
+    NonEmptyParameterUsage knownUsage = usage.asNonEmpty();
+    if (hasUnsafeCast(appView, candidateClass, knownUsage)) {
+      return false;
+    }
+    return true;
   }
 
   @Override
   public boolean isEligibleForStaticGetClassInlining(
       AppView<AppInfoWithLiveness> appView,
+      DexProgramClass candidateClass,
       int parameter,
       ObjectState objectState,
       ProgramMethod context) {
@@ -100,6 +117,9 @@
       // We will not be able to remove the monitor instruction afterwards.
       return false;
     }
+    if (hasUnsafeCast(appView, candidateClass, knownUsage)) {
+      return false;
+    }
     for (DexField fieldReadFromParameter : knownUsage.getFieldsReadFromParameter()) {
       DexClass holder = appView.definitionFor(fieldReadFromParameter.getHolderType());
       DexEncodedField definition = fieldReadFromParameter.lookupOnClass(holder);
@@ -117,4 +137,20 @@
     }
     return true;
   }
+
+  private boolean hasUnsafeCast(
+      AppView<AppInfoWithLiveness> appView,
+      DexProgramClass candidateClass,
+      NonEmptyParameterUsage knownUsage) {
+    for (DexType castType : knownUsage.getCastsWithParameter()) {
+      if (!castType.isClassType()) {
+        return true;
+      }
+      DexClass castClass = appView.definitionFor(castType);
+      if (castClass == null || !appView.appInfo().isSubtype(candidateClass, castClass)) {
+        return true;
+      }
+    }
+    return false;
+  }
 }