Create an experimental mode that merges fast init mode and default mode.

RELNOTES=n/a
PiperOrigin-RevId: 423948272
diff --git a/java/dagger/internal/codegen/writing/ComponentImplementation.java b/java/dagger/internal/codegen/writing/ComponentImplementation.java
index 3351a93..e18c6c2 100644
--- a/java/dagger/internal/codegen/writing/ComponentImplementation.java
+++ b/java/dagger/internal/codegen/writing/ComponentImplementation.java
@@ -47,6 +47,7 @@
 
 import androidx.room.compiler.processing.XMessager;
 import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import androidx.room.compiler.processing.compat.XConverters;
 import com.google.auto.common.MoreElements;
 import com.google.auto.common.MoreTypes;
@@ -116,14 +117,18 @@
   }
 
   /** Compiler Modes. */
-  // TODO(wanyingd): add experimental merged mode.
   public enum CompilerMode {
     DEFAULT,
-    FAST_INIT;
+    FAST_INIT,
+    EXPERIMENTAL_MERGED_MODE;
 
     public boolean isFastInit() {
       return this == CompilerMode.FAST_INIT;
     }
+
+    public boolean isExperimentalMergedMode() {
+      return this == CompilerMode.EXPERIMENTAL_MERGED_MODE;
+    }
   }
 
   /** A type of field that this component can contain. */
@@ -310,10 +315,13 @@
     this.componentFieldsByImplementation =
         createComponentFieldsByImplementation(this, compilerOptions);
     this.messager = messager;
+    XTypeElement typeElement = rootComponentImplementation().componentDescriptor().typeElement();
     this.compilerMode =
-        compilerOptions.fastInit(rootComponentImplementation().componentDescriptor().typeElement())
+        compilerOptions.fastInit(typeElement)
             ? CompilerMode.FAST_INIT
-            : CompilerMode.DEFAULT;
+            : (compilerOptions.experimentalMergedMode(typeElement)
+                ? CompilerMode.EXPERIMENTAL_MERGED_MODE
+                : CompilerMode.DEFAULT);
   }
 
   /**
diff --git a/java/dagger/internal/codegen/writing/ComponentProvisionRequestRepresentation.java b/java/dagger/internal/codegen/writing/ComponentProvisionRequestRepresentation.java
index 93eab6a..31eecb2 100644
--- a/java/dagger/internal/codegen/writing/ComponentProvisionRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/ComponentProvisionRequestRepresentation.java
@@ -36,31 +36,43 @@
   private final BindingGraph bindingGraph;
   private final ComponentRequirementExpressions componentRequirementExpressions;
   private final CompilerOptions compilerOptions;
+  private final boolean isExperimentalMergedMode;
 
   @AssistedInject
   ComponentProvisionRequestRepresentation(
       @Assisted ProvisionBinding binding,
       BindingGraph bindingGraph,
+      ComponentImplementation componentImplementation,
       ComponentRequirementExpressions componentRequirementExpressions,
       CompilerOptions compilerOptions) {
     this.binding = binding;
     this.bindingGraph = bindingGraph;
     this.componentRequirementExpressions = componentRequirementExpressions;
     this.compilerOptions = compilerOptions;
+    this.isExperimentalMergedMode =
+        componentImplementation.compilerMode().isExperimentalMergedMode();
   }
 
   @Override
   Expression getDependencyExpression(ClassName requestingClass) {
+    CodeBlock componentDependency =
+        isExperimentalMergedMode
+            ? CodeBlock.of("(($T) dependencies[0])", componentRequirement().type().getTypeName())
+            : getComponentRequirementExpression(requestingClass);
     CodeBlock invocation =
         CodeBlock.of(
             "$L.$L()",
-            componentRequirementExpressions.getExpression(componentRequirement(), requestingClass),
+            componentDependency,
             toJavac(binding.bindingElement().get()).getSimpleName());
     return Expression.create(
         binding.contributedPrimitiveType().orElse(binding.key().type().xprocessing()),
         maybeCheckForNull(binding, compilerOptions, invocation));
   }
 
+  CodeBlock getComponentRequirementExpression(ClassName requestingClass) {
+    return componentRequirementExpressions.getExpression(componentRequirement(), requestingClass);
+  }
+
   private ComponentRequirement componentRequirement() {
     return bindingGraph
         .componentDescriptor()
diff --git a/java/dagger/internal/codegen/writing/ComponentRequestRepresentations.java b/java/dagger/internal/codegen/writing/ComponentRequestRepresentations.java
index 5ba0515..324cef6 100644
--- a/java/dagger/internal/codegen/writing/ComponentRequestRepresentations.java
+++ b/java/dagger/internal/codegen/writing/ComponentRequestRepresentations.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
 import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
 import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
@@ -65,8 +66,12 @@
       membersInjectionBindingRepresentationFactory;
   private final ProvisionBindingRepresentation.Factory provisionBindingRepresentationFactory;
   private final ProductionBindingRepresentation.Factory productionBindingRepresentationFactory;
+  private final ExperimentalSwitchingProviderDependencyRepresentation.Factory
+      experimentalSwitchingProviderDependencyRepresentationFactory;
   private final DaggerTypes types;
   private final Map<Binding, BindingRepresentation> representations = new HashMap<>();
+  private final Map<Binding, ExperimentalSwitchingProviderDependencyRepresentation>
+      experimentalSwitchingProviderDependencyRepresentations = new HashMap<>();
 
   @Inject
   ComponentRequestRepresentations(
@@ -77,6 +82,8 @@
       MembersInjectionBindingRepresentation.Factory membersInjectionBindingRepresentationFactory,
       ProvisionBindingRepresentation.Factory provisionBindingRepresentationFactory,
       ProductionBindingRepresentation.Factory productionBindingRepresentationFactory,
+      ExperimentalSwitchingProviderDependencyRepresentation.Factory
+          experimentalSwitchingProviderDependencyRepresentationFactory,
       DaggerTypes types) {
     this.parent = parent;
     this.graph = graph;
@@ -85,6 +92,8 @@
         membersInjectionBindingRepresentationFactory;
     this.provisionBindingRepresentationFactory = provisionBindingRepresentationFactory;
     this.productionBindingRepresentationFactory = productionBindingRepresentationFactory;
+    this.experimentalSwitchingProviderDependencyRepresentationFactory =
+        experimentalSwitchingProviderDependencyRepresentationFactory;
     this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
     this.types = types;
   }
@@ -222,4 +231,29 @@
     }
     throw new AssertionError();
   }
+
+  /**
+   * Returns an {@link ExperimentalSwitchingProviderDependencyRepresentation} for the requested
+   * binding to satisfy dependency requests on it from experimental switching providers. Cannot be
+   * used for Members Injection requests.
+   */
+  ExperimentalSwitchingProviderDependencyRepresentation
+      getExperimentalSwitchingProviderDependencyRepresentation(BindingRequest request) {
+    checkState(
+        componentImplementation.compilerMode().isExperimentalMergedMode(),
+        "Compiler mode should be experimentalMergedMode!");
+    Optional<Binding> localBinding = graph.localContributionBinding(request.key());
+
+    if (localBinding.isPresent()) {
+      return reentrantComputeIfAbsent(
+          experimentalSwitchingProviderDependencyRepresentations,
+          localBinding.get(),
+          binding ->
+              experimentalSwitchingProviderDependencyRepresentationFactory.create(
+                  (ProvisionBinding) binding));
+    }
+
+    checkArgument(parent.isPresent(), "no expression found for %s", request);
+    return parent.get().getExperimentalSwitchingProviderDependencyRepresentation(request);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviderDependencyRepresentation.java b/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviderDependencyRepresentation.java
new file mode 100644
index 0000000..817f76b
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviderDependencyRepresentation.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.binding.BindsTypeChecker;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import dagger.spi.model.BindingKind;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.RequestKind;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Returns type casted expressions to satisfy dependency requests from experimental switching
+ * providers.
+ */
+final class ExperimentalSwitchingProviderDependencyRepresentation {
+  private final ProvisionBinding binding;
+  private final ShardImplementation shardImplementation;
+  private final BindsTypeChecker bindsTypeChecker;
+  private final DaggerTypes types;
+  private final DaggerElements elements;
+  private final TypeMirror type;
+
+  @AssistedInject
+  ExperimentalSwitchingProviderDependencyRepresentation(
+      @Assisted ProvisionBinding binding,
+      ComponentImplementation componentImplementation,
+      DaggerTypes types,
+      DaggerElements elements) {
+    this.binding = binding;
+    this.shardImplementation = componentImplementation.shardImplementation(binding);
+    this.types = types;
+    this.elements = elements;
+    this.bindsTypeChecker = new BindsTypeChecker(types, elements);
+    this.type =
+        isDelegateSetValuesBinding()
+            ? types.erasure(elements.getTypeElement(TypeNames.COLLECTION).asType())
+            : toJavac(binding.contributedType());
+  }
+
+  Expression getDependencyExpression(RequestKind requestKind, ProvisionBinding requestingBinding) {
+    int index = findIndexOfDependency(requestingBinding);
+    TypeMirror frameworkType =
+        types.getDeclaredType(elements.getTypeElement(FrameworkType.PROVIDER.frameworkClassName()));
+    Expression expression =
+        FrameworkType.PROVIDER.to(
+            requestKind,
+            Expression.create(
+                frameworkType, CodeBlock.of("(($T) dependencies[$L])", frameworkType, index)),
+            types);
+    if (usesExplicitTypeCast(expression, requestKind)) {
+      return expression.castTo(type);
+    }
+    if (usesErasedTypeCast(requestKind)) {
+      return expression.castTo(types.erasure(type));
+    }
+    return expression;
+  }
+
+  private int findIndexOfDependency(ProvisionBinding requestingBinding) {
+    return requestingBinding.dependencies().stream()
+            .map(DependencyRequest::key)
+            .collect(toImmutableList())
+            .indexOf(binding.key())
+        + (requestingBinding.requiresModuleInstance()
+                && requestingBinding.contributingModule().isPresent()
+            ? 1
+            : 0);
+  }
+
+  private boolean isDelegateSetValuesBinding() {
+    return binding.kind().equals(BindingKind.DELEGATE)
+        && binding.contributionType().equals(ContributionType.SET_VALUES);
+  }
+
+  private boolean usesExplicitTypeCast(Expression expression, RequestKind requestKind) {
+    // If the type is accessible, we can directly cast the expression use the type.
+    return requestKind.equals(RequestKind.INSTANCE)
+        && !bindsTypeChecker.isAssignable(expression.type(), type, binding.contributionType())
+        && isTypeAccessibleFrom(type, shardImplementation.name().packageName());
+  }
+
+  private boolean usesErasedTypeCast(RequestKind requestKind) {
+    // If a type has inaccessible type arguments, then cast to raw type.
+    return requestKind.equals(RequestKind.INSTANCE)
+        && !isTypeAccessibleFrom(type, shardImplementation.name().packageName())
+        && isRawTypeAccessible(type, shardImplementation.name().packageName());
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    ExperimentalSwitchingProviderDependencyRepresentation create(ProvisionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviders.java b/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviders.java
new file mode 100644
index 0000000..679be6b
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviders.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getLast;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.spi.model.Key;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import javax.inject.Inject;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Keeps track of all provider expression requests for a component.
+ *
+ * <p>The provider expression request will be satisfied by a single generated {@code Provider} class
+ * that can provide instances for all types by switching on an id.
+ */
+@PerComponentImplementation
+final class ExperimentalSwitchingProviders {
+  /**
+   * Each switch size is fixed at 100 cases each and put in its own method. This is to limit the
+   * size of the methods so that we don't reach the "huge" method size limit for Android that will
+   * prevent it from being AOT compiled in some versions of Android (b/77652521). This generally
+   * starts to happen around 1500 cases, but we are choosing 100 to be safe.
+   */
+  // TODO(bcorso): Include a proguard_spec in the Dagger library to prevent inlining these methods?
+  // TODO(ronshapiro): Consider making this configurable via a flag.
+  private static final int MAX_CASES_PER_SWITCH = 100;
+
+  private static final long MAX_CASES_PER_CLASS = MAX_CASES_PER_SWITCH * MAX_CASES_PER_SWITCH;
+  private static final TypeVariableName T = TypeVariableName.get("T");
+
+  /**
+   * Maps a {@link Key} to an instance of a {@link SwitchingProviderBuilder}. Each group of {@code
+   * MAX_CASES_PER_CLASS} keys will share the same instance.
+   */
+  private final Map<Key, SwitchingProviderBuilder> switchingProviderBuilders =
+      new LinkedHashMap<>();
+
+  private final ShardImplementation componentShard;
+  private final DaggerTypes types;
+  private final UniqueNameSet switchingProviderNames = new UniqueNameSet();
+  private final ComponentRequestRepresentations componentRequestRepresentations;
+  private final ComponentImplementation componentImplementation;
+
+  @Inject
+  ExperimentalSwitchingProviders(
+      ComponentImplementation componentImplementation,
+      ComponentRequestRepresentations componentRequestRepresentations,
+      DaggerTypes types) {
+    this.componentShard = checkNotNull(componentImplementation).getComponentShard();
+    this.componentRequestRepresentations = componentRequestRepresentations;
+    this.componentImplementation = componentImplementation;
+    this.types = checkNotNull(types);
+  }
+
+  /** Returns the framework instance creation expression for an inner switching provider class. */
+  FrameworkInstanceCreationExpression newFrameworkInstanceCreationExpression(
+      ProvisionBinding binding, RequestRepresentation unscopedInstanceRequestRepresentation) {
+    return new FrameworkInstanceCreationExpression() {
+      @Override
+      public CodeBlock creationExpression() {
+        return switchingProviderBuilders
+            .computeIfAbsent(binding.key(), key -> getSwitchingProviderBuilder())
+            .getNewInstanceCodeBlock(binding, unscopedInstanceRequestRepresentation);
+      }
+    };
+  }
+
+  private SwitchingProviderBuilder getSwitchingProviderBuilder() {
+    if (switchingProviderBuilders.size() % MAX_CASES_PER_CLASS == 0) {
+      String name = switchingProviderNames.getUniqueName("SwitchingProvider");
+      // TODO(wanyingd): move Switching Providers and injection methods to Shard classes to avoid
+      // exceeding component class constant pool limit.
+      SwitchingProviderBuilder switchingProviderBuilder =
+          new SwitchingProviderBuilder(componentShard.name().nestedClass(name));
+      componentShard.addTypeSupplier(switchingProviderBuilder::build);
+      return switchingProviderBuilder;
+    }
+    return getLast(switchingProviderBuilders.values());
+  }
+
+  // TODO(bcorso): Consider just merging this class with ExperimentalSwitchingProviders.
+  private final class SwitchingProviderBuilder {
+    // Keep the switch cases ordered by switch id. The switch Ids are assigned in pre-order
+    // traversal, but the switch cases are assigned in post-order traversal of the binding graph.
+    private final Map<Integer, CodeBlock> switchCases = new TreeMap<>();
+    private final Map<Key, Integer> switchIds = new HashMap<>();
+    private final ClassName switchingProviderType;
+
+    SwitchingProviderBuilder(ClassName switchingProviderType) {
+      this.switchingProviderType = checkNotNull(switchingProviderType);
+    }
+
+    private CodeBlock getNewInstanceCodeBlock(
+        ProvisionBinding binding, RequestRepresentation unscopedInstanceRequestRepresentation) {
+      Key key = binding.key();
+      if (!switchIds.containsKey(key)) {
+        int switchId = switchIds.size();
+        switchIds.put(key, switchId);
+        switchCases.put(
+            switchId, createSwitchCaseCodeBlock(key, unscopedInstanceRequestRepresentation));
+      }
+
+      ShardImplementation shardImplementation =
+          componentImplementation.shardImplementation(binding);
+      CodeBlock switchingProviderDependencies;
+      switch (binding.kind()) {
+          // TODO(wanyingd): there might be a better way to get component requirement information
+          // without using unscopedInstanceRequestRepresentation.
+        case COMPONENT_PROVISION:
+          switchingProviderDependencies =
+              ((ComponentProvisionRequestRepresentation) unscopedInstanceRequestRepresentation)
+                  .getComponentRequirementExpression(shardImplementation.name());
+          break;
+        case SUBCOMPONENT_CREATOR:
+          switchingProviderDependencies =
+              ((SubcomponentCreatorRequestRepresentation) unscopedInstanceRequestRepresentation)
+                  .getDependencyExpressionArguments();
+          break;
+        case MULTIBOUND_SET:
+        case MULTIBOUND_MAP:
+        case OPTIONAL:
+        case INJECTION:
+        case PROVISION:
+        case ASSISTED_FACTORY:
+          // Arguments built in the order of module reference, provision dependencies and members
+          // injection dependencies
+          switchingProviderDependencies =
+              componentRequestRepresentations.getCreateMethodArgumentsCodeBlock(
+                  binding, shardImplementation.name());
+          break;
+        default:
+          throw new IllegalArgumentException("Unexpected binding kind: " + binding.kind());
+      }
+
+      TypeMirror castedType =
+          shardImplementation.accessibleType(toJavac(binding.contributedType()));
+      return CodeBlock.of(
+          "new $T<$L>($L)",
+          switchingProviderType,
+          // Add the type parameter explicitly when the binding is scoped because Java can't resolve
+          // the type when wrapped. For example, the following will error:
+          //   fooProvider = DoubleCheck.provider(new SwitchingProvider<>(1));
+          CodeBlock.of("$T", castedType),
+          switchingProviderDependencies.isEmpty()
+              ? CodeBlock.of("$L", switchIds.get(key))
+              : CodeBlock.of("$L, $L", switchIds.get(key), switchingProviderDependencies));
+    }
+
+    private CodeBlock createSwitchCaseCodeBlock(
+        Key key, RequestRepresentation unscopedInstanceRequestRepresentation) {
+      // TODO(bcorso): Try to delay calling getDependencyExpression() until we are writing out the
+      // SwitchingProvider because calling it here makes FrameworkFieldInitializer think there's a
+      // cycle when initializing ExperimentalSwitchingProviders which adds an unnecessary
+      // DelegateFactory.
+      CodeBlock instanceCodeBlock =
+          unscopedInstanceRequestRepresentation
+              .getDependencyExpression(switchingProviderType)
+              .box(types)
+              .codeBlock();
+
+      return CodeBlock.builder()
+          // TODO(bcorso): Is there something else more useful than the key?
+          .add("case $L: // $L \n", switchIds.get(key), key)
+          .addStatement("return ($T) $L", T, instanceCodeBlock)
+          .build();
+    }
+
+    private TypeSpec build() {
+      TypeSpec.Builder builder =
+          classBuilder(switchingProviderType)
+              .addModifiers(PRIVATE, FINAL, STATIC)
+              .addTypeVariable(T)
+              .addSuperinterface(providerOf(T))
+              .addMethods(getMethods());
+
+      // The SwitchingProvider constructor lists switch id first and then the dependency array.
+      MethodSpec.Builder constructor = MethodSpec.constructorBuilder();
+      builder.addField(TypeName.INT, "id", PRIVATE, FINAL);
+      constructor.addParameter(TypeName.INT, "id").addStatement("this.id = id");
+      // Pass in provision dependencies and members injection dependencies.
+      builder.addField(Object[].class, "dependencies", FINAL, PRIVATE);
+      constructor
+          .addParameter(Object[].class, "dependencies")
+          .addStatement("this.dependencies = dependencies")
+          .varargs(true);
+
+      return builder.addMethod(constructor.build()).build();
+    }
+
+    private ImmutableList<MethodSpec> getMethods() {
+      ImmutableList<CodeBlock> switchCodeBlockPartitions = switchCodeBlockPartitions();
+      if (switchCodeBlockPartitions.size() == 1) {
+        // There are less than MAX_CASES_PER_SWITCH cases, so no need for extra get methods.
+        return ImmutableList.of(
+            methodBuilder("get")
+                .addModifiers(PUBLIC)
+                .addAnnotation(suppressWarnings(UNCHECKED))
+                .addAnnotation(Override.class)
+                .returns(T)
+                .addCode(getOnlyElement(switchCodeBlockPartitions))
+                .build());
+      }
+
+      // This is the main public "get" method that will route to private getter methods.
+      MethodSpec.Builder routerMethod =
+          methodBuilder("get")
+              .addModifiers(PUBLIC)
+              .addAnnotation(Override.class)
+              .returns(T)
+              .beginControlFlow("switch (id / $L)", MAX_CASES_PER_SWITCH);
+
+      ImmutableList.Builder<MethodSpec> getMethods = ImmutableList.builder();
+      for (int i = 0; i < switchCodeBlockPartitions.size(); i++) {
+        MethodSpec method =
+            methodBuilder("get" + i)
+                .addModifiers(PRIVATE)
+                .addAnnotation(suppressWarnings(UNCHECKED))
+                .returns(T)
+                .addCode(switchCodeBlockPartitions.get(i))
+                .build();
+        getMethods.add(method);
+        routerMethod.addStatement("case $L: return $N()", i, method);
+      }
+
+      routerMethod.addStatement("default: throw new $T(id)", AssertionError.class).endControlFlow();
+
+      return getMethods.add(routerMethod.build()).build();
+    }
+
+    private ImmutableList<CodeBlock> switchCodeBlockPartitions() {
+      return Lists.partition(ImmutableList.copyOf(switchCases.values()), MAX_CASES_PER_SWITCH)
+          .stream()
+          .map(
+              partitionCases ->
+                  CodeBlock.builder()
+                      .beginControlFlow("switch (id)")
+                      .add(CodeBlocks.concat(partitionCases))
+                      .addStatement("default: throw new $T(id)", AssertionError.class)
+                      .endControlFlow()
+                      .build())
+          .collect(toImmutableList());
+    }
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/FrameworkInstanceKind.java b/java/dagger/internal/codegen/writing/FrameworkInstanceKind.java
index 94e9df0..8bceddc 100644
--- a/java/dagger/internal/codegen/writing/FrameworkInstanceKind.java
+++ b/java/dagger/internal/codegen/writing/FrameworkInstanceKind.java
@@ -16,18 +16,29 @@
 
 package dagger.internal.codegen.writing;
 
+import static dagger.spi.model.BindingKind.DELEGATE;
+
 import dagger.internal.codegen.binding.ContributionBinding;
 import dagger.internal.codegen.writing.ComponentImplementation.CompilerMode;
+import dagger.spi.model.BindingKind;
 
 /** Generation mode for satisfying framework request to Provision Binding. */
 enum FrameworkInstanceKind {
   SWITCHING_PROVIDER,
+  EXPERIMENTAL_SWITCHING_PROVIDER,
   STATIC_FACTORY,
   PROVIDER_FIELD;
 
   public static FrameworkInstanceKind from(ContributionBinding binding, CompilerMode compilerMode) {
     if (usesSwitchingProvider(binding, compilerMode)) {
-      return SWITCHING_PROVIDER;
+      if (compilerMode.isExperimentalMergedMode()) {
+        return EXPERIMENTAL_SWITCHING_PROVIDER;
+      } else if (compilerMode.isFastInit()) {
+        return SWITCHING_PROVIDER;
+      } else {
+        throw new IllegalStateException(
+            "Compiler mode " + compilerMode + " cannot use Switching Provider.");
+      }
     } else if (usesStaticFactoryCreation(binding, compilerMode)) {
       return STATIC_FACTORY;
     } else {
@@ -37,7 +48,12 @@
 
   private static boolean usesSwitchingProvider(
       ContributionBinding binding, CompilerMode compilerMode) {
-    if (!compilerMode.isFastInit()) {
+    if (!compilerMode.isFastInit() && !compilerMode.isExperimentalMergedMode()) {
+      return false;
+    }
+    // TODO(wanyingd): remove this check once we allow inaccessible types in merged mode.
+    if (compilerMode.isExperimentalMergedMode()
+        && binding.kind().equals(BindingKind.ASSISTED_FACTORY)) {
       return false;
     }
     switch (binding.kind()) {
@@ -83,9 +99,11 @@
       case MULTIBOUND_SET:
         return true;
       case PROVISION:
-        return !compilerMode.isFastInit() && !binding.requiresModuleInstance();
+        return !compilerMode.isFastInit()
+            && !compilerMode.isExperimentalMergedMode()
+            && !binding.requiresModuleInstance();
       case INJECTION:
-        return !compilerMode.isFastInit();
+        return !compilerMode.isFastInit() && !compilerMode.isExperimentalMergedMode();
       default:
         return false;
     }
diff --git a/java/dagger/internal/codegen/writing/MapRequestRepresentation.java b/java/dagger/internal/codegen/writing/MapRequestRepresentation.java
index 295fdd8..92ad920 100644
--- a/java/dagger/internal/codegen/writing/MapRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/MapRequestRepresentation.java
@@ -58,11 +58,13 @@
   private final ComponentRequestRepresentations componentRequestRepresentations;
   private final DaggerTypes types;
   private final DaggerElements elements;
+  private final boolean isExperimentalMergedMode;
 
   @AssistedInject
   MapRequestRepresentation(
       @Assisted ProvisionBinding binding,
       BindingGraph graph,
+      ComponentImplementation componentImplementation,
       ComponentRequestRepresentations componentRequestRepresentations,
       DaggerTypes types,
       DaggerElements elements) {
@@ -74,6 +76,8 @@
     this.elements = elements;
     this.dependencies =
         Maps.toMap(binding.dependencies(), dep -> graph.contributionBinding(dep.key()));
+    this.isExperimentalMergedMode =
+        componentImplementation.compilerMode().isExperimentalMergedMode();
   }
 
   @Override
@@ -138,9 +142,15 @@
     return CodeBlock.of(
         "$L, $L",
         getMapKeyExpression(dependencies.get(dependency), requestingClass, elements),
-        componentRequestRepresentations
-            .getDependencyExpression(bindingRequest(dependency), requestingClass)
-            .codeBlock());
+        isExperimentalMergedMode
+            ? componentRequestRepresentations
+                .getExperimentalSwitchingProviderDependencyRepresentation(
+                    bindingRequest(dependency))
+                .getDependencyExpression(dependency.kind(), binding)
+                .codeBlock()
+            : componentRequestRepresentations
+                .getDependencyExpression(bindingRequest(dependency), requestingClass)
+                .codeBlock());
   }
 
   private Expression collectionsStaticFactoryInvocation(
diff --git a/java/dagger/internal/codegen/writing/MembersInjectionMethods.java b/java/dagger/internal/codegen/writing/MembersInjectionMethods.java
index a3d42e9..ce332f5 100644
--- a/java/dagger/internal/codegen/writing/MembersInjectionMethods.java
+++ b/java/dagger/internal/codegen/writing/MembersInjectionMethods.java
@@ -17,12 +17,15 @@
 package dagger.internal.codegen.writing;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
 import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.MEMBERS_INJECTION_METHOD;
 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
 import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
 
 import com.google.common.collect.ImmutableSet;
 import com.squareup.javapoet.ClassName;
@@ -52,6 +55,7 @@
 @PerComponentImplementation
 final class MembersInjectionMethods {
   private final Map<Key, Expression> injectMethodExpressions = new LinkedHashMap<>();
+  private final Map<Key, Expression> experimentalInjectMethodExpressions = new LinkedHashMap<>();
   private final ComponentImplementation componentImplementation;
   private final ComponentRequestRepresentations bindingExpressions;
   private final BindingGraph graph;
@@ -86,7 +90,7 @@
             : graph.localContributionBinding(key).get();
     Expression expression =
         reentrantComputeIfAbsent(
-            injectMethodExpressions, key, k -> injectMethodExpression(binding));
+            injectMethodExpressions, key, k -> injectMethodExpression(binding, false));
     ShardImplementation shardImplementation = componentImplementation.shardImplementation(binding);
     return Expression.create(
         expression.type(),
@@ -99,8 +103,32 @@
                 instance));
   }
 
-  private Expression injectMethodExpression(Binding binding) {
-    ShardImplementation shardImplementation = componentImplementation.shardImplementation(binding);
+  /**
+   * Returns the members injection {@link Expression} for the given {@link Key}, creating it if
+   * necessary.
+   */
+  Expression getInjectExpressionExperimental(
+      ProvisionBinding provisionBinding, CodeBlock instance, ClassName requestingClass) {
+    checkState(
+        componentImplementation.compilerMode().isExperimentalMergedMode(),
+        "Compiler mode should be experimentalMergedMode!");
+    Expression expression =
+        reentrantComputeIfAbsent(
+            experimentalInjectMethodExpressions,
+            provisionBinding.key(),
+            k -> injectMethodExpression(provisionBinding, true));
+    return Expression.create(
+        expression.type(), CodeBlock.of("$L($L, dependencies)", expression.codeBlock(), instance));
+  }
+
+  private Expression injectMethodExpression(Binding binding, boolean useStaticInjectionMethod) {
+    // TODO(wanyingd): move Switching Providers and injection methods to Shard classes to avoid
+    // exceeding component class constant pool limit.
+    // Add to Component Shard so that is can be accessible from Switching Providers.
+    ShardImplementation shardImplementation =
+        useStaticInjectionMethod
+            ? componentImplementation.getComponentShard()
+            : componentImplementation.shardImplementation(binding);
     TypeMirror keyType = binding.key().type().java();
     TypeMirror membersInjectedType =
         isTypeAccessibleFrom(keyType, shardImplementation.name().packageName())
@@ -113,10 +141,16 @@
     String methodName = shardImplementation.getUniqueMethodName("inject" + bindingTypeName);
     ParameterSpec parameter = ParameterSpec.builder(membersInjectedTypeName, "instance").build();
     MethodSpec.Builder methodBuilder =
-        methodBuilder(methodName)
-            .addModifiers(PRIVATE)
-            .returns(membersInjectedTypeName)
-            .addParameter(parameter);
+        useStaticInjectionMethod
+            ? methodBuilder(methodName)
+                .addModifiers(PRIVATE, STATIC)
+                .returns(membersInjectedTypeName)
+                .addParameter(parameter)
+                .addParameter(Object[].class, "dependencies")
+            : methodBuilder(methodName)
+                .addModifiers(PRIVATE)
+                .returns(membersInjectedTypeName)
+                .addParameter(parameter);
     TypeElement canIgnoreReturnValue =
         elements.getTypeElement("com.google.errorprone.annotations.CanIgnoreReturnValue");
     if (canIgnoreReturnValue != null) {
@@ -130,8 +164,13 @@
             instance,
             membersInjectedType,
             request ->
-                bindingExpressions
-                    .getDependencyArgumentExpression(request, shardImplementation.name())
+                (useStaticInjectionMethod
+                        ? bindingExpressions
+                            .getExperimentalSwitchingProviderDependencyRepresentation(
+                                bindingRequest(request))
+                            .getDependencyExpression(request.kind(), (ProvisionBinding) binding)
+                        : bindingExpressions.getDependencyArgumentExpression(
+                            request, shardImplementation.name()))
                     .codeBlock(),
             types,
             metadataUtil));
diff --git a/java/dagger/internal/codegen/writing/OptionalRequestRepresentation.java b/java/dagger/internal/codegen/writing/OptionalRequestRepresentation.java
index 6b989e8..caef3a6 100644
--- a/java/dagger/internal/codegen/writing/OptionalRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/OptionalRequestRepresentation.java
@@ -39,10 +39,12 @@
   private final ComponentRequestRepresentations componentRequestRepresentations;
   private final DaggerTypes types;
   private final SourceVersion sourceVersion;
+  private final boolean isExperimentalMergedMode;
 
   @AssistedInject
   OptionalRequestRepresentation(
       @Assisted ProvisionBinding binding,
+      ComponentImplementation componentImplementation,
       ComponentRequestRepresentations componentRequestRepresentations,
       DaggerTypes types,
       SourceVersion sourceVersion) {
@@ -50,6 +52,8 @@
     this.componentRequestRepresentations = componentRequestRepresentations;
     this.types = types;
     this.sourceVersion = sourceVersion;
+    this.isExperimentalMergedMode =
+        componentImplementation.compilerMode().isExperimentalMergedMode();
   }
 
   @Override
@@ -74,9 +78,15 @@
     DependencyRequest dependency = getOnlyElement(binding.dependencies());
 
     CodeBlock dependencyExpression =
-        componentRequestRepresentations
-            .getDependencyExpression(bindingRequest(dependency), requestingClass)
-            .codeBlock();
+        isExperimentalMergedMode
+            ? componentRequestRepresentations
+                .getExperimentalSwitchingProviderDependencyRepresentation(
+                    bindingRequest(dependency))
+                .getDependencyExpression(dependency.kind(), binding)
+                .codeBlock()
+            : componentRequestRepresentations
+                .getDependencyExpression(bindingRequest(dependency), requestingClass)
+                .codeBlock();
 
     // If the dependency type is inaccessible, then we have to use Optional.<Object>of(...), or else
     // we will get "incompatible types: inference variable has incompatible bounds.
@@ -87,7 +97,7 @@
             types.erasure(binding.key().type().java()),
             optionalKind.presentObjectExpression(dependencyExpression));
   }
-  
+
   @AssistedFactory
   static interface Factory {
     OptionalRequestRepresentation create(ProvisionBinding binding);
diff --git a/java/dagger/internal/codegen/writing/ProvisionBindingRepresentation.java b/java/dagger/internal/codegen/writing/ProvisionBindingRepresentation.java
index 5cf34e1..0085d32 100644
--- a/java/dagger/internal/codegen/writing/ProvisionBindingRepresentation.java
+++ b/java/dagger/internal/codegen/writing/ProvisionBindingRepresentation.java
@@ -61,6 +61,7 @@
     FrameworkInstanceSupplier frameworkInstanceSupplier = null;
     switch (FrameworkInstanceKind.from(binding, compilerMode)) {
       case SWITCHING_PROVIDER:
+      case EXPERIMENTAL_SWITCHING_PROVIDER:
         frameworkInstanceSupplier = switchingProviderInstanceSupplierFactory.create(binding);
         break;
       case STATIC_FACTORY:
@@ -82,6 +83,9 @@
   }
 
   private boolean usesDirectInstanceExpression(RequestKind requestKind) {
+    if (compilerMode.isExperimentalMergedMode()) {
+      return false;
+    }
     if (requestKind != RequestKind.INSTANCE && requestKind != RequestKind.FUTURE) {
       return false;
     }
diff --git a/java/dagger/internal/codegen/writing/SetRequestRepresentation.java b/java/dagger/internal/codegen/writing/SetRequestRepresentation.java
index 15491d9..7fec86a 100644
--- a/java/dagger/internal/codegen/writing/SetRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/SetRequestRepresentation.java
@@ -51,11 +51,13 @@
   private final ComponentRequestRepresentations componentRequestRepresentations;
   private final DaggerTypes types;
   private final DaggerElements elements;
+  private final boolean isExperimentalMergedMode;
 
   @AssistedInject
   SetRequestRepresentation(
       @Assisted ProvisionBinding binding,
       BindingGraph graph,
+      ComponentImplementation componentImplementation,
       ComponentRequestRepresentations componentRequestRepresentations,
       DaggerTypes types,
       DaggerElements elements) {
@@ -64,6 +66,8 @@
     this.componentRequestRepresentations = componentRequestRepresentations;
     this.types = types;
     this.elements = elements;
+    this.isExperimentalMergedMode =
+        componentImplementation.compilerMode().isExperimentalMergedMode();
   }
 
   @Override
@@ -141,7 +145,15 @@
       DependencyRequest dependency, ClassName requestingClass) {
     RequestRepresentation bindingExpression =
         componentRequestRepresentations.getRequestRepresentation(bindingRequest(dependency));
-    CodeBlock expression = bindingExpression.getDependencyExpression(requestingClass).codeBlock();
+    CodeBlock expression =
+        isExperimentalMergedMode
+            ? componentRequestRepresentations
+                .getExperimentalSwitchingProviderDependencyRepresentation(
+                    bindingRequest(dependency))
+                .getDependencyExpression(dependency.kind(), binding)
+                .codeBlock()
+            : bindingExpression.getDependencyExpression(requestingClass).codeBlock();
+
     // TODO(b/211774331): Type casting should be Set after contributions to Set multibinding are
     // limited to be Set.
     // Add a cast to "(Collection)" when the contribution is a raw "Provider" type because the
diff --git a/java/dagger/internal/codegen/writing/SimpleMethodRequestRepresentation.java b/java/dagger/internal/codegen/writing/SimpleMethodRequestRepresentation.java
index c89c6b7..20c6ac6 100644
--- a/java/dagger/internal/codegen/writing/SimpleMethodRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/SimpleMethodRequestRepresentation.java
@@ -20,6 +20,7 @@
 import static com.google.auto.common.MoreElements.asExecutable;
 import static com.google.auto.common.MoreElements.asType;
 import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
 import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
@@ -60,6 +61,7 @@
   private final SourceVersion sourceVersion;
   private final KotlinMetadataUtil metadataUtil;
   private final ShardImplementation shardImplementation;
+  private final boolean isExperimentalMergedMode;
 
   @AssistedInject
   SimpleMethodRequestRepresentation(
@@ -70,7 +72,8 @@
       ComponentRequirementExpressions componentRequirementExpressions,
       SourceVersion sourceVersion,
       KotlinMetadataUtil metadataUtil,
-      ComponentImplementation componentImplementation) {
+      ComponentImplementation componentImplementation,
+      ExperimentalSwitchingProviders switchingProviders) {
     this.compilerOptions = compilerOptions;
     this.provisionBinding = binding;
     this.metadataUtil = metadataUtil;
@@ -83,6 +86,8 @@
     this.componentRequirementExpressions = componentRequirementExpressions;
     this.sourceVersion = sourceVersion;
     this.shardImplementation = componentImplementation.shardImplementation(binding);
+    this.isExperimentalMergedMode =
+        componentImplementation.compilerMode().isExperimentalMergedMode();
   }
 
   @Override
@@ -153,8 +158,12 @@
   }
 
   private Expression dependencyArgument(DependencyRequest dependency, ClassName requestingClass) {
-    return componentRequestRepresentations.getDependencyArgumentExpression(
-        dependency, requestingClass);
+    return isExperimentalMergedMode
+        ? componentRequestRepresentations
+            .getExperimentalSwitchingProviderDependencyRepresentation(bindingRequest(dependency))
+            .getDependencyExpression(dependency.kind(), provisionBinding)
+        : componentRequestRepresentations.getDependencyArgumentExpression(
+            dependency, requestingClass);
   }
 
   private Expression injectMembers(CodeBlock instance, ClassName requestingClass) {
@@ -172,8 +181,11 @@
         instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance);
       }
     }
-    return membersInjectionMethods.getInjectExpression(
-        provisionBinding.key(), instance, requestingClass);
+    return isExperimentalMergedMode
+        ? membersInjectionMethods.getInjectExpressionExperimental(
+            provisionBinding, instance, requestingClass)
+        : membersInjectionMethods.getInjectExpression(
+            provisionBinding.key(), instance, requestingClass);
   }
 
   private Optional<CodeBlock> moduleReference(ClassName requestingClass) {
@@ -182,7 +194,11 @@
             .contributingModule()
             .map(XTypeElement::getType)
             .map(ComponentRequirement::forModule)
-            .map(module -> componentRequirementExpressions.getExpression(module, requestingClass))
+            .map(
+                module ->
+                    isExperimentalMergedMode
+                        ? CodeBlock.of("(($T) dependencies[0])", module.type().getTypeName())
+                        : componentRequirementExpressions.getExpression(module, requestingClass))
         : Optional.empty();
   }
 
diff --git a/java/dagger/internal/codegen/writing/SubcomponentCreatorRequestRepresentation.java b/java/dagger/internal/codegen/writing/SubcomponentCreatorRequestRepresentation.java
index f441532..74ebbad 100644
--- a/java/dagger/internal/codegen/writing/SubcomponentCreatorRequestRepresentation.java
+++ b/java/dagger/internal/codegen/writing/SubcomponentCreatorRequestRepresentation.java
@@ -16,26 +16,33 @@
 
 package dagger.internal.codegen.writing;
 
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
 import dagger.assisted.Assisted;
 import dagger.assisted.AssistedFactory;
 import dagger.assisted.AssistedInject;
 import dagger.internal.codegen.binding.ContributionBinding;
-import dagger.internal.codegen.javapoet.CodeBlocks;
 import dagger.internal.codegen.javapoet.Expression;
 import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import java.util.ArrayList;
+import java.util.List;
 
 /** A binding expression for a subcomponent creator that just invokes the constructor. */
 final class SubcomponentCreatorRequestRepresentation extends RequestRepresentation {
   private final ShardImplementation shardImplementation;
   private final ContributionBinding binding;
+  private final boolean isExperimentalMergedMode;
 
   @AssistedInject
   SubcomponentCreatorRequestRepresentation(
       @Assisted ContributionBinding binding, ComponentImplementation componentImplementation) {
     this.binding = binding;
     this.shardImplementation = componentImplementation.shardImplementation(binding);
+    this.isExperimentalMergedMode =
+        componentImplementation.compilerMode().isExperimentalMergedMode();
   }
 
   @Override
@@ -44,9 +51,26 @@
         binding.key().type().java(),
         "new $T($L)",
         shardImplementation.getSubcomponentCreatorSimpleName(binding.key()),
-        shardImplementation.componentFieldsByImplementation().values().stream()
-            .map(field -> CodeBlock.of("$N", field))
-            .collect(CodeBlocks.toParametersCodeBlock()));
+        isExperimentalMergedMode
+            ? getDependenciesExperimental()
+            : shardImplementation.componentFieldsByImplementation().values().stream()
+                .map(field -> CodeBlock.of("$N", field))
+                .collect(toParametersCodeBlock()));
+  }
+
+  private CodeBlock getDependenciesExperimental() {
+    List<CodeBlock> expressions = new ArrayList<>();
+    int index = 0;
+    for (FieldSpec field : shardImplementation.componentFieldsByImplementation().values()) {
+      expressions.add(CodeBlock.of("($T) dependencies[$L]", field.type, index++));
+    }
+    return expressions.stream().collect(toParametersCodeBlock());
+  }
+
+  CodeBlock getDependencyExpressionArguments() {
+    return shardImplementation.componentFieldsByImplementation().values().stream()
+        .map(field -> CodeBlock.of("$N", field))
+        .collect(toParametersCodeBlock());
   }
 
   @AssistedFactory
diff --git a/java/dagger/internal/codegen/writing/SwitchingProviderInstanceSupplier.java b/java/dagger/internal/codegen/writing/SwitchingProviderInstanceSupplier.java
index 0d81c4a..ef6bd49 100644
--- a/java/dagger/internal/codegen/writing/SwitchingProviderInstanceSupplier.java
+++ b/java/dagger/internal/codegen/writing/SwitchingProviderInstanceSupplier.java
@@ -40,13 +40,17 @@
   SwitchingProviderInstanceSupplier(
       @Assisted ProvisionBinding binding,
       SwitchingProviders switchingProviders,
+      ExperimentalSwitchingProviders experimentalSwitchingProviders,
       BindingGraph graph,
       ComponentImplementation componentImplementation,
       UnscopedDirectInstanceRequestRepresentationFactory
           unscopedDirectInstanceRequestRepresentationFactory) {
     FrameworkInstanceCreationExpression frameworkInstanceCreationExpression =
-        switchingProviders.newFrameworkInstanceCreationExpression(
-            binding, unscopedDirectInstanceRequestRepresentationFactory.create(binding));
+        componentImplementation.compilerMode().isExperimentalMergedMode()
+            ? experimentalSwitchingProviders.newFrameworkInstanceCreationExpression(
+                binding, unscopedDirectInstanceRequestRepresentationFactory.create(binding))
+            : switchingProviders.newFrameworkInstanceCreationExpression(
+                binding, unscopedDirectInstanceRequestRepresentationFactory.create(binding));
     this.frameworkInstanceSupplier =
         new FrameworkFieldInitializer(
             componentImplementation, binding, scope(binding, frameworkInstanceCreationExpression));