Compute join of reference types in cf analysis

Change-Id: I00fea3ec7a7cb5d349184e012389f99c01ab4921
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/FrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/FrameType.java
index f614d65..35fc8e5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/FrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/FrameType.java
@@ -52,6 +52,11 @@
     if (type.isPrimitiveType()) {
       return primitive(type);
     }
+    return initializedReference(type);
+  }
+
+  static InitializedReferenceFrameType initializedReference(DexType type) {
+    assert type.isReferenceType();
     return new InitializedReferenceFrameType(type);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/InitializedReferenceFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedReferenceFrameType.java
index fd00888..27ada3e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/InitializedReferenceFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/InitializedReferenceFrameType.java
@@ -4,10 +4,12 @@
 
 package com.android.tools.r8.cf.code.frame;
 
-import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeUtils;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.naming.NamingLens;
 import org.objectweb.asm.Opcodes;
@@ -44,7 +46,8 @@
   }
 
   @Override
-  public SingleFrameType join(SingleFrameType frameType) {
+  public SingleFrameType join(
+      AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
     if (equals(frameType)) {
       return this;
     }
@@ -62,8 +65,10 @@
     }
     assert type.isArrayType() || type.isClassType();
     assert otherType.isArrayType() || otherType.isClassType();
-    // TODO(b/214496607): Implement join of different reference types using class hierarchy.
-    throw new Unimplemented();
+    DexType joinType =
+        DexTypeUtils.toDexType(
+            appView, type.toTypeElement(appView).join(otherType.toTypeElement(appView), appView));
+    return FrameType.initializedReference(joinType);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/OneWord.java b/src/main/java/com/android/tools/r8/cf/code/frame/OneWord.java
index 9ac5082..06add68 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/OneWord.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/OneWord.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.cf.code.frame;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.naming.NamingLens;
 import org.objectweb.asm.Opcodes;
@@ -25,7 +27,8 @@
   }
 
   @Override
-  public SingleFrameType join(SingleFrameType frameType) {
+  public SingleFrameType join(
+      AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
     return this;
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/SingleFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/SingleFrameType.java
index 657d493..39c6e70 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/SingleFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/SingleFrameType.java
@@ -4,8 +4,11 @@
 
 package com.android.tools.r8.cf.code.frame;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
 
 public interface SingleFrameType extends FrameType {
 
-  SingleFrameType join(SingleFrameType frameType);
+  SingleFrameType join(
+      AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType);
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/SinglePrimitiveFrameType.java b/src/main/java/com/android/tools/r8/cf/code/frame/SinglePrimitiveFrameType.java
index 69ba4b6..0739869 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/SinglePrimitiveFrameType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/SinglePrimitiveFrameType.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.cf.code.frame;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+
 public abstract class SinglePrimitiveFrameType extends SingletonFrameType
     implements PrimitiveFrameType, SingleFrameType {
 
@@ -47,7 +50,8 @@
   }
 
   @Override
-  public final SingleFrameType join(SingleFrameType frameType) {
+  public final SingleFrameType join(
+      AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
     if (this == frameType) {
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedNew.java b/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedNew.java
index 03eff42..bfaccfb 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedNew.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedNew.java
@@ -5,6 +5,8 @@
 package com.android.tools.r8.cf.code.frame;
 
 import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.naming.NamingLens;
@@ -51,7 +53,8 @@
   }
 
   @Override
-  public SingleFrameType join(SingleFrameType frameType) {
+  public SingleFrameType join(
+      AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
     return equals(frameType) ? this : FrameType.oneWord();
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedThis.java b/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedThis.java
index 69d1ecb..5b6730d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedThis.java
+++ b/src/main/java/com/android/tools/r8/cf/code/frame/UninitializedThis.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.cf.code.frame;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.naming.NamingLens;
@@ -36,7 +38,8 @@
   }
 
   @Override
-  public SingleFrameType join(SingleFrameType frameType) {
+  public SingleFrameType join(
+      AppView<? extends AppInfoWithClassHierarchy> appView, SingleFrameType frameType) {
     if (this == frameType) {
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java b/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
index bff5528..62531ba 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
@@ -18,7 +18,7 @@
     return toDexType(appView, join);
   }
 
-  private static DexType toDexType(
+  public static DexType toDexType(
       AppView<? extends AppInfoWithClassHierarchy> appView, TypeElement type) {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     if (type.isPrimitiveType()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractState.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractState.java
index 914cc9e..f940edf 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/AbstractState.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.analysis.framework.intraprocedural;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.code.BasicBlock;
 
 /** The abstract state of the dataflow analysis, which is computed for each {@link BasicBlock}. */
@@ -16,10 +17,10 @@
     return asAbstractState();
   }
 
-  public abstract StateType join(StateType state);
+  public abstract StateType join(AppView<?> appView, StateType state);
 
-  public boolean isGreaterThanOrEquals(StateType state) {
-    StateType leastUpperBound = join(state);
+  public boolean isGreaterThanOrEquals(AppView<?> appView, StateType state) {
+    StateType leastUpperBound = join(appView, state);
     return equals(leastUpperBound);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
index 8974bbf..73eeb55 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/DataflowAnalysisResult.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.analysis.framework.intraprocedural;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.code.BasicBlock;
 import java.util.Map;
 
@@ -43,10 +44,10 @@
       this.blockExitStates = blockExitStates;
     }
 
-    public StateType join() {
+    public StateType join(AppView<?> appView) {
       StateType result = null;
       for (StateType blockExitState : blockExitStates.values()) {
-        result = result != null ? result.join(blockExitState) : blockExitState;
+        result = result != null ? result.join(appView, blockExitState) : blockExitState;
       }
       return result;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisBase.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisBase.java
index d7dbeb3..3a8b75c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisBase.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraProceduralDataflowAnalysisBase.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.analysis.framework.intraprocedural;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult.FailedDataflowAnalysisResult;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.DataflowAnalysisResult.SuccessfulDataflowAnalysisResult;
 import com.android.tools.r8.utils.Timing;
@@ -27,6 +28,8 @@
 public class IntraProceduralDataflowAnalysisBase<
     Block, Instruction extends AbstractInstruction, StateType extends AbstractState<StateType>> {
 
+  final AppView<?> appView;
+
   final StateType bottom;
 
   final ControlFlowGraph<Block, Instruction> cfg;
@@ -48,10 +51,12 @@
   final IntraProceduralDataflowAnalysisOptions options;
 
   public IntraProceduralDataflowAnalysisBase(
+      AppView<?> appView,
       StateType bottom,
       ControlFlowGraph<Block, Instruction> cfg,
       AbstractTransferFunction<Block, Instruction, StateType> transfer,
       IntraProceduralDataflowAnalysisOptions options) {
+    this.appView = appView;
     this.bottom = bottom;
     this.cfg = cfg;
     this.transfer = transfer;
@@ -137,7 +142,7 @@
     if (block == cfg.getEntryBlock()) {
       return transfer
           .computeInitialState(block, bottom)
-          .join(computeBlockEntryStateForNormalBlock(block));
+          .join(appView, computeBlockEntryStateForNormalBlock(block));
     }
     if (cfg.hasExceptionalPredecessors(block)) {
       return exceptionalBlockEntryStates.getOrDefault(block, bottom).clone();
@@ -162,7 +167,7 @@
                       block,
                       predecessor,
                       blockExitStates.getOrDefault(predecessor, bottom).clone());
-              return TraversalContinuation.doContinue(entryState.join(edgeState));
+              return TraversalContinuation.doContinue(entryState.join(appView, edgeState));
             },
             bottom);
     return traversalContinuation.asContinue().getValue().clone();
@@ -171,7 +176,7 @@
   boolean setBlockExitState(Block block, StateType state) {
     assert !isBlockWithIntermediateSuccessorBlock(block);
     StateType previous = blockExitStates.put(block, state);
-    assert previous == null || state.isGreaterThanOrEquals(previous);
+    assert previous == null || state.isGreaterThanOrEquals(appView, previous);
     return !state.equals(previous);
   }
 
@@ -202,7 +207,7 @@
   private void updateBlockEntryStateForBlock(
       Block block, StateType edgeState, Map<Block, StateType> states) {
     StateType previous = states.getOrDefault(block, bottom);
-    states.put(block, previous.join(edgeState));
+    states.put(block, previous.join(appView, edgeState));
   }
 
   public boolean isIntermediateBlock(Block block) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java
index 9f3ea94..5e2a7ec 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/IntraproceduralDataflowAnalysis.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.analysis.framework.intraprocedural;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
@@ -12,10 +13,16 @@
     extends IntraProceduralDataflowAnalysisBase<BasicBlock, Instruction, StateType> {
 
   public IntraproceduralDataflowAnalysis(
+      AppView<?> appView,
       StateType bottom,
       IRCode code,
       AbstractTransferFunction<BasicBlock, Instruction, StateType> transfer) {
-    super(bottom, code, transfer, IntraProceduralDataflowAnalysisOptions.getCollapseInstance());
+    super(
+        appView,
+        bottom,
+        code,
+        transfer,
+        IntraProceduralDataflowAnalysisOptions.getCollapseInstance());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfIntraproceduralDataflowAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfIntraproceduralDataflowAnalysis.java
index 99205b5..cef6a5d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfIntraproceduralDataflowAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfIntraproceduralDataflowAnalysis.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.ir.analysis.framework.intraprocedural.cf;
 
 import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.IntraProceduralDataflowAnalysisBase;
@@ -14,9 +15,15 @@
     extends IntraProceduralDataflowAnalysisBase<CfBlock, CfInstruction, StateType> {
 
   public CfIntraproceduralDataflowAnalysis(
+      AppView<?> appView,
       StateType bottom,
       CfControlFlowGraph cfg,
       AbstractTransferFunction<CfBlock, CfInstruction, StateType> transfer) {
-    super(bottom, cfg, transfer, IntraProceduralDataflowAnalysisOptions.getCollapseInstance());
+    super(
+        appView,
+        bottom,
+        cfg,
+        transfer,
+        IntraProceduralDataflowAnalysisOptions.getCollapseInstance());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
index f45344f..8842317 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
@@ -26,7 +26,7 @@
     // Analyze code.
     IntraproceduralDataflowAnalysis<ParameterUsages> analysis =
         new IntraproceduralDataflowAnalysis<>(
-            ParameterUsages.bottom(), code, new TransferFunction(appView, method, code));
+            appView, ParameterUsages.bottom(), code, new TransferFunction(appView, method, code));
     SuccessfulDataflowAnalysisResult<?, ParameterUsages> result =
         timing.time(
             "Data flow analysis",
@@ -34,7 +34,7 @@
     if (result == null) {
       return ClassInlinerMethodConstraint.alwaysFalse();
     }
-    ParameterUsages usages = timing.time("Externalize", () -> result.join().externalize());
+    ParameterUsages usages = timing.time("Externalize", () -> result.join(appView).externalize());
     if (usages.isBottom()) {
       return ClassInlinerMethodConstraint.alwaysTrue();
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsages.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsages.java
index ac81581..f4a45a8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsages.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsages.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.optimize.classinliner.analysis;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState;
 import com.android.tools.r8.utils.IntObjToObjFunction;
 
@@ -42,7 +43,7 @@
   }
 
   @Override
-  public ParameterUsages join(ParameterUsages state) {
+  public ParameterUsages join(AppView<?> appView, ParameterUsages state) {
     if (isBottom()) {
       return state;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendFlowAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendFlowAnalysis.java
index 1179c6d..bb75dc2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendFlowAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendFlowAnalysis.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.optimize.string;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction;
@@ -40,10 +41,16 @@
    * loop.
    */
   static boolean hasAppendInstructionInLoop(
-      IRCode code, Value builder, StringBuilderOptimizationConfiguration configuration) {
+      AppView<?> appView,
+      IRCode code,
+      Value builder,
+      StringBuilderOptimizationConfiguration configuration) {
     IntraproceduralDataflowAnalysis<AbstractStateImpl> analysis =
         new IntraproceduralDataflowAnalysis<>(
-            AbstractStateImpl.bottom(), code, new TransferFunction(builder, configuration));
+            appView,
+            AbstractStateImpl.bottom(),
+            code,
+            new TransferFunction(builder, configuration));
     DataflowAnalysisResult result = analysis.run(builder.definition.getBlock());
     return result.isFailedAnalysisResult();
   }
@@ -86,7 +93,7 @@
     }
 
     @Override
-    public AbstractStateImpl join(AbstractStateImpl state) {
+    public AbstractStateImpl join(AppView<?> appView, AbstractStateImpl state) {
       if (liveAppendInstructions.isEmpty()) {
         return state;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderEscapeState.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderEscapeState.java
index 5d9c471..f1e7fb8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderEscapeState.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderEscapeState.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.utils.MapUtils;
@@ -98,7 +99,7 @@
   }
 
   @Override
-  public StringBuilderEscapeState join(StringBuilderEscapeState other) {
+  public StringBuilderEscapeState join(AppView<?> appView, StringBuilderEscapeState other) {
     if (this.isBottom()) {
       return other;
     } else if (other.isBottom()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
index 3ad14fb..957ccfe 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -722,7 +722,7 @@
         return null;
       }
       if (StringBuilderAppendFlowAnalysis.hasAppendInstructionInLoop(
-          code, builder, optimizationConfiguration)) {
+          appView, code, builder, optimizationConfiguration)) {
         return null;
       }
       return StringUtils.join("", contents);
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java
index dfa38f5..239cb8e 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfFrameState.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.cf.code.frame.FrameType;
 import com.android.tools.r8.cf.code.frame.PreciseFrameType;
 import com.android.tools.r8.cf.code.frame.UninitializedFrameType;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
@@ -72,11 +73,13 @@
   }
 
   @Override
-  public boolean isGreaterThanOrEquals(CfFrameState state) {
+  public boolean isGreaterThanOrEquals(AppView<?> appView, CfFrameState state) {
     if (this == state) {
       return true;
     }
-    CfFrameState leastUpperBound = join(state, UnaryOperator.identity());
+    assert appView.hasClassHierarchy();
+    CfFrameState leastUpperBound =
+        join(appView.withClassHierarchy(), state, UnaryOperator.identity());
     return equals(leastUpperBound);
   }
 
@@ -313,13 +316,18 @@
   }
 
   @Override
-  public final CfFrameState join(CfFrameState state) {
+  public final CfFrameState join(AppView<?> appView, CfFrameState state) {
+    assert appView.hasClassHierarchy();
     return join(
-        state, frameType -> frameType.isSingle() ? FrameType.oneWord() : FrameType.twoWord());
+        appView.withClassHierarchy(),
+        state,
+        frameType -> frameType.isSingle() ? FrameType.oneWord() : FrameType.twoWord());
   }
 
   public final CfFrameState join(
-      CfFrameState state, UnaryOperator<FrameType> joinWithMissingLocal) {
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      CfFrameState state,
+      UnaryOperator<FrameType> joinWithMissingLocal) {
     if (state.isBottom() || isError()) {
       return this;
     }
@@ -328,7 +336,7 @@
     }
     assert isConcrete();
     assert state.isConcrete();
-    return asConcrete().join(state.asConcrete(), joinWithMissingLocal);
+    return asConcrete().join(appView, state.asConcrete(), joinWithMissingLocal);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
index 049a279..b21c294 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/CfOpenClosedInterfacesAnalysis.java
@@ -48,7 +48,7 @@
     CfControlFlowGraph cfg = CfControlFlowGraph.create(cfCode, appView.options());
     CfIntraproceduralDataflowAnalysis<CfFrameState> analysis =
         new CfIntraproceduralDataflowAnalysis<>(
-            CfFrameState.bottom(), cfg, new TransferFunction(method));
+            appView, CfFrameState.bottom(), cfg, new TransferFunction(method));
     DataflowAnalysisResult result = analysis.run(cfg.getEntryBlock());
     // TODO(b/214496607): Determine open interfaces.
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
index f995cbc..90d3701 100644
--- a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/ConcreteCfFrameState.java
@@ -10,11 +10,13 @@
 import com.android.tools.r8.cf.code.CfAssignability;
 import com.android.tools.r8.cf.code.CfAssignability.AssignabilityResult;
 import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.Builder;
 import com.android.tools.r8.cf.code.frame.FrameType;
 import com.android.tools.r8.cf.code.frame.PreciseFrameType;
 import com.android.tools.r8.cf.code.frame.SingleFrameType;
 import com.android.tools.r8.cf.code.frame.UninitializedFrameType;
 import com.android.tools.r8.cf.code.frame.WideFrameType;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
@@ -309,10 +311,12 @@
   }
 
   public CfFrameState join(
-      ConcreteCfFrameState state, UnaryOperator<FrameType> joinWithMissingLocal) {
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ConcreteCfFrameState state,
+      UnaryOperator<FrameType> joinWithMissingLocal) {
     CfFrame.Builder builder = CfFrame.builder();
-    joinLocals(state.locals, builder, joinWithMissingLocal);
-    ErroneousCfFrameState error = joinStack(state.stack, builder);
+    joinLocals(appView, state.locals, builder, joinWithMissingLocal);
+    ErroneousCfFrameState error = joinStack(appView, state.stack, builder);
     if (error != null) {
       return error;
     }
@@ -321,8 +325,9 @@
   }
 
   private void joinLocals(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
       Int2ObjectSortedMap<FrameType> locals,
-      CfFrame.Builder builder,
+      Builder builder,
       UnaryOperator<FrameType> joinWithMissingLocal) {
     ObjectBidirectionalIterator<Entry<FrameType>> iterator =
         this.locals.int2ObjectEntrySet().iterator();
@@ -357,7 +362,7 @@
             builder);
       } else {
         joinLocalsWithSameIndex(
-            localIndex, frameType, otherFrameType, iterator, otherIterator, builder);
+            localIndex, frameType, otherFrameType, iterator, otherIterator, appView, builder);
       }
     }
     joinLocalsOnlyPresentInOne(iterator, builder, joinWithMissingLocal);
@@ -399,11 +404,12 @@
       FrameType otherFrameType,
       ObjectBidirectionalIterator<Entry<FrameType>> iterator,
       ObjectBidirectionalIterator<Entry<FrameType>> otherIterator,
+      AppView<? extends AppInfoWithClassHierarchy> appView,
       CfFrame.Builder builder) {
     if (frameType.isSingle()) {
       if (otherFrameType.isSingle()) {
         joinSingleLocalsWithSameIndex(
-            localIndex, frameType.asSingle(), otherFrameType.asSingle(), builder);
+            localIndex, frameType.asSingle(), otherFrameType.asSingle(), appView, builder);
       } else {
         setWideLocalToTop(localIndex, builder);
         handleOverlappingLocals(localIndex + 1, iterator, otherIterator, builder);
@@ -423,8 +429,9 @@
       int localIndex,
       SingleFrameType frameType,
       SingleFrameType otherFrameType,
+      AppView<? extends AppInfoWithClassHierarchy> appView,
       CfFrame.Builder builder) {
-    builder.store(localIndex, frameType.join(otherFrameType));
+    builder.store(localIndex, frameType.join(appView, otherFrameType));
   }
 
   private void joinWideLocalsWithSameIndex(
@@ -543,7 +550,10 @@
     setSingleLocalToTop(localIndex + 1, builder);
   }
 
-  private ErroneousCfFrameState joinStack(Deque<PreciseFrameType> stack, CfFrame.Builder builder) {
+  private ErroneousCfFrameState joinStack(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      Deque<PreciseFrameType> stack,
+      CfFrame.Builder builder) {
     Iterator<PreciseFrameType> iterator = this.stack.iterator();
     Iterator<PreciseFrameType> otherIterator = stack.iterator();
     int stackIndex = 0;
@@ -561,7 +571,7 @@
       }
       PreciseFrameType preciseJoin;
       if (frameType.isSingle()) {
-        SingleFrameType join = frameType.asSingle().join(otherFrameType.asSingle());
+        SingleFrameType join = frameType.asSingle().join(appView, otherFrameType.asSingle());
         if (join.isOneWord()) {
           return joinStackImpreciseJoinError(stackIndex, frameType, otherFrameType);
         }