Fix Inliner with nest

- private methods accessed with invokeVirtual/interface
  should be inlined
- inlining should rewrite direct calls to virtual/
  interface calls
- corresponding tests

Bug:133608609
Change-Id: I84c3b8c01ae899cf81fd4b611e7f72d382ff732a
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index a3a329e..6e702a1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -48,6 +48,7 @@
 import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring.DexFieldWithAccess;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
+import com.android.tools.r8.ir.optimize.NestUtils;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
 import com.android.tools.r8.ir.synthetic.FieldAccessorSourceCode;
 import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
@@ -316,6 +317,10 @@
     return accessFlags.isSynthetic();
   }
 
+  public boolean isOnlyInlinedIntoNestMembers() {
+    return compilationState == PROCESSED_INLINING_CANDIDATE_SAME_NEST;
+  }
+
   public boolean isInliningCandidate(
       DexEncodedMethod container, Reason inliningReason, AppInfoWithSubtyping appInfo) {
     checkIfObsolete();
@@ -347,7 +352,7 @@
       case PROCESSED_INLINING_CANDIDATE_SAME_PACKAGE:
         return containerType.isSamePackage(method.holder);
       case PROCESSED_INLINING_CANDIDATE_SAME_NEST:
-        return ConstraintWithTarget.sameNest(containerType, method.holder, appInfo);
+        return NestUtils.sameNest(containerType, method.holder, appInfo);
       case PROCESSED_INLINING_CANDIDATE_SAME_CLASS:
         return containerType == method.holder;
       default:
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index ae432c6..d4529ea 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.Phi.RegisterReadType;
+import com.android.tools.r8.ir.optimize.NestUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.IteratorUtils;
 import com.google.common.collect.ImmutableList;
@@ -417,8 +418,14 @@
       Set<BasicBlock> blocksToRemove,
       DexType downcast) {
     assert blocksToRemove != null;
+    DexType codeHolder = code.method.method.holder;
+    DexType inlineeHolder = inlinee.method.method.holder;
+    if (codeHolder != inlineeHolder && inlinee.method.isOnlyInlinedIntoNestMembers()) {
+      // Should rewrite private calls to virtual calls.
+      assert NestUtils.sameNest(codeHolder, inlineeHolder, appView);
+      NestUtils.rewriteNestCallsForInlining(inlinee, codeHolder, appView);
+    }
     boolean inlineeCanThrow = canThrow(inlinee);
-
     // Split the block with the invocation into three blocks, where the first block contains all
     // instructions before the invocation, the second block contains only the invocation, and the
     // third block contains all instructions that follow the invocation.
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index a7d3edb..8f3a6a7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -45,6 +45,10 @@
         || result == null;
   }
 
+  public boolean isInterface() {
+    return itf;
+  }
+
   @Override
   public <T> T accept(InstructionVisitor<T> visitor) {
     return visitor.visit(this);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index 7c4b0aa..38cf994 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -88,7 +88,9 @@
       AppView<AppInfoWithLiveness> appView, DexType invocationContext) {
     DexType refinedReceiverType = TypeAnalysis.getRefinedReceiverType(appView, this);
     DexMethod method = getInvokedMethod();
-    return appView.appInfo().lookupSingleInterfaceTarget(method, refinedReceiverType);
+    return appView
+        .appInfo()
+        .lookupSingleInterfaceTarget(method, invocationContext, refinedReceiverType);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 2c1b5a6..eb0b6c7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -91,7 +91,9 @@
       AppView<AppInfoWithLiveness> appView, DexType invocationContext) {
     DexType refinedReceiverType = TypeAnalysis.getRefinedReceiverType(appView, this);
     DexMethod method = getInvokedMethod();
-    return appView.appInfo().lookupSingleVirtualTarget(method, refinedReceiverType);
+    return appView
+        .appInfo()
+        .lookupSingleVirtualTarget(method, invocationContext, refinedReceiverType);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 5d1ab42..0069dc6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassHierarchy;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexDefinitionSupplier;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
@@ -138,7 +137,7 @@
       return true;
     }
     if (flags.isPrivate()) {
-      return target == context;
+      return NestUtils.sameNest(target, context, appView);
     }
     if (flags.isProtected()) {
       return appView.appInfo().isSubtype(context, target) || target.isSamePackage(context);
@@ -300,24 +299,6 @@
           && this.targetHolder == o.targetHolder;
     }
 
-    public static boolean sameNest(
-        DexType contextHolder, DexType targetHolder, DexDefinitionSupplier definitions) {
-      if (contextHolder == targetHolder) {
-        return true;
-      }
-      DexClass contextHolderClass = definitions.definitionFor(contextHolder);
-      assert contextHolderClass != null;
-      if (!contextHolderClass.isInANest()) {
-        return false;
-      }
-      DexClass targetHolderClass = definitions.definitionFor(targetHolder);
-      if (targetHolderClass == null) {
-        // Conservatively return false
-        return false;
-      }
-      return contextHolderClass.getNestHost() == targetHolderClass.getNestHost();
-    }
-
     public static ConstraintWithTarget deriveConstraint(
         DexType contextHolder, DexType targetHolder, AccessFlags flags, AppView<?> appView) {
       if (flags.isPublic()) {
@@ -326,7 +307,7 @@
         DexClass contextHolderClass = appView.definitionFor(contextHolder);
         assert contextHolderClass != null;
         if (contextHolderClass.isInANest()) {
-          return sameNest(contextHolder, targetHolder, appView)
+          return NestUtils.sameNest(contextHolder, targetHolder, appView)
               ? new ConstraintWithTarget(Constraint.SAMENEST, targetHolder)
               : NEVER;
         }
@@ -389,7 +370,7 @@
           return NEVER;
         }
         if (other.constraint == Constraint.SAMENEST) {
-          if (sameNest(one.targetHolder, other.targetHolder, appView)) {
+          if (NestUtils.sameNest(one.targetHolder, other.targetHolder, appView)) {
             return one;
           }
           return NEVER;
@@ -410,7 +391,7 @@
       if (Constraint.SAMENEST.isSet(constraint)) {
         assert one.constraint == Constraint.SAMENEST;
         if (other.constraint == Constraint.SAMENEST) {
-          if (sameNest(one.targetHolder, other.targetHolder, appView)) {
+          if (NestUtils.sameNest(one.targetHolder, other.targetHolder, appView)) {
             return one;
           }
           return NEVER;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java b/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
new file mode 100644
index 0000000..bae9b27
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2019, 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.ir.optimize;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeInterface;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+
+public class NestUtils {
+
+  public static boolean sameNest(DexType type1, DexType type2, DexDefinitionSupplier definitions) {
+    if (type1 == type2) {
+      return true;
+    }
+    DexClass clazz1 = definitions.definitionFor(type1);
+    if (clazz1 == null) {
+      // Conservatively return false
+      return false;
+    }
+    if (!clazz1.isInANest()) {
+      return false;
+    }
+    DexClass clazz2 = definitions.definitionFor(type2);
+    if (clazz2 == null) {
+      // Conservatively return false
+      return false;
+    }
+    return clazz1.getNestHost() == clazz2.getNestHost();
+  }
+
+  public static void rewriteNestCallsForInlining(
+      IRCode code, DexType callerHolder, AppView<?> appView) {
+    // This method is called when inlining code into the nest member callerHolder.
+    InstructionIterator iterator = code.instructionIterator();
+    DexClass callerHolderClass = appView.definitionFor(callerHolder);
+    assert callerHolderClass != null;
+    assert code.method.method.holder != callerHolder;
+    while (iterator.hasNext()) {
+      Instruction instruction = iterator.next();
+      if (instruction.isInvokeDirect()) {
+        InvokeDirect invoke = instruction.asInvokeDirect();
+        DexMethod method = invoke.getInvokedMethod();
+        DexEncodedMethod encodedMethod = appView.definitionFor(method);
+        if (encodedMethod != null && !encodedMethod.isInstanceInitializer()) {
+          assert encodedMethod.isPrivateMethod();
+          // Call to private method which has now to be interface/virtual
+          // (Now call to nest member private method).
+          if (invoke.isInterface()) {
+            iterator.replaceCurrentInstruction(
+                new InvokeInterface(method, invoke.outValue(), invoke.inValues()));
+          } else {
+            iterator.replaceCurrentInstruction(
+                new InvokeVirtual(method, invoke.outValue(), invoke.inValues()));
+          }
+        }
+      } else if (instruction.isInvokeInterface() || instruction.isInvokeVirtual()) {
+        InvokeMethod invoke = instruction.asInvokeMethod();
+        DexMethod method = invoke.getInvokedMethod();
+        if (method.holder == callerHolder) {
+          DexEncodedMethod encodedMethod = appView.definitionFor(method);
+          if (encodedMethod != null && encodedMethod.isPrivateMethod()) {
+            // Interface/virtual nest member call to private method,
+            // which has now to be a direct call
+            // (Now call to same class private method).
+            iterator.replaceCurrentInstruction(
+                new InvokeDirect(
+                    method, invoke.outValue(), invoke.inValues(), callerHolderClass.isInterface()));
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 77ea68b..f48b72b 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.PresortedComparable;
 import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.optimize.NestUtils;
 import com.android.tools.r8.utils.CollectionUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -769,9 +770,9 @@
     }
     switch (type) {
       case VIRTUAL:
-        return lookupSingleVirtualTarget(target);
+        return lookupSingleVirtualTarget(target, invocationContext);
       case INTERFACE:
-        return lookupSingleInterfaceTarget(target);
+        return lookupSingleInterfaceTarget(target, invocationContext);
       case DIRECT:
         return lookupDirectTarget(target);
       case STATIC:
@@ -784,13 +785,18 @@
   }
 
   /** For mapping invoke virtual instruction to single target method. */
-  public DexEncodedMethod lookupSingleVirtualTarget(DexMethod method) {
+  public DexEncodedMethod lookupSingleVirtualTarget(DexMethod method, DexType invocationContext) {
     assert checkIfObsolete();
-    return lookupSingleVirtualTarget(method, method.holder);
+    return lookupSingleVirtualTarget(method, invocationContext, method.holder);
   }
 
-  public DexEncodedMethod lookupSingleVirtualTarget(DexMethod method, DexType refinedReceiverType) {
+  public DexEncodedMethod lookupSingleVirtualTarget(
+      DexMethod method, DexType invocationContext, DexType refinedReceiverType) {
     assert checkIfObsolete();
+    DexEncodedMethod directResult = nestAccessLookup(method, invocationContext);
+    if (directResult != null) {
+      return directResult;
+    }
     // This implements the logic from
     // https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.invokevirtual
     assert method != null;
@@ -847,6 +853,21 @@
     return result;
   }
 
+  private DexEncodedMethod nestAccessLookup(DexMethod method, DexType invocationContext) {
+    if (method.holder == invocationContext || !definitionFor(invocationContext).isInANest()) {
+      return null;
+    }
+    DexEncodedMethod directTarget = lookupDirectTarget(method);
+    assert directTarget == null || directTarget.method.holder == method.holder;
+    if (directTarget != null
+        && directTarget.isPrivateMethod()
+        && NestUtils.sameNest(method.holder, invocationContext, this)) {
+      return directTarget;
+    }
+
+    return null;
+  }
+
   /**
    * Computes which methods overriding <code>method</code> are visible for the subtypes of type.
    *
@@ -937,14 +958,18 @@
     return false;
   }
 
-  public DexEncodedMethod lookupSingleInterfaceTarget(DexMethod method) {
+  public DexEncodedMethod lookupSingleInterfaceTarget(DexMethod method, DexType invocationContext) {
     assert checkIfObsolete();
-    return lookupSingleInterfaceTarget(method, method.holder);
+    return lookupSingleInterfaceTarget(method, invocationContext, method.holder);
   }
 
   public DexEncodedMethod lookupSingleInterfaceTarget(
-      DexMethod method, DexType refinedReceiverType) {
+      DexMethod method, DexType invocationContext, DexType refinedReceiverType) {
     assert checkIfObsolete();
+    DexEncodedMethod directResult = nestAccessLookup(method, invocationContext);
+    if (directResult != null) {
+      return directResult;
+    }
     if (instantiatedLambdas.contains(method.holder)) {
       return null;
     }
diff --git a/src/test/examplesJava11/nesthostexample/NestPvtFieldPropagated.java b/src/test/examplesJava11/nesthostexample/NestPvtFieldPropagated.java
new file mode 100644
index 0000000..2cbdf25
--- /dev/null
+++ b/src/test/examplesJava11/nesthostexample/NestPvtFieldPropagated.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2019, 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 nesthostexample;
+
+public class NestPvtFieldPropagated {
+
+  public static class Inner {
+    private static String staticField = "toPropagateStatic";
+  }
+
+  public static void main(String[] args) {
+    System.out.println(Inner.staticField);
+  }
+}
diff --git a/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java b/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java
new file mode 100644
index 0000000..dd76928
--- /dev/null
+++ b/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2019, 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 nesthostexample;
+
+public class NestPvtMethodCallInlined {
+
+  public static class Inner {
+
+    public String methodWithPvtCallToInline() {
+      return notInlinedPvtCall();
+    }
+
+    @NeverInline
+    private String notInlinedPvtCall() {
+      return "notInlinedPvtCallInner";
+    }
+
+    private String nestPvtCallToInline() {
+      return "nestPvtCallToInlineInner";
+    }
+  }
+
+  public interface InnerInterface {
+
+    default String methodWithPvtCallToInline() {
+      return notInlinedPvtCall();
+    }
+
+    @NeverInline
+    private String notInlinedPvtCall() {
+      return "notInlinedPvtCallInnerInterface";
+    }
+
+    private String nestPvtCallToInline() {
+      return "nestPvtCallToInlineInnerInterface";
+    }
+
+    default String dispatch(InnerSub sub) {
+      return sub.notInlinedPvtCall();
+    }
+
+    @NeverInline
+    default String dispatchInlining(InnerSub iSub) {
+      return iSub.dispatch(this);
+    }
+  }
+
+  public static class InnerInterfaceImpl implements InnerInterface {}
+
+  public static class InnerSub extends Inner {
+
+    @NeverInline
+    public String dispatchInlining(InnerInterface impl) {
+      return impl.dispatch(this);
+    }
+
+    public String dispatch(InnerInterface itf) {
+      return itf.notInlinedPvtCall();
+    }
+
+    @NeverInline
+    private String notInlinedPvtCall() {
+      return "notInlinedPvtCallInnerSub";
+    }
+
+    private String nestPvtCallToInline() {
+      return "nestPvtCallToInlineInnerSub";
+    }
+  }
+
+  public static void main(String[] args) {
+    Inner i = new Inner();
+    InnerSub iSub = new InnerSub();
+    InnerInterface impl = new InnerInterfaceImpl();
+
+    // Inlining through nest access (invoke virtual/interface).
+    System.out.println(i.nestPvtCallToInline());
+    System.out.println(impl.nestPvtCallToInline());
+
+    // Inlining transformations.
+    // Invoke direct -> invoke virtual.
+    System.out.println(i.methodWithPvtCallToInline());
+    // Invoke interface -> invoke virtual.
+    System.out.println(impl.methodWithPvtCallToInline());
+    // Invoke virtual -> invoke direct.
+    System.out.println(iSub.dispatchInlining(impl));
+    // Invoke interface -> invoke direct.
+    System.out.println(impl.dispatchInlining(iSub));
+
+    // Inheritance + invoke virtual and nest access.
+    // This may mess up lookup logic.
+    System.out.println(iSub.nestPvtCallToInline());
+    System.out.println(((Inner) iSub).nestPvtCallToInline());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTestUtils.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTestUtils.java
index ce661cb..77ae469 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTestUtils.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTestUtils.java
@@ -53,6 +53,13 @@
           "NestHostInliningSubclasses$InnerNoPrivAccess",
           "OutsideInliningNoAccess",
           "OutsideInliningWithAccess",
+          "NestPvtMethodCallInlined",
+          "NestPvtMethodCallInlined$Inner",
+          "NestPvtMethodCallInlined$InnerInterface",
+          "NestPvtMethodCallInlined$InnerInterfaceImpl",
+          "NestPvtMethodCallInlined$InnerSub",
+          "NestPvtFieldPropagated",
+          "NestPvtFieldPropagated$Inner",
           "NestHostExample",
           "NestHostExample$NestMemberInner",
           "NestHostExample$NestMemberInner$NestMemberInnerInner",
@@ -77,6 +84,8 @@
           .put("prune", "BasicNestHostTreePruning")
           .put("inlining", "NestHostInlining")
           .put("inliningSub", "NestHostInliningSubclasses")
+          .put("pvtCallInlined", "NestPvtMethodCallInlined")
+          .put("memberPropagated", "NestPvtFieldPropagated")
           .build();
   public static final String ALL_RESULT_LINE =
       String.join(
@@ -134,6 +143,18 @@
                   "staticInterfaceMethodstaticStaticInterfaceMethod",
                   "staticInterfaceMethodstaticStaticInterfaceMethod",
                   "staticInterfaceMethodstaticStaticInterfaceMethod"))
+          .put(
+              "pvtCallInlined",
+              StringUtils.lines(
+                  "nestPvtCallToInlineInner",
+                  "nestPvtCallToInlineInnerInterface",
+                  "notInlinedPvtCallInner",
+                  "notInlinedPvtCallInnerInterface",
+                  "notInlinedPvtCallInnerSub",
+                  "notInlinedPvtCallInnerInterface",
+                  "nestPvtCallToInlineInnerSub",
+                  "nestPvtCallToInlineInner"))
+          .put("memberPropagated", StringUtils.lines("toPropagateStatic"))
           .build();
 
   public static String getMainClass(String id) {
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java
new file mode 100644
index 0000000..5463fcc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2019, 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.desugar.nestaccesscontrol;
+
+import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.classesMatching;
+import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getExpectedResult;
+import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getMainClass;
+import static junit.framework.TestCase.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NestMemberPropagatedTest extends TestBase {
+
+  public NestMemberPropagatedTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withAllApiLevels()
+        .build();
+  }
+
+  @Test
+  public void testPvtMemberPropagated() throws Exception {
+    List<Path> toCompile = classesMatching("NestPvtFieldPropagated");
+    testForR8(parameters.getBackend())
+        .addKeepMainRule(getMainClass("memberPropagated"))
+        .noMinification()
+        .addOptionsModification(
+            options -> {
+              options.enableClassInlining = false;
+            })
+        .setMinApi(parameters.getApiLevel())
+        .addProgramFiles(toCompile)
+        .compile()
+        .inspect(this::assertMemberPropagated)
+        .run(parameters.getRuntime(), getMainClass("memberPropagated"))
+        .assertSuccessWithOutput(getExpectedResult("memberPropagated"));
+  }
+
+  private void assertMemberPropagated(CodeInspector inspector) {
+    for (FoundClassSubject subj : inspector.allClasses()) {
+      assertEquals(0, subj.allFields().size());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
new file mode 100644
index 0000000..f861377
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2019, 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.desugar.nestaccesscontrol;
+
+import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.classesMatching;
+import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getExpectedResult;
+import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getMainClass;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NestMethodInlinedTest extends TestBase {
+
+  public NestMethodInlinedTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withAllApiLevels()
+        .build();
+  }
+
+  @Test
+  public void testPvtMethodCallInlined() throws Exception {
+    List<Path> toCompile = classesMatching("NestPvtMethodCallInlined");
+    testForR8(parameters.getBackend())
+        .addKeepMainRule(getMainClass("pvtCallInlined"))
+        .noMinification()
+        .addOptionsModification(
+            options -> {
+              options.enableValuePropagation = false;
+              options.enableClassInlining = false;
+              options.enableVerticalClassMerging = false;
+            })
+        .enableInliningAnnotations("nesthostexample")
+        .setMinApi(parameters.getApiLevel())
+        .addProgramFiles(toCompile)
+        .compile()
+        .inspect(this::assertMethodsInlined)
+        .inspect(NestAttributesUpdateTest::assertNestAttributesCorrect)
+        .run(parameters.getRuntime(), getMainClass("pvtCallInlined"))
+        .assertSuccessWithOutput(getExpectedResult("pvtCallInlined"));
+  }
+
+  private void assertMethodsInlined(CodeInspector inspector) {
+    // Inlining through nest access.
+    int nbDispatchInlining = 0;
+    int nbNotInlinedPvtCall = 0;
+    for (FoundClassSubject subj : inspector.allClasses()) {
+      // TODO (b/133745203): inline invokeinterface accessed through nest access control.
+      // Remove the if.
+      if (!subj.getDexClass().isInterface()) {
+        assertTrue(
+            "nestPvtCallToInline should be inlined (from " + subj.getOriginalName() + ")",
+            subj.allMethods().stream()
+                .noneMatch(
+                    method ->
+                        method.toString().contains("nestPvtCallToInline")
+                            || method.toString().contains("methodWithPvtCallToInline")));
+      }
+      // Inlining nest access should transform virtual/ift invokes -> direct.
+      MethodSubject methodSubject = subj.uniqueMethodWithName("dispatchInlining");
+      if (methodSubject.isPresent()) {
+        nbDispatchInlining++;
+        assertTrue(
+            methodSubject.streamInstructions().noneMatch(InstructionSubject::isInvokeVirtual));
+        // TODO (b/133745203): inline invokeinterface accessed through nest access control.
+        // Also assert no invokeInterface
+      }
+      methodSubject = subj.uniqueMethodWithName("notInlinedPvtCall");
+      if (methodSubject.isPresent()) {
+        nbNotInlinedPvtCall++;
+      }
+    }
+    assertEquals("dispatchInlining methods should not be inlined", 2, nbDispatchInlining);
+    assertEquals("notInlinedPvtCall methods should not be inlined", 3, nbNotInlinedPvtCall);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index 70dd5ea..8e898e8 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -223,7 +223,7 @@
   public void lookupSingleTarget() {
     DexMethod method = buildMethod(invokeReceiver, methodName);
     Assert.assertNotNull(appInfo.resolveMethod(toType(invokeReceiver), method).asResultOfResolve());
-    DexEncodedMethod singleVirtualTarget = appInfo.lookupSingleVirtualTarget(method);
+    DexEncodedMethod singleVirtualTarget = appInfo.lookupSingleVirtualTarget(method, method.holder);
     if (singleTargetHolderOrNull == null) {
       Assert.assertNull(singleVirtualTarget);
     } else {