[Ahead-of-time Subcomponents] Support for Optional bindings. This includes
support for replacing private binding methods with public modifiable binding
methods, and wrapping all modifiable bindings not already wrapped by a
modifiable binding method.

This also includes a consolidation of the functional tests.

RELNOTES=n/a

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=211634962
diff --git a/java/dagger/internal/codegen/BUILD b/java/dagger/internal/codegen/BUILD
index eac3f02..baf685e 100644
--- a/java/dagger/internal/codegen/BUILD
+++ b/java/dagger/internal/codegen/BUILD
@@ -227,7 +227,6 @@
 java_library(
     name = "writing",
     srcs = [
-        "AbstractMethodModifiableBindingExpression.java",
         "AnnotationCreatorGenerator.java",
         "BindingExpression.java",
         "BindingMethodImplementation.java",
@@ -267,8 +266,10 @@
         "MembersInjectorProviderCreationExpression.java",
         "MethodBindingExpression.java",
         "MissingBindingExpression.java",
+        "ModifiableAbstractMethodBindingExpression.java",
         "ModifiableBindingMethods.java",
         "ModifiableBindingType.java",
+        "ModifiableConcreteMethodBindingExpression.java",
         "MonitoringModuleGenerator.java",
         "MonitoringModuleProcessingStep.java",
         "OptionalBindingExpression.java",
diff --git a/java/dagger/internal/codegen/BindingExpression.java b/java/dagger/internal/codegen/BindingExpression.java
index b15852a..1f190f6 100644
--- a/java/dagger/internal/codegen/BindingExpression.java
+++ b/java/dagger/internal/codegen/BindingExpression.java
@@ -19,6 +19,7 @@
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
 
 /** A factory of code expressions used to access a single request for a binding in a component. */
 // TODO(user): Rename this to RequestExpression?
@@ -47,4 +48,13 @@
     // By default, just delegate to #getDependencyExpression().
     return CodeBlock.of("return $L;", getDependencyExpression(component.name()).codeBlock());
   }
+
+  /**
+   * Returns an expression for the implementation of a modifiable binding method for the given
+   * component model.
+   */
+  CodeBlock getModifiableBindingMethodImplementation(
+      ModifiableBindingMethod modifiableBindingMethod, GeneratedComponentModel component) {
+    return CodeBlock.of("return $L;", getDependencyExpression(component.name()).codeBlock());
+  }
 }
diff --git a/java/dagger/internal/codegen/ComponentBindingExpressions.java b/java/dagger/internal/codegen/ComponentBindingExpressions.java
index 8f4b5b3..beed699 100644
--- a/java/dagger/internal/codegen/ComponentBindingExpressions.java
+++ b/java/dagger/internal/codegen/ComponentBindingExpressions.java
@@ -43,6 +43,7 @@
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
 import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
 import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.model.BindingKind;
 import dagger.model.DependencyRequest;
 import dagger.model.Key;
 import dagger.model.RequestKind;
@@ -246,18 +247,27 @@
   Optional<MethodSpec> getComponentMethod(ComponentMethodDescriptor componentMethod) {
     checkArgument(componentMethod.dependencyRequest().isPresent());
     DependencyRequest dependencyRequest = componentMethod.dependencyRequest().get();
-    MethodSpec.Builder methodBuilder =
+    MethodSpec method =
         MethodSpec.overriding(
-            componentMethod.methodElement(),
-            MoreTypes.asDeclared(graph.componentType().asType()),
-            types);
+                componentMethod.methodElement(),
+                MoreTypes.asDeclared(graph.componentType().asType()),
+                types)
+            .addCode(
+                getBindingExpression(dependencyRequest.key(), dependencyRequest.kind())
+                    .getComponentMethodImplementation(componentMethod, generatedComponentModel))
+            .build();
 
-    ModifiableBindingType type =
+    ModifiableBindingType modifiableBindingType =
         getModifiableBindingType(dependencyRequest.key(), dependencyRequest.kind());
-    if (type.isModifiable()) {
+    if (modifiableBindingType.isModifiable()) {
       generatedComponentModel.registerModifiableBindingMethod(
-          type, dependencyRequest.key(), dependencyRequest.kind(), methodBuilder.build());
-      if (!type.hasBaseClassImplementation()) {
+          modifiableBindingType,
+          dependencyRequest.key(),
+          dependencyRequest.kind(),
+          method,
+          newModifiableBindingWillBeFinalized(
+              modifiableBindingType, dependencyRequest.key(), dependencyRequest.kind()));
+      if (!modifiableBindingType.hasBaseClassImplementation()) {
         // A component method should not be emitted if it encapsulates a modifiable binding that
         // cannot be satisfied by the abstract base class implementation of a subcomponent.
         checkState(
@@ -268,39 +278,71 @@
       }
     }
 
-    return Optional.of(
-        methodBuilder
-            .addCode(
-                getBindingExpression(dependencyRequest.key(), dependencyRequest.kind())
-                    .getComponentMethodImplementation(componentMethod, generatedComponentModel))
-            .build());
+    return Optional.of(method);
   }
 
   /**
-   * Returns the implementation of a method encapsulating a modifiable binding in a supertype
+   * Returns the implementation of a modifiable binding method originally defined in a supertype
    * implementation of this subcomponent. Returns {@link Optional#empty()} when the binding cannot
    * or should not be modified by the current binding graph. This is only relevant for ahead-of-time
    * subcomponents.
    */
-  Optional<MethodSpec> getModifiableBindingMethod(ModifiableBindingMethod modifiableBindingMethod) {
-    if (shouldOverrideModifiableBindingMethod(modifiableBindingMethod)) {
-      Expression bindingExpression =
-          getDependencyExpression(
-              modifiableBindingMethod.key(),
-              modifiableBindingMethod.kind(),
-              generatedComponentModel.name());
-      MethodSpec baseMethod = modifiableBindingMethod.baseMethod();
+  Optional<ModifiableBindingMethod> getModifiableBindingMethod(
+      ModifiableBindingMethod modifiableBindingMethod) {
+    if (shouldModifyKnownBinding(modifiableBindingMethod)) {
+      Key key = modifiableBindingMethod.key();
+      RequestKind requestKind = modifiableBindingMethod.kind();
+      MethodSpec baseMethod = modifiableBindingMethod.methodSpec();
       return Optional.of(
-          MethodSpec.methodBuilder(baseMethod.name)
-              .addModifiers(PUBLIC)
-              .returns(baseMethod.returnType)
-              .addAnnotation(Override.class)
-              .addStatement("return $L", bindingExpression.codeBlock())
-              .build());
+          ModifiableBindingMethod.implement(
+              modifiableBindingMethod,
+              MethodSpec.methodBuilder(baseMethod.name)
+                  .addModifiers(PUBLIC)
+                  .returns(baseMethod.returnType)
+                  .addAnnotation(Override.class)
+                  .addCode(
+                      getBindingExpression(key, requestKind)
+                          .getModifiableBindingMethodImplementation(
+                              modifiableBindingMethod, generatedComponentModel))
+                  .build(),
+              knownModifiableBindingWillBeFinalized(modifiableBindingMethod)));
     }
     return Optional.empty();
   }
 
+  /**
+   * Returns true if a modifiable binding method that was registered in a superclass implementation
+   * of this subcomponent should be marked as "finalized" if it is being overridden by this
+   * subcomponent implementation. "Finalized" means we should not attempt to modify the binding in
+   * any subcomponent subclass. This is only relevant for ahead-of-time subcomponents.
+   */
+  // TODO(user): extract a ModifiableBindingExpressions class? This may need some dependencies
+  // (like the GCM) but could remove some concerns from this class
+  private boolean knownModifiableBindingWillBeFinalized(
+      ModifiableBindingMethod modifiableBindingMethod) {
+    ModifiableBindingType newModifiableBindingType =
+        getModifiableBindingType(modifiableBindingMethod.key(), modifiableBindingMethod.kind());
+    if (!newModifiableBindingType.isModifiable()) {
+      // If a modifiable binding has become non-modifiable it is final by definition.
+      return true;
+    }
+    // All currently supported modifiable types are finalized upon modification.
+    return shouldModifyBinding(
+        newModifiableBindingType, modifiableBindingMethod.key(), modifiableBindingMethod.kind());
+  }
+
+  /**
+   * Returns true if a newly discovered modifiable binding method, once it is defined in this
+   * subcomponent implementation, should be marked as "finalized", meaning we should not attempt to
+   * modify the binding in any subcomponent subclass. This is only relevant for ahead-of-time
+   * subcomponents.
+   */
+  private boolean newModifiableBindingWillBeFinalized(
+      ModifiableBindingType modifiableBindingType, Key key, RequestKind requestKind) {
+    // All currently supported modifiable types are finalized upon modification.
+    return shouldModifyBinding(modifiableBindingType, key, requestKind);
+  }
+
   private BindingExpression getBindingExpression(Key key, RequestKind requestKind) {
     if (expressions.contains(key, requestKind)) {
       return expressions.get(key, requestKind);
@@ -352,13 +394,34 @@
    */
   private BindingExpression createModifiableBindingExpression(
       ModifiableBindingType type, Key key, RequestKind requestKind) {
+    ResolvedBindings resolvedBindings = graph.resolvedBindings(requestKind, key);
+    Optional<ModifiableBindingMethod> matchingModifiableBindingMethod =
+        generatedComponentModel.getModifiableBindingMethod(key, requestKind);
+    Optional<ComponentMethodDescriptor> matchingComponentMethod =
+        findMatchingComponentMethod(key, requestKind);
     switch (type) {
       case GENERATED_INSTANCE:
-        ResolvedBindings resolvedBindings = graph.resolvedBindings(requestKind, key);
         return new GeneratedInstanceBindingExpression(
-            generatedComponentModel, resolvedBindings, requestKind);
+            generatedComponentModel,
+            resolvedBindings,
+            requestKind,
+            matchingModifiableBindingMethod,
+            matchingComponentMethod);
       case MISSING:
-        return new MissingBindingExpression(generatedComponentModel, key, requestKind);
+        return new MissingBindingExpression(
+            generatedComponentModel,
+            key,
+            requestKind,
+            matchingModifiableBindingMethod,
+            matchingComponentMethod);
+      case OPTIONAL:
+        BindingExpression expression = createBindingExpression(resolvedBindings, requestKind);
+        // If the expression hasn't already been registered as a modifiable binding method then wrap
+        // the binding here.
+        if (!generatedComponentModel.getModifiableBindingMethod(key, requestKind).isPresent()) {
+          return wrapInMethod(resolvedBindings, requestKind, expression);
+        }
+        return expression;
       default:
         throw new IllegalStateException(
             String.format(
@@ -393,6 +456,10 @@
       if (binding.requiresGeneratedInstance()) {
         return ModifiableBindingType.GENERATED_INSTANCE;
       }
+
+      if (binding.kind().equals(BindingKind.OPTIONAL)) {
+        return ModifiableBindingType.OPTIONAL;
+      }
     } else if (!resolvableBinding(key, requestKind)) {
       return ModifiableBindingType.MISSING;
     }
@@ -403,11 +470,29 @@
 
   /**
    * Returns true if the current binding graph can, and should, modify a binding by overriding a
-   * modfiable binding method. This is only relevant for ahead-of-time subcomponents.
+   * modifiable binding method. This is only relevant for ahead-of-time subcomponents.
    */
-  private boolean shouldOverrideModifiableBindingMethod(
-      ModifiableBindingMethod modifiableBindingMethod) {
-    switch (modifiableBindingMethod.type()) {
+  private boolean shouldModifyKnownBinding(ModifiableBindingMethod modifiableBindingMethod) {
+    ModifiableBindingType newModifiableBindingType =
+        getModifiableBindingType(modifiableBindingMethod.key(), modifiableBindingMethod.kind());
+    if (!newModifiableBindingType.equals(modifiableBindingMethod.type())) {
+      // It is possible that a binding can change types, in which case we should always modify the
+      // binding.
+      return true;
+    }
+    return shouldModifyBinding(
+        modifiableBindingMethod.type(),
+        modifiableBindingMethod.key(),
+        modifiableBindingMethod.kind());
+  }
+
+  /**
+   * Returns true if the current binding graph can, and should, modify a binding by overriding a
+   * modifiable binding method. This is only relevant for ahead-of-time subcomponents.
+   */
+  private boolean shouldModifyBinding(
+      ModifiableBindingType modifiableBindingType, Key key, RequestKind requestKind) {
+    switch (modifiableBindingType) {
       case GENERATED_INSTANCE:
         return !generatedComponentModel.isAbstract();
       case MISSING:
@@ -415,12 +500,16 @@
         // ancestors satisfy missing bindings of their children with their own missing binding
         // methods so that we can minimize the cases where we need to reach into doubly-nested
         // descendant component implementations
-        return resolvableBinding(modifiableBindingMethod.key(), modifiableBindingMethod.kind());
+        return resolvableBinding(key, requestKind);
+      case OPTIONAL:
+        // Only override optional binding methods if we have a non-empty binding.
+        ResolvedBindings resolvedBindings = graph.resolvedBindings(requestKind, key);
+        return !resolvedBindings.contributionBinding().dependencies().isEmpty();
       default:
         throw new IllegalStateException(
             String.format(
                 "Overriding modifiable binding method with unsupported ModifiableBindingType [%s].",
-                modifiableBindingMethod.type()));
+                modifiableBindingType));
     }
   }
 
@@ -764,10 +853,10 @@
   /**
    * Returns {@code true} if the binding should use the static factory creation strategy.
    *
-   * In default mode, we always use the static factory creation strategy. In fastInit mode, we
+   * <p>In default mode, we always use the static factory creation strategy. In fastInit mode, we
    * prefer to use a SwitchingProvider instead of static factories in order to reduce class loading;
-   * however, we allow static factories that can reused across multiple bindings, e.g.
-   * {@code MapFactory} or {@code SetFactory}.
+   * however, we allow static factories that can reused across multiple bindings, e.g. {@code
+   * MapFactory} or {@code SetFactory}.
    */
   private boolean useStaticFactoryCreation(ContributionBinding binding) {
     return !(compilerOptions.experimentalAndroidMode2() || compilerOptions.fastInit())
@@ -791,8 +880,10 @@
 
   /**
    * Returns a binding expression that uses a given one as the body of a method that users call. If
-   * a component provision method matches it, it will be the method implemented. If not, a new
-   * private method will be written.
+   * a component provision method matches it, it will be the method implemented. If it does not
+   * match a component provision method and the binding is modifiable the a new public modifiable
+   * binding method will be written. If the binding doesn't match a component method nor is it
+   * modifiable, then a new private method will be written.
    */
   private BindingExpression wrapInMethod(
       ResolvedBindings resolvedBindings,
@@ -800,8 +891,24 @@
       BindingExpression bindingExpression) {
     BindingMethodImplementation methodImplementation =
         methodImplementation(resolvedBindings, requestKind, bindingExpression);
+    Optional<ComponentMethodDescriptor> matchingComponentMethod =
+        findMatchingComponentMethod(resolvedBindings.key(), requestKind);
 
-    return findMatchingComponentMethod(resolvedBindings.key(), requestKind)
+    ModifiableBindingType modifiableBindingType =
+        getModifiableBindingType(resolvedBindings.key(), requestKind);
+    if (modifiableBindingType.isModifiable() && !matchingComponentMethod.isPresent()) {
+      return new ModifiableConcreteMethodBindingExpression(
+          resolvedBindings,
+          requestKind,
+          modifiableBindingType,
+          methodImplementation,
+          generatedComponentModel,
+          generatedComponentModel.getModifiableBindingMethod(resolvedBindings.key(), requestKind),
+          newModifiableBindingWillBeFinalized(
+              modifiableBindingType, resolvedBindings.key(), requestKind));
+    }
+
+    return matchingComponentMethod
         .<BindingExpression>map(
             componentMethod ->
                 new ComponentMethodBindingExpression(
diff --git a/java/dagger/internal/codegen/ComponentModelBuilder.java b/java/dagger/internal/codegen/ComponentModelBuilder.java
index d3a3872..54b4fc9 100644
--- a/java/dagger/internal/codegen/ComponentModelBuilder.java
+++ b/java/dagger/internal/codegen/ComponentModelBuilder.java
@@ -317,6 +317,8 @@
 
   private void addSubcomponents() {
     for (BindingGraph subgraph : graph.subgraphs()) {
+      // TODO(b/72748365): Can an abstract inner subcomponent implementation be elided if it's
+      // totally empty?
       generatedComponentModel.addSubcomponent(
           subgraph.componentDescriptor(),
           generatedComponentModel.isAbstract()
@@ -596,9 +598,7 @@
           bindingExpressions
               .getModifiableBindingMethod(modifiableBindingMethod)
               .ifPresent(
-                  method ->
-                      generatedComponentModel.addImplementedModifiableBindingMethod(
-                          modifiableBindingMethod, method));
+                  method -> generatedComponentModel.addImplementedModifiableBindingMethod(method));
         }
       } else {
         super.addInterfaceMethods();
diff --git a/java/dagger/internal/codegen/GeneratedComponentModel.java b/java/dagger/internal/codegen/GeneratedComponentModel.java
index 6b21c5e..67cd215 100644
--- a/java/dagger/internal/codegen/GeneratedComponentModel.java
+++ b/java/dagger/internal/codegen/GeneratedComponentModel.java
@@ -263,8 +263,12 @@
    * ahead-of-time subcomponents.
    */
   void addModifiableBindingMethod(
-      ModifiableBindingType type, Key key, RequestKind kind, MethodSpec methodSpec) {
-    modifiableBindingMethods.addMethod(type, key, kind, methodSpec);
+      ModifiableBindingType type,
+      Key key,
+      RequestKind kind,
+      MethodSpec methodSpec,
+      boolean finalized) {
+    modifiableBindingMethods.addMethod(type, key, kind, methodSpec, finalized);
     methodSpecsMap.put(MethodSpecKind.MODIFIABLE_BINDING_METHOD, methodSpec);
   }
 
@@ -274,15 +278,18 @@
    * method, encapsulates a modifiable binding.
    */
   void registerModifiableBindingMethod(
-      ModifiableBindingType type, Key key, RequestKind kind, MethodSpec methodSpec) {
-    modifiableBindingMethods.addMethod(type, key, kind, methodSpec);
+      ModifiableBindingType type,
+      Key key,
+      RequestKind kind,
+      MethodSpec methodSpec,
+      boolean finalized) {
+    modifiableBindingMethods.addMethod(type, key, kind, methodSpec, finalized);
   }
 
   /** Adds the implementation for the given {@link ModifiableBindingMethod} to the component. */
-  void addImplementedModifiableBindingMethod(
-      ModifiableBindingMethod method, MethodSpec methodSpec) {
+  void addImplementedModifiableBindingMethod(ModifiableBindingMethod method) {
     modifiableBindingMethods.methodImplemented(method);
-    methodSpecsMap.put(MethodSpecKind.MODIFIABLE_BINDING_METHOD, methodSpec);
+    methodSpecsMap.put(MethodSpecKind.MODIFIABLE_BINDING_METHOD, method.methodSpec());
   }
 
   /** Adds the given type to the component. */
@@ -358,13 +365,25 @@
       ImmutableList<ModifiableBindingMethod> superclassModifiableBindingMethods =
           supermodel.get().getModifiableBindingMethods();
       superclassModifiableBindingMethods.stream()
-          .filter(method -> !modifiableBindingMethods.isFinalized(method))
+          .filter(method -> !modifiableBindingMethods.finalized(method))
           .forEach(modifiableBindingMethodsBuilder::add);
     }
-    modifiableBindingMethodsBuilder.addAll(modifiableBindingMethods.getMethods());
+    modifiableBindingMethodsBuilder.addAll(modifiableBindingMethods.getNonFinalizedMethods());
     return modifiableBindingMethodsBuilder.build();
   }
 
+  /**
+   * Returns the {@link ModifiableBindingMethod} for this subcomponent for the given binding, if it
+   * exists.
+   */
+  Optional<ModifiableBindingMethod> getModifiableBindingMethod(Key key, RequestKind requestKind) {
+    Optional<ModifiableBindingMethod> method = modifiableBindingMethods.getMethod(key, requestKind);
+    if (!method.isPresent() && supermodel.isPresent()) {
+      return supermodel.get().getModifiableBindingMethod(key, requestKind);
+    }
+    return method;
+  }
+
   /** Generates the component and returns the resulting {@link TypeSpec.Builder}. */
   TypeSpec.Builder generate() {
     fieldSpecsMap.asMap().values().forEach(component::addFields);
diff --git a/java/dagger/internal/codegen/GeneratedInstanceBindingExpression.java b/java/dagger/internal/codegen/GeneratedInstanceBindingExpression.java
index dffa052..90aa7c4 100644
--- a/java/dagger/internal/codegen/GeneratedInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/GeneratedInstanceBindingExpression.java
@@ -16,16 +16,19 @@
 
 package dagger.internal.codegen;
 
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
 import dagger.model.RequestKind;
+import java.util.Optional;
 
 /**
- * An {@link AbstractMethodModifiableBindingExpression} for a binding that requires an instance of a
+ * An {@link ModifiableAbstractMethodBindingExpression} for a binding that requires an instance of a
  * generated type. This expression is used in abstract implementations of a subcomponent when there
  * are no concrete definitions of generated types available. The (unimplemented) method is added to
  * the {@code GeneratedComponentModel} when this dependency expression is requested. The method is
  * overridden when generating the concrete implementation of an ancestor component.
  */
-final class GeneratedInstanceBindingExpression extends AbstractMethodModifiableBindingExpression {
+final class GeneratedInstanceBindingExpression extends ModifiableAbstractMethodBindingExpression {
   private final GeneratedComponentModel generatedComponentModel;
   private final ContributionBinding binding;
   private final RequestKind requestKind;
@@ -33,12 +36,16 @@
   GeneratedInstanceBindingExpression(
       GeneratedComponentModel generatedComponentModel,
       ResolvedBindings resolvedBindings,
-      RequestKind requestKind) {
+      RequestKind requestKind,
+      Optional<ModifiableBindingMethod> matchingModifiableBindingMethod,
+      Optional<ComponentMethodDescriptor> matchingComponentMethod) {
     super(
         generatedComponentModel,
         ModifiableBindingType.GENERATED_INSTANCE,
         resolvedBindings.key(),
-        requestKind);
+        requestKind,
+        matchingModifiableBindingMethod,
+        matchingComponentMethod);
     this.generatedComponentModel = generatedComponentModel;
     this.binding = resolvedBindings.contributionBinding();
     this.requestKind = requestKind;
diff --git a/java/dagger/internal/codegen/MissingBindingExpression.java b/java/dagger/internal/codegen/MissingBindingExpression.java
index dbc95e9..fd8f9b8 100644
--- a/java/dagger/internal/codegen/MissingBindingExpression.java
+++ b/java/dagger/internal/codegen/MissingBindingExpression.java
@@ -22,23 +22,36 @@
 import static dagger.internal.codegen.SourceFiles.simpleVariableName;
 
 import com.google.auto.common.MoreTypes;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
 import dagger.model.Key;
 import dagger.model.RequestKind;
+import java.util.Optional;
 
 /**
- * A {@link AbstractMethodModifiableBindingExpression} for a binding that is missing when generating
+ * A {@link ModifiableAbstractMethodBindingExpression} for a binding that is missing when generating
  * the abstract base class implementation of a subcomponent. The (unimplemented) method is added to
  * the {@link GeneratedComponentModel} when the dependency expression is requested. The method is
  * overridden when generating the implementation of an ancestor component.
  */
-final class MissingBindingExpression extends AbstractMethodModifiableBindingExpression {
+final class MissingBindingExpression extends ModifiableAbstractMethodBindingExpression {
   private final GeneratedComponentModel generatedComponentModel;
   private final Key key;
   private final RequestKind kind;
 
   MissingBindingExpression(
-      GeneratedComponentModel generatedComponentModel, Key key, RequestKind kind) {
-    super(generatedComponentModel, ModifiableBindingType.MISSING, key, kind);
+      GeneratedComponentModel generatedComponentModel,
+      Key key,
+      RequestKind kind,
+      Optional<ModifiableBindingMethod> matchingModifiableBindingMethod,
+      Optional<ComponentMethodDescriptor> matchingComponentMethod) {
+    super(
+        generatedComponentModel,
+        ModifiableBindingType.MISSING,
+        key,
+        kind,
+        matchingModifiableBindingMethod,
+        matchingComponentMethod);
     this.generatedComponentModel = generatedComponentModel;
     this.key = key;
     this.kind = kind;
diff --git a/java/dagger/internal/codegen/AbstractMethodModifiableBindingExpression.java b/java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java
similarity index 62%
rename from java/dagger/internal/codegen/AbstractMethodModifiableBindingExpression.java
rename to java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java
index 8d1efdb..77fc1ab 100644
--- a/java/dagger/internal/codegen/AbstractMethodModifiableBindingExpression.java
+++ b/java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java
@@ -24,8 +24,11 @@
 import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
 import dagger.model.Key;
 import dagger.model.RequestKind;
+import java.util.Optional;
 
 /**
  * A {@link BindingExpression} that invokes a method that encapsulates a binding that cannot be
@@ -34,42 +37,64 @@
  * expression is requested. The method is overridden when generating the implementation of an
  * ancestor component.
  */
-abstract class AbstractMethodModifiableBindingExpression extends BindingExpression {
+abstract class ModifiableAbstractMethodBindingExpression extends BindingExpression {
   private final GeneratedComponentModel generatedComponentModel;
   private final ModifiableBindingType modifiableBindingType;
   private final Key key;
   private final RequestKind kind;
-  private String methodName;
+  private Optional<String> methodName;
 
-  AbstractMethodModifiableBindingExpression(
+  ModifiableAbstractMethodBindingExpression(
       GeneratedComponentModel generatedComponentModel,
       ModifiableBindingType modifiableBindingType,
       Key key,
-      RequestKind kind) {
+      RequestKind kind,
+      Optional<ModifiableBindingMethod> matchingModifiableBindingMethod,
+      Optional<ComponentMethodDescriptor> matchingComponentMethod) {
     this.generatedComponentModel = generatedComponentModel;
     this.modifiableBindingType = modifiableBindingType;
     this.key = key;
     this.kind = kind;
+    this.methodName =
+        initializeMethodName(matchingComponentMethod, matchingModifiableBindingMethod);
+  }
+
+  /**
+   * If this binding corresponds to an existing component method, or a known modifiable binding
+   * method, use them to initialize the method name, which is a signal to call the existing method
+   * rather than emit an abstract method.
+   */
+  private static Optional<String> initializeMethodName(
+      Optional<ComponentMethodDescriptor> matchingComponentMethod,
+      Optional<ModifiableBindingMethod> matchingModifiableBindingMethod) {
+    if (matchingComponentMethod.isPresent()) {
+      return Optional.of(matchingComponentMethod.get().methodElement().getSimpleName().toString());
+    }
+    if (matchingModifiableBindingMethod.isPresent()) {
+      return Optional.of(matchingModifiableBindingMethod.get().methodSpec().name);
+    }
+    return Optional.empty();
   }
 
   @Override
   final Expression getDependencyExpression(ClassName requestingClass) {
     addUnimplementedMethod();
-    return Expression.create(key.type(), CodeBlock.of("$L()", methodName));
+    return Expression.create(key.type(), CodeBlock.of("$L()", methodName.get()));
   }
 
   private void addUnimplementedMethod() {
-    if (methodName == null) {
+    if (!methodName.isPresent()) {
       // Only add the method once in case of repeated references to the missing binding.
-      methodName = chooseMethodName();
+      methodName = Optional.of(chooseMethodName());
       generatedComponentModel.addModifiableBindingMethod(
           modifiableBindingType,
           key,
           kind,
-          MethodSpec.methodBuilder(methodName)
+          MethodSpec.methodBuilder(methodName.get())
               .addModifiers(PUBLIC, ABSTRACT)
               .returns(requestTypeName(kind, TypeName.get(key.type())))
-              .build());
+              .build(),
+          false /* finalized */);
     }
   }
 
diff --git a/java/dagger/internal/codegen/ModifiableBindingMethods.java b/java/dagger/internal/codegen/ModifiableBindingMethods.java
index 766031b..290621e 100644
--- a/java/dagger/internal/codegen/ModifiableBindingMethods.java
+++ b/java/dagger/internal/codegen/ModifiableBindingMethods.java
@@ -16,7 +16,9 @@
 
 package dagger.internal.codegen;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.DaggerStreams.toImmutableList;
 
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableList;
@@ -26,6 +28,7 @@
 import dagger.model.Key;
 import dagger.model.RequestKind;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -38,62 +41,73 @@
  * superclasses to know what binding methods to attempt to modify.
  */
 final class ModifiableBindingMethods {
-  private final Map<KeyAndKind, ModifiableBindingMethod> methods = Maps.newHashMap();
+  private final Map<KeyAndKind, ModifiableBindingMethod> methods = Maps.newLinkedHashMap();
   private final Set<KeyAndKind> finalizedMethods = Sets.newHashSet();
 
   /** Register a method encapsulating a modifiable binding. */
   void addMethod(
-      ModifiableBindingType type, Key key, RequestKind kind, MethodSpec unimplementedMethod) {
+      ModifiableBindingType type, Key key, RequestKind kind, MethodSpec method, boolean finalized) {
+    checkArgument(type.isModifiable());
     KeyAndKind keyAndKind = KeyAndKind.create(key, kind);
-    checkState(
-        !finalizedMethods.contains(keyAndKind),
-        "Adding a modifiable binding method for a binding that has been marked as finalized for "
-            + "the current subcomponent implementation. The binding is for a %s-%s of type %s.",
-        key,
-        kind,
-        type);
-    methods.put(keyAndKind, ModifiableBindingMethod.create(type, key, kind, unimplementedMethod));
+    if (finalized) {
+      finalizedMethods.add(keyAndKind);
+    }
+    methods.put(keyAndKind, ModifiableBindingMethod.create(type, key, kind, method, finalized));
   }
 
-  /** Returns all {@link ModifiableBindingMethod}s. */
-  ImmutableList<ModifiableBindingMethod> getMethods() {
-    // We will never add a modifiable binding method and mark it as having been finalized in the
-    // same instance of ModifiableBindingMethods, so there's no need to filter `methods` by
-    // `finalizedMethods`.
-    return ImmutableList.copyOf(methods.values());
+  /** Returns all {@link ModifiableBindingMethod}s that have not been marked as finalized. */
+  ImmutableList<ModifiableBindingMethod> getNonFinalizedMethods() {
+    return methods.values().stream().filter(m -> !m.finalized()).collect(toImmutableList());
+  }
+
+  /** Returns the {@link ModifiableBindingMethod} for the given binding if present. */
+  Optional<ModifiableBindingMethod> getMethod(Key key, RequestKind kind) {
+    return Optional.ofNullable(methods.get(KeyAndKind.create(key, kind)));
   }
 
   /**
    * Mark the {@link ModifiableBindingMethod} as having been implemented, thus modifying the
-   * binding. For those bindings that are finalized when modified, mark the binding as finalized,
-   * meaning it should no longer be modified.
+   * binding.
    */
   void methodImplemented(ModifiableBindingMethod method) {
-    if (method.type().finalizedOnModification()) {
-      KeyAndKind keyAndKind = KeyAndKind.create(method.key(), method.kind());
+    if (method.finalized()) {
       checkState(
-          !methods.containsKey(keyAndKind),
-          "Indicating a modifiable binding method is finalized when it was registered as "
-              + "modifiable for the current subcomponent implementation. The binding is for a "
-              + "%s-%s of type %s.",
+          finalizedMethods.add(KeyAndKind.create(method.key(), method.kind())),
+          "Implementing and finalizing a modifiable binding method that has been marked as "
+              + "finalized in the current subcomponent implementation. The binding is for a %s-%s "
+              + "of type %s.",
           method.key(),
           method.kind(),
           method.type());
-      finalizedMethods.add(keyAndKind);
     }
   }
 
   /** Whether a given binding has been marked as finalized. */
-  boolean isFinalized(ModifiableBindingMethod method) {
+  boolean finalized(ModifiableBindingMethod method) {
     return finalizedMethods.contains(KeyAndKind.create(method.key(), method.kind()));
   }
 
   @AutoValue
   abstract static class ModifiableBindingMethod {
     private static ModifiableBindingMethod create(
-        ModifiableBindingType type, Key key, RequestKind kind, MethodSpec unimplementedMethod) {
+        ModifiableBindingType type,
+        Key key,
+        RequestKind kind,
+        MethodSpec methodSpec,
+        boolean finalized) {
       return new AutoValue_ModifiableBindingMethods_ModifiableBindingMethod(
-          type, key, kind, unimplementedMethod);
+          type, key, kind, methodSpec, finalized);
+    }
+
+    /** Create a {@ModifiableBindingMethod} representing an implementation of an existing method. */
+    static ModifiableBindingMethod implement(
+        ModifiableBindingMethod unimplementedMethod, MethodSpec methodSpec, boolean finalized) {
+      return new AutoValue_ModifiableBindingMethods_ModifiableBindingMethod(
+          unimplementedMethod.type(),
+          unimplementedMethod.key(),
+          unimplementedMethod.kind(),
+          methodSpec,
+          finalized);
     }
 
     abstract ModifiableBindingType type();
@@ -102,7 +116,9 @@
 
     abstract RequestKind kind();
 
-    abstract MethodSpec baseMethod();
+    abstract MethodSpec methodSpec();
+
+    abstract boolean finalized();
   }
 
   @AutoValue
diff --git a/java/dagger/internal/codegen/ModifiableBindingType.java b/java/dagger/internal/codegen/ModifiableBindingType.java
index db8f723..d5e2e7c 100644
--- a/java/dagger/internal/codegen/ModifiableBindingType.java
+++ b/java/dagger/internal/codegen/ModifiableBindingType.java
@@ -72,8 +72,6 @@
   INJECTION,
   ;
 
-  private static final ImmutableSet<ModifiableBindingType> TYPES_FINALIZED_ON_MODIFICATION =
-      ImmutableSet.of(MISSING, GENERATED_INSTANCE, OPTIONAL, INJECTION);
   private static final ImmutableSet<ModifiableBindingType> TYPES_WITH_BASE_CLASS_IMPLEMENTATIONS =
       ImmutableSet.of(MULTIBINDING, OPTIONAL, INJECTION);
 
@@ -82,16 +80,6 @@
   }
 
   /**
-   * Returns true if the modifiable binding should not be further modified once it's base
-   * implementation has been overridden. For example, a missing or optional binding may only be
-   * satisfied once in a subcomponent implementation class hierarchy, but a multibinding may be
-   * modified with every implementation of a subcomponent.
-   */
-  boolean finalizedOnModification() {
-    return TYPES_FINALIZED_ON_MODIFICATION.contains(this);
-  }
-
-  /**
    * Returns true if the method encapsulating the modifiable binding should have a concrete
    * implementation in the abstract base class for a subcomponent.
    */
diff --git a/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java b/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java
new file mode 100644
index 0000000..15e3215
--- /dev/null
+++ b/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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;
+
+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 javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
+import dagger.model.RequestKind;
+import java.util.Optional;
+
+/**
+ * A binding expression that wraps a modifiable binding expression in a public, no-arg method.
+ *
+ * <p>Dependents of this binding expression will just call the modifiable binding method.
+ */
+final class ModifiableConcreteMethodBindingExpression extends MethodBindingExpression {
+  private final ContributionBinding binding;
+  private final RequestKind requestKind;
+  private final ModifiableBindingType modifiableBindingType;
+  private final BindingMethodImplementation methodImplementation;
+  private final GeneratedComponentModel generatedComponentModel;
+  private final boolean bindingFinalized;
+  private Optional<String> methodName;
+
+  ModifiableConcreteMethodBindingExpression(
+      ResolvedBindings resolvedBindings,
+      RequestKind requestKind,
+      ModifiableBindingType modifiableBindingType,
+      BindingMethodImplementation methodImplementation,
+      GeneratedComponentModel generatedComponentModel,
+      Optional<ModifiableBindingMethod> matchingModifiableBindingMethod,
+      boolean bindingFinalized) {
+    super(methodImplementation, generatedComponentModel);
+    this.binding = resolvedBindings.contributionBinding();
+    this.requestKind = checkNotNull(requestKind);
+    this.modifiableBindingType = checkNotNull(modifiableBindingType);
+    this.methodImplementation = checkNotNull(methodImplementation);
+    this.generatedComponentModel = checkNotNull(generatedComponentModel);
+    this.bindingFinalized = bindingFinalized;
+    this.methodName =
+        matchingModifiableBindingMethod.map(modifiableMethod -> modifiableMethod.methodSpec().name);
+  }
+
+  @Override
+  protected void addMethod() {
+    // Add the modifiable binding method to the component model if we haven't already.
+    if (!methodName.isPresent()) {
+      methodName =
+          Optional.of(generatedComponentModel.getUniqueGetterMethodName(binding, requestKind));
+      generatedComponentModel.addModifiableBindingMethod(
+          modifiableBindingType,
+          binding.key(),
+          requestKind,
+          methodBuilder(methodName.get())
+              .addModifiers(bindingFinalized ? PRIVATE : PUBLIC)
+              .returns(TypeName.get(methodImplementation.returnType()))
+              .addCode(methodImplementation.body())
+              .build(),
+          bindingFinalized);
+    }
+  }
+
+  @Override
+  protected String methodName() {
+    checkState(methodName.isPresent(), "addMethod() must be called before methodName().");
+    return methodName.get();
+  }
+}