Inline lambda methods into synthetic accessors

Bug: b/302376570
Change-Id: Ibf28924ab03a7e66d8b957ab8df4e7a3f5a27a81
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 70eb14e..96ceebf 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -825,7 +825,6 @@
     internal.passthroughDexCode = true;
 
     // Assert some of R8 optimizations are disabled.
-    assert !internal.inlinerOptions().enableInlining;
     assert !internal.enableClassInlining;
     assert !internal.enableEnumValueOptimization;
     assert !internal.outline.enabled;
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
index b782e91..5079e73 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
@@ -223,8 +223,6 @@
     internal.mainDexListConsumer = mainDexListConsumer;
     internal.mainDexKeptGraphConsumer = mainDexKeptGraphConsumer;
     internal.minimalMainDex = internal.debug;
-    internal.enableEnumValueOptimization = false;
-    internal.inlinerOptions().enableInlining = false;
     assert internal.retainCompileTimeAnnotations;
     internal.retainCompileTimeAnnotations = false;
     return internal;
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 1372ccb..912ef55 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -193,7 +193,6 @@
     assert !internal.passthroughDexCode;
 
     // Assert some of R8 optimizations are disabled.
-    assert !internal.inlinerOptions().enableInlining;
     assert !internal.enableClassInlining;
     assert !internal.enableEnumValueOptimization;
     assert !internal.outline.enabled;
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index 58011d6..0c9cb62 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -184,6 +184,10 @@
     unset(Constants.ACC_PRIVATE);
   }
 
+  public boolean wasPrivate() {
+    return wasSet(Constants.ACC_PRIVATE);
+  }
+
   public boolean isProtected() {
     return isSet(Constants.ACC_PROTECTED);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index ba6564d..5422c8a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -219,9 +219,12 @@
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       assumeInserter = new AssumeInserter(appViewWithLiveness);
       this.lensCodeRewriter = new LensCodeRewriter(appViewWithLiveness);
-      this.inliner = new Inliner(appViewWithLiveness, this, lensCodeRewriter);
+      this.inliner =
+          options.inlinerOptions().isEnabled()
+              ? new Inliner(appViewWithLiveness, this, lensCodeRewriter)
+              : null;
       this.classInliner =
-          options.enableClassInlining && options.inlinerOptions().enableInlining
+          options.enableClassInlining && inliner != null
               ? new ClassInliner(appViewWithLiveness, inliner)
               : null;
       this.dynamicTypeOptimization = new DynamicTypeOptimization(appViewWithLiveness);
@@ -638,7 +641,7 @@
 
     previous = printMethod(code, "IR after inserting assume instructions (SSA)", previous);
 
-    if (!isDebugMode && options.inlinerOptions().enableInlining && inliner != null) {
+    if (inliner != null && !isDebugMode) {
       timing.begin("Inlining");
       inliner.performInlining(code.context(), code, feedback, methodProcessor, timing);
       timing.end();
@@ -716,8 +719,6 @@
       timing.begin("Inline classes");
       // Class inliner should work before lambda merger, so if it inlines the
       // lambda, it does not get collected by merger.
-      assert options.inlinerOptions().enableInlining;
-      assert inliner != null;
       classInliner.processMethodCode(
           context, code, feedback, methodProcessor, methodProcessingContext);
       timing.end();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallGraph.java
index 1e890ec..16dfc8b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallGraph.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallGraph.java
@@ -59,12 +59,24 @@
 
   public CallSiteInformation createCallSiteInformation(
       AppView<AppInfoWithLiveness> appView, MethodProcessorWithWave methodProcessor) {
-    // Don't leverage single/dual call site information when we are not tree shaking.
-    return appView.options().isShrinking()
+    return needsCallSiteInformation(appView)
         ? new CallGraphBasedCallSiteInformation(appView, this, methodProcessor)
         : CallSiteInformation.empty();
   }
 
+  private boolean needsCallSiteInformation(AppView<AppInfoWithLiveness> appView) {
+    InternalOptions options = appView.options();
+    if (options.debug) {
+      return false;
+    }
+    if (options.isOptimizing() && options.isShrinking()) {
+      return true;
+    }
+    // When we are neither optimizing nor shrinking, we still allow inlining of javac synthetic
+    // lambda methods into R8 generated accessor methods.
+    return options.isGeneratingDex();
+  }
+
   public ProgramMethodSet extractLeaves() {
     return extractNodes(Node::isLeaf, Node::cleanCallersAndReadersForRemoval);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
index f89b900..36fe467 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.conversion.MethodProcessorWithWave;
+import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.KeepMethodInfo;
 import com.android.tools.r8.synthesis.SyntheticItems;
@@ -19,6 +20,7 @@
 import com.google.common.collect.Sets;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
 
 public abstract class CallSiteInformation {
 
@@ -94,16 +96,32 @@
       this.methodProcessor = methodProcessor;
 
       InternalOptions options = appView.options();
-      ProgramMethodSet pinned =
-          MethodOverridesCollector.findAllMethodsAndOverridesThatMatches(
-              appView,
-              ImmediateProgramSubtypingInfo.create(appView),
-              appView.appInfo().classes(),
-              method -> {
+      Predicate<ProgramMethod> pinnedPredicate;
+      if (options.isOptimizing() && options.isShrinking()) {
+        ProgramMethodSet pinnedMethods =
+            MethodOverridesCollector.findAllMethodsAndOverridesThatMatches(
+                appView,
+                ImmediateProgramSubtypingInfo.create(appView),
+                appView.appInfo().classes(),
+                method -> {
+                  KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+                  return !keepInfo.isClosedWorldReasoningAllowed(options, method)
+                      || keepInfo.isPinned(options, method);
+                });
+        pinnedPredicate = pinnedMethods::contains;
+      } else {
+        pinnedPredicate =
+            method -> {
+              MethodOptimizationInfo optimizationInfo = method.getOptimizationInfo();
+              if (optimizationInfo.shouldSingleCallerInlineIntoSyntheticLambdaAccessor()) {
                 KeepMethodInfo keepInfo = appView.getKeepInfo(method);
-                return !keepInfo.isClosedWorldReasoningAllowed(options)
-                    || keepInfo.isPinned(options);
-              });
+                assert keepInfo.isClosedWorldReasoningAllowed(options, method);
+                assert !keepInfo.isPinned(options, method);
+                return false;
+              }
+              return true;
+            };
+      }
 
       for (Node node : graph.getNodes()) {
         ProgramMethod method = node.getProgramMethod();
@@ -111,7 +129,7 @@
 
         // For non-pinned methods and methods that override library methods we do not know the exact
         // number of call sites.
-        if (pinned.contains(method)) {
+        if (pinnedPredicate.test(method)) {
           continue;
         }
 
@@ -128,7 +146,7 @@
 
         int numberOfCallSites = node.getNumberOfCallSites();
         if (numberOfCallSites == 1) {
-          if (!appView.getKeepInfo(method).isSingleCallerInliningAllowed(options)) {
+          if (!appView.getKeepInfo(method).isSingleCallerInliningAllowed(options, method)) {
             continue;
           }
           if (methodProcessor.isPostMethodProcessor()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
index 5ab36a6..c0705e4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
@@ -59,7 +59,7 @@
     }
     if (appViewWithLiveness
         .getKeepInfo(callee)
-        .isCodeReplacementAllowed(appViewWithLiveness.options())) {
+        .isCodeReplacementAllowed(appViewWithLiveness.options(), callee)) {
       // Since the code of the callee may be replaced, we cannot inline it into the caller, and we
       // also cannot collect any optimization info for the method. Therefore, we drop the call edge
       // to reduce the total number of call graph edges, which should lead to fewer call graph
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index fcd5428..20f1dda 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -4,6 +4,10 @@
 
 package com.android.tools.r8.ir.desugar;
 
+import static com.android.tools.r8.ir.desugar.lambda.ForcefullyMovedLambdaMethodConsumer.emptyForcefullyMovedLambdaMethodConsumer;
+import static com.android.tools.r8.ir.desugar.lambda.SyntheticLambdaAccessorMethodConsumer.emptySyntheticLambdaAccessorMethodConsumer;
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
+
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -30,6 +34,7 @@
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
 import com.android.tools.r8.ir.desugar.lambda.LambdaDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.lambda.SyntheticLambdaAccessorMethodConsumer;
 import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.twr.TwrCloseResourceDesugaringEventConsumer;
@@ -51,6 +56,7 @@
 import java.util.Set;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * Class that gets notified for structural changes made as a result of desugaring (e.g., the
@@ -93,6 +99,8 @@
       ProfileCollectionAdditions profileCollectionAdditions,
       BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer,
       BiConsumer<ConstantDynamicClass, ProgramMethod> constantDynamicClassConsumer,
+      Function<LambdaClass, SyntheticLambdaAccessorMethodConsumer>
+          syntheticLambdaAccessorMethodConsumer,
       BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer,
       SyntheticAdditions additions,
       BiConsumer<ProgramMethod, ProgramMethod> companionMethodConsumer) {
@@ -101,6 +109,7 @@
             appView,
             lambdaClassConsumer,
             constantDynamicClassConsumer,
+            syntheticLambdaAccessorMethodConsumer,
             twrCloseResourceMethodConsumer,
             additions,
             companionMethodConsumer);
@@ -436,7 +445,9 @@
       synthesizedLambdaClasses.sort(Comparator.comparing(LambdaClass::getType));
       for (LambdaClass lambdaClass : synthesizedLambdaClasses) {
         lambdaClass.target.ensureAccessibilityIfNeeded(
-            classConverterResultBuilder, needsProcessing);
+            classConverterResultBuilder,
+            emptySyntheticLambdaAccessorMethodConsumer(),
+            needsProcessing);
         lambdaClass.getLambdaProgramClass().forEachProgramMethod(needsProcessing);
       }
       synthesizedLambdaClasses.clear();
@@ -474,6 +485,8 @@
     //  synthetic items.
     private final BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer;
     private final BiConsumer<ConstantDynamicClass, ProgramMethod> constantDynamicClassConsumer;
+    private final Function<LambdaClass, SyntheticLambdaAccessorMethodConsumer>
+        syntheticLambdaAccessorMethodConsumer;
     private final BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer;
     private final SyntheticAdditions additions;
 
@@ -488,12 +501,15 @@
         AppView<? extends AppInfoWithClassHierarchy> appView,
         BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer,
         BiConsumer<ConstantDynamicClass, ProgramMethod> constantDynamicClassConsumer,
+        Function<LambdaClass, SyntheticLambdaAccessorMethodConsumer>
+            syntheticLambdaAccessorMethodConsumer,
         BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer,
         SyntheticAdditions additions,
         BiConsumer<ProgramMethod, ProgramMethod> onCompanionMethodCallback) {
       this.appView = appView;
       this.lambdaClassConsumer = lambdaClassConsumer;
       this.constantDynamicClassConsumer = constantDynamicClassConsumer;
+      this.syntheticLambdaAccessorMethodConsumer = syntheticLambdaAccessorMethodConsumer;
       this.twrCloseResourceMethodConsumer = twrCloseResourceMethodConsumer;
       this.additions = additions;
       this.onCompanionMethodCallback = onCompanionMethodCallback;
@@ -789,7 +805,10 @@
             LambdaClass lambdaClass = entry.getKey();
             ProgramMethod context = entry.getValue();
 
-            lambdaClass.target.ensureAccessibilityIfNeeded();
+            lambdaClass.target.ensureAccessibilityIfNeeded(
+                emptyForcefullyMovedLambdaMethodConsumer(),
+                syntheticLambdaAccessorMethodConsumer.apply(lambdaClass),
+                emptyConsumer());
 
             // Populate set of types with serialized lambda method for removal.
             if (lambdaClass.descriptor.interfaces.contains(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 6812690..aa3cbe9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.ir.desugar;
 
-import static com.android.tools.r8.ir.desugar.lambda.ForcefullyMovedLambdaMethodConsumer.emptyForcefullyMovedLambdaMethodConsumer;
-import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
 import static com.android.tools.r8.utils.DesugarUtils.appendFullyQualifiedHolderToMethodName;
 
 import com.android.tools.r8.dex.Constants;
@@ -33,6 +31,7 @@
 import com.android.tools.r8.ir.desugar.lambda.ForcefullyMovedLambdaMethodConsumer;
 import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
 import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring.DesugarInvoke;
+import com.android.tools.r8.ir.desugar.lambda.SyntheticLambdaAccessorMethodConsumer;
 import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
 import java.util.ArrayList;
 import java.util.List;
@@ -285,14 +284,18 @@
   }
 
   private boolean canAccessModifyLambdaImplMethod() {
-    MethodHandleType invokeType = descriptor.implHandle.type;
     return appView.options().canAccessModifyLambdaImplementationMethods(appView)
-        && !isPrivateOrStaticInterfaceMethodInvokeThatWillBeDesugared()
-        && (invokeType.isInvokeDirect() || invokeType.isInvokeStatic())
-        && descriptor.delegatesToLambdaImplMethod(appView.dexItemFactory())
+        && canAccessModifyLambdaImplMethodInD8()
         && !desugaring.isDirectTargetedLambdaImplementationMethod(descriptor.implHandle);
   }
 
+  public boolean canAccessModifyLambdaImplMethodInD8() {
+    MethodHandleType invokeType = descriptor.implHandle.type;
+    return !isPrivateOrStaticInterfaceMethodInvokeThatWillBeDesugared()
+        && (invokeType.isInvokeDirect() || invokeType.isInvokeStatic())
+        && descriptor.delegatesToLambdaImplMethod(appView.dexItemFactory());
+  }
+
   @SuppressWarnings("ReferenceEquality")
   private Target createLambdaImplMethodTarget(ProgramMethod accessedFrom) {
     DexMethodHandle implHandle = descriptor.implHandle;
@@ -478,18 +481,19 @@
     // Ensure access of the referenced symbol(s).
     abstract ProgramMethod ensureAccessibility(
         ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        SyntheticLambdaAccessorMethodConsumer syntheticLambdaAccessorMethodConsumer,
         Consumer<ProgramMethod> needsProcessingConsumer);
 
-    public final void ensureAccessibilityIfNeeded() {
-      ensureAccessibilityIfNeeded(emptyForcefullyMovedLambdaMethodConsumer(), emptyConsumer());
-    }
-
     // Ensure access of the referenced symbol(s).
     public final void ensureAccessibilityIfNeeded(
         ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        SyntheticLambdaAccessorMethodConsumer syntheticLambdaAccessorMethodConsumer,
         Consumer<ProgramMethod> needsProcessingConsumer) {
       if (!hasEnsuredAccessibility) {
-        ensureAccessibility(forcefullyMovedLambdaMethodConsumer, needsProcessingConsumer);
+        ensureAccessibility(
+            forcefullyMovedLambdaMethodConsumer,
+            syntheticLambdaAccessorMethodConsumer,
+            needsProcessingConsumer);
         hasEnsuredAccessibility = true;
       }
     }
@@ -534,6 +538,7 @@
     @Override
     ProgramMethod ensureAccessibility(
         ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        SyntheticLambdaAccessorMethodConsumer syntheticLambdaAccessorMethodConsumer,
         Consumer<ProgramMethod> needsProcessingConsumer) {
       return null;
     }
@@ -552,6 +557,7 @@
     @Override
     ProgramMethod ensureAccessibility(
         ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        SyntheticLambdaAccessorMethodConsumer syntheticLambdaAccessorMethodConsumer,
         Consumer<ProgramMethod> needsProcessingConsumer) {
       // We already found the static method to be called, just relax its accessibility.
       MethodAccessFlags flags = target.getAccessFlags();
@@ -580,6 +586,7 @@
     @Override
     ProgramMethod ensureAccessibility(
         ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        SyntheticLambdaAccessorMethodConsumer syntheticLambdaAccessorMethodConsumer,
         Consumer<ProgramMethod> needsProcessingConsumer) {
       // For all instantiation points for which the compiler creates lambda$
       // methods, it creates these methods in the same class/interface.
@@ -659,6 +666,7 @@
     @Override
     ProgramMethod ensureAccessibility(
         ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        SyntheticLambdaAccessorMethodConsumer syntheticLambdaAccessorMethodConsumer,
         Consumer<ProgramMethod> needsProcessingConsumer) {
       return null;
     }
@@ -680,6 +688,7 @@
     @Override
     ProgramMethod ensureAccessibility(
         ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        SyntheticLambdaAccessorMethodConsumer syntheticLambdaAccessorMethodConsumer,
         Consumer<ProgramMethod> needsProcessingConsumer) {
       // When compiling with whole program optimization, check that we are not inplace modifying.
       // For all instantiation points for which the compiler creates lambda$
@@ -782,6 +791,7 @@
     @Override
     ProgramMethod ensureAccessibility(
         ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
+        SyntheticLambdaAccessorMethodConsumer syntheticLambdaAccessorMethodConsumer,
         Consumer<ProgramMethod> needsProcessingConsumer) {
       // Create a static accessor with proper accessibility.
       DexProgramClass accessorClass = appView.definitionForProgramType(callTarget.holder);
@@ -811,6 +821,8 @@
                   .disableAndroidApiLevelCheck()
                   .build());
       accessorClass.addDirectMethod(accessorMethod.getDefinition());
+      syntheticLambdaAccessorMethodConsumer.acceptSyntheticLambdaAccessorMethod(
+          accessorMethod, implMethod);
       needsProcessingConsumer.accept(accessorMethod);
       return accessorMethod;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
index d92a51a..5ba70be 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
@@ -32,18 +32,18 @@
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
 import com.android.tools.r8.utils.Box;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
 import java.util.Collection;
 import java.util.Deque;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import org.objectweb.asm.Opcodes;
 
 public class LambdaInstructionDesugaring implements CfInstructionDesugaring {
 
   private final AppView<?> appView;
   private final Set<DexMethod> directTargetedLambdaImplementationMethods =
-      Sets.newIdentityHashSet();
+      ConcurrentHashMap.newKeySet();
 
   public boolean isDirectTargetedLambdaImplementationMethod(DexMethodHandle implMethod) {
     return implMethod.type.isInvokeDirect()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/SyntheticLambdaAccessorMethodConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/SyntheticLambdaAccessorMethodConsumer.java
new file mode 100644
index 0000000..c499cbb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/SyntheticLambdaAccessorMethodConsumer.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2024, 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.desugar.lambda;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface SyntheticLambdaAccessorMethodConsumer {
+
+  void acceptSyntheticLambdaAccessorMethod(ProgramMethod accessor, DexMethod target);
+
+  static SyntheticLambdaAccessorMethodConsumer emptySyntheticLambdaAccessorMethodConsumer() {
+    return (accessor, target) -> {};
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index a3881fc..a0bfb2d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -524,7 +524,7 @@
       ProgramMethod singleTarget,
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
     DexMethod singleTargetReference = singleTarget.getReference();
-    if (!appView.getKeepInfo(singleTarget).isInliningAllowed(options)) {
+    if (!appView.getKeepInfo(singleTarget).isInliningAllowed(options, singleTarget)) {
       whyAreYouNotInliningReporter.reportPinned();
       return true;
     }
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 a2940f7..17eb9a8 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
@@ -156,7 +156,7 @@
   @SuppressWarnings("ReferenceEquality")
   public ConstraintWithTarget computeInliningConstraint(IRCode code) {
     InternalOptions options = appView.options();
-    if (!options.inlinerOptions().enableInlining) {
+    if (!options.inlinerOptions().isEnabled()) {
       return ConstraintWithTarget.NEVER;
     }
     ProgramMethod method = code.context();
@@ -165,7 +165,7 @@
       return ConstraintWithTarget.NEVER;
     }
     KeepMethodInfo keepInfo = appView.getKeepInfo(method);
-    if (!keepInfo.isInliningAllowed(options) && !keepInfo.isClassInliningAllowed(options)) {
+    if (!keepInfo.isInliningAllowed(options, method) && !keepInfo.isClassInliningAllowed(options)) {
       return ConstraintWithTarget.NEVER;
     }
 
@@ -1342,7 +1342,8 @@
           singleCallerInlinedMethodsForClass.removeIf(
               (callee, caller) -> {
                 boolean convertToAbstractOrThrowNullMethod =
-                    callee.getDefinition().belongsToVirtualPool();
+                    callee.getDefinition().belongsToVirtualPool()
+                        && !callee.getAccessFlags().wasPrivate();
                 if (callee.getDefinition().getGenericSignature().hasSignature()) {
                   // TODO(b/203188583): Enable pruning of methods with generic signatures. For this
                   //  to work we need to pass in a seed to GenericSignatureContextBuilder.create in
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index b348230..6f8b997 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -195,6 +195,11 @@
   }
 
   @Override
+  public boolean shouldSingleCallerInlineIntoSyntheticLambdaAccessor() {
+    return false;
+  }
+
+  @Override
   public boolean mayHaveSideEffects() {
     return UNKNOWN_MAY_HAVE_SIDE_EFFECTS;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index c23876d..4edfa65 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -26,7 +26,8 @@
   enum InlinePreference {
     MultiCallerInline,
     ForceInline,
-    Default
+    Default,
+    SingleCallerInlineIntoSyntheticLambdaAccessor
   }
 
   public boolean isDefault() {
@@ -108,6 +109,8 @@
 
   public abstract boolean forceInline();
 
+  public abstract boolean shouldSingleCallerInlineIntoSyntheticLambdaAccessor();
+
   public abstract boolean mayHaveSideEffects();
 
   /** Context sensitive version of {@link #mayHaveSideEffects()}. */
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java
index 727328a..1ec1914 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfo.java
@@ -241,6 +241,11 @@
   }
 
   @Override
+  public boolean shouldSingleCallerInlineIntoSyntheticLambdaAccessor() {
+    throw new Unreachable();
+  }
+
+  @Override
   public boolean returnValueHasBeenPropagated() {
     throw new Unreachable();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index f8c1f07..16d1013 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -598,6 +598,11 @@
   }
 
   @Override
+  public boolean shouldSingleCallerInlineIntoSyntheticLambdaAccessor() {
+    return inlining == InlinePreference.SingleCallerInlineIntoSyntheticLambdaAccessor;
+  }
+
+  @Override
   public boolean mayHaveSideEffects() {
     return isFlagSet(MAY_HAVE_SIDE_EFFECT_FLAG);
   }
@@ -803,6 +808,12 @@
     inlining = InlinePreference.Default;
   }
 
+  public void markSingleCallerInlineIntoSyntheticLambdaAccessor() {
+    assert inlining == InlinePreference.Default
+        || inlining == InlinePreference.SingleCallerInlineIntoSyntheticLambdaAccessor;
+    inlining = InlinePreference.SingleCallerInlineIntoSyntheticLambdaAccessor;
+  }
+
   void setMultiCallerMethod() {
     if (inlining == InlinePreference.Default) {
       inlining = InlinePreference.MultiCallerInline;
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 063b69b..20741e0 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
@@ -287,7 +287,6 @@
             classesWithSingleCallerInlinedInstanceInitializers, executorService, timing);
     classesWithSingleCallerInlinedInstanceInitializers = null;
 
-    // TODO(b/296030319): Also publish the computed optimization information for fields.
     PrunedItems prunedItems =
         new ArgumentPropagatorOptimizationInfoPopulator(
                 appView, converter, fieldStates, methodStates, postMethodProcessorBuilder)
@@ -313,7 +312,9 @@
   public void onMethodPruned(ProgramMethod method) {
     if (codeScanner != null) {
       MethodState methodState = codeScanner.getMethodStates().removeOrElse(method, null);
-      assert methodState == null || method.getDefinition().belongsToDirectPool();
+      assert methodState == null
+          || method.getDefinition().belongsToDirectPool()
+          || method.getAccessFlags().wasPrivate();
     }
 
     assert effectivelyUnusedArgumentsAnalysis != null;
diff --git a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerScanner.java b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerScanner.java
index 205bd4b..26b85ed 100644
--- a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerScanner.java
@@ -48,7 +48,7 @@
     singleCallerMethodCandidates.removeIf(
         (callee, caller) ->
             callee.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
-                || !appView.getKeepInfo(callee).isSingleCallerInliningAllowed(options)
+                || !appView.getKeepInfo(callee).isSingleCallerInliningAllowed(options, callee)
                 || !AndroidApiLevelUtils.isApiSafeForInlining(caller, callee, appView.options()));
     return traceInstructions(singleCallerMethodCandidates, executorService);
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index cb5e723..915973c 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.graph.FieldAccessInfoImpl.MISSING_FIELD_ACCESS_INFO;
 import static com.android.tools.r8.ir.desugar.LambdaDescriptor.isLambdaMetafactoryMethod;
@@ -121,6 +122,8 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
+import com.android.tools.r8.ir.desugar.lambda.SyntheticLambdaAccessorMethodConsumer;
+import com.android.tools.r8.ir.optimize.info.MutableMethodOptimizationInfo;
 import com.android.tools.r8.keepanno.ast.KeepDeclaration;
 import com.android.tools.r8.kotlin.KotlinMetadataEnqueuerExtension;
 import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
@@ -4288,6 +4291,7 @@
             profileCollectionAdditions,
             lambdaCallback,
             this::recordConstantDynamicSynthesizingContext,
+            this::recordSyntheticLambdaAccessorMethod,
             this::recordTwrCloseResourceMethodSynthesizingContext,
             additions,
             (method, companion) -> {
@@ -4350,6 +4354,33 @@
     }
   }
 
+  private SyntheticLambdaAccessorMethodConsumer recordSyntheticLambdaAccessorMethod(
+      LambdaClass lambdaClass) {
+    return (accessor, target) -> recordSyntheticLambdaAccessorMethod(lambdaClass, accessor, target);
+  }
+
+  private void recordSyntheticLambdaAccessorMethod(
+      LambdaClass lambdaClass, ProgramMethod accessor, DexMethod target) {
+    assert mode.isInitialTreeShaking();
+    if (options.debug) {
+      return;
+    }
+    if (options.isOptimizing() && options.isShrinking()) {
+      return;
+    }
+    if (!lambdaClass.canAccessModifyLambdaImplMethodInD8()) {
+      return;
+    }
+    ProgramMethod targetMethod = asProgramMethodOrNull(appView.definitionFor(target));
+    if (targetMethod != null && targetMethod.getHolder() == accessor.getHolder()) {
+      MutableMethodOptimizationInfo optimizationInfo =
+          targetMethod.getDefinition().getMutableOptimizationInfo();
+      optimizationInfo.markSingleCallerInlineIntoSyntheticLambdaAccessor();
+    } else {
+      assert false;
+    }
+  }
+
   private void recordTwrCloseResourceMethodSynthesizingContext(
       ProgramMethod closeMethod, ProgramMethod context) {
     synchronized (synthesizingContexts) {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index 50c76a6..4b13a61 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.KeepAnnotationCollectionInfo.RetentionInfo;
 import com.android.tools.r8.shaking.KeepInfo.Builder;
 import com.android.tools.r8.shaking.KeepReason.ReflectiveUseFrom;
@@ -158,6 +159,13 @@
     return !isOptimizationAllowed(configuration) || !isShrinkingAllowed(configuration);
   }
 
+  public boolean isPinned(GlobalKeepInfoConfiguration configuration, ProgramMethod method) {
+    if (method.getOptimizationInfo().shouldSingleCallerInlineIntoSyntheticLambdaAccessor()) {
+      return false;
+    }
+    return isPinned(configuration);
+  }
+
   /**
    * True if an item may have its name minified/changed.
    *
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index f121490..e5ed3e7 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -128,6 +128,14 @@
     return isOptimizationAllowed(configuration) && internalIsClosedWorldReasoningAllowed();
   }
 
+  public boolean isClosedWorldReasoningAllowed(
+      GlobalKeepInfoConfiguration configuration, ProgramMethod method) {
+    if (method.getOptimizationInfo().shouldSingleCallerInlineIntoSyntheticLambdaAccessor()) {
+      return true;
+    }
+    return isClosedWorldReasoningAllowed(configuration);
+  }
+
   boolean internalIsClosedWorldReasoningAllowed() {
     return allowClosedWorldReasoning;
   }
@@ -138,6 +146,14 @@
         : internalIsCodeReplacementAllowed();
   }
 
+  public boolean isCodeReplacementAllowed(
+      GlobalKeepInfoConfiguration configuration, ProgramMethod method) {
+    if (method.getOptimizationInfo().shouldSingleCallerInlineIntoSyntheticLambdaAccessor()) {
+      return false;
+    }
+    return isCodeReplacementAllowed(configuration);
+  }
+
   boolean internalIsCodeReplacementAllowed() {
     return allowCodeReplacement;
   }
@@ -150,7 +166,11 @@
     return allowConstantArgumentOptimization;
   }
 
-  public boolean isInliningAllowed(GlobalKeepInfoConfiguration configuration) {
+  public boolean isInliningAllowed(
+      GlobalKeepInfoConfiguration configuration, ProgramMethod method) {
+    if (method.getOptimizationInfo().shouldSingleCallerInlineIntoSyntheticLambdaAccessor()) {
+      return true;
+    }
     return isOptimizationAllowed(configuration) && internalIsInliningAllowed();
   }
 
@@ -225,7 +245,11 @@
     return allowReturnTypeStrengthening;
   }
 
-  public boolean isSingleCallerInliningAllowed(GlobalKeepInfoConfiguration configuration) {
+  public boolean isSingleCallerInliningAllowed(
+      GlobalKeepInfoConfiguration configuration, ProgramMethod method) {
+    if (method.getOptimizationInfo().shouldSingleCallerInlineIntoSyntheticLambdaAccessor()) {
+      return true;
+    }
     return isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && internalIsSingleCallerInliningAllowed();
@@ -747,7 +771,6 @@
 
     public Joiner disallowParameterAnnotationsRemoval(RetentionInfo retention) {
       builder.getParameterAnnotationsInfo().destructiveJoinAnyTypeInfo(retention);
-      builder.self();
       return self();
     }
 
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 0952033..20d9edc 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -356,7 +356,6 @@
   }
 
   public void disableGlobalOptimizations() {
-    inlinerOptions.enableInlining = false;
     enableClassInlining = false;
     enableDevirtualization = false;
     enableEnumUnboxing = false;
@@ -1874,6 +1873,13 @@
       this.options = options;
     }
 
+    public boolean isEnabled() {
+      // Note that this is deliberately enabled in release mode even when optimizations and
+      // shrinking are disabled, since we still allow inlining of javac synthetic lambda methods
+      // into their R8 generated accessor methods.
+      return enableInlining && !options.debug;
+    }
+
     public static void disableInlining(InternalOptions options) {
       options.inlinerOptions().enableInlining = false;
     }
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/InlineIntoSyntheticLambdaAccessorMethodTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/InlineIntoSyntheticLambdaAccessorMethodTest.java
new file mode 100644
index 0000000..6b97e55
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/InlineIntoSyntheticLambdaAccessorMethodTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2024, 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.lambdas;
+
+import static com.android.tools.r8.ir.desugar.LambdaClass.R8_LAMBDA_ACCESSOR_METHOD_PREFIX;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InlineIntoSyntheticLambdaAccessorMethodTest extends TestBase {
+
+  @Parameter(0)
+  public boolean debug;
+
+  @Parameter(1)
+  public TestParameters parameters;
+
+  @Parameters(name = "{1}, debug: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        BooleanUtils.values(), getTestParameters().withDexRuntimesAndAllApiLevels().build());
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addDontObfuscate()
+        .addDontOptimize()
+        .addDontShrink()
+        .applyIf(debug, TestCompilerBuilder::debug)
+        .setMinApi(parameters)
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject mainClassSubject = inspector.clazz(Main.class);
+              assertThat(mainClassSubject, isPresent());
+
+              MethodSubject lambdaMethodSubject =
+                  mainClassSubject.uniqueMethodWithFinalName("lambda$main$0");
+              assertThat(lambdaMethodSubject, isPresentIf(debug));
+
+              MethodSubject accessorMethodSubject =
+                  mainClassSubject.uniqueMethodThatMatches(
+                      method -> method.getFinalName().startsWith(R8_LAMBDA_ACCESSOR_METHOD_PREFIX));
+              assertThat(accessorMethodSubject, isPresent());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithEmptyOutput();
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      new Thread(() -> System.out.println("Running!"));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
index e78aece..d2c3545 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
@@ -109,7 +109,7 @@
             parameters.isCfRuntime(), this::checkNoOutputSynthetics, this::checkOneOutputSynthetic)
         .inspectStackTrace(
             stackTrace -> {
-              int frames = parameters.isCfRuntime() ? 3 : 5;
+              int frames = parameters.isCfRuntime() ? 3 : 4;
               checkRawStackTraceFrameCount(stackTrace, frames, "Expected nothing to be inlined");
               checkExpectedStackTrace(stackTrace);
             });
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
index f391aff..f8a5171 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
@@ -299,12 +299,6 @@
                         null))
                 .build());
       }
-      builder.add(
-          ExternalStartupMethod.builder()
-              .setMethodReference(
-                  Reference.methodFromMethod(
-                      B.class.getDeclaredMethod("lambda$synthesize$0", Object.class)))
-              .build());
     }
     builder.add(
         ExternalStartupClass.builder().setClassReference(Reference.classFromClass(C.class)).build(),