[Ahead-of-time Subcomponents] Introduce ModifiableBindingType to classify
modifiable bindings across subcomponent implementations. Recast missing
bindings and generated instance bindings as "modifiable bindings". Add some
additional support for generated instance bindings.

RELNOTES=n/a

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=209645096
diff --git a/java/dagger/internal/codegen/AbstractMethodModifiableBindingExpression.java b/java/dagger/internal/codegen/AbstractMethodModifiableBindingExpression.java
new file mode 100644
index 0000000..8d1efdb
--- /dev/null
+++ b/java/dagger/internal/codegen/AbstractMethodModifiableBindingExpression.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 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 dagger.internal.codegen.RequestKinds.requestTypeName;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+
+/**
+ * A {@link BindingExpression} that invokes a method that encapsulates a binding that cannot be
+ * satisfied 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.
+ */
+abstract class AbstractMethodModifiableBindingExpression extends BindingExpression {
+  private final GeneratedComponentModel generatedComponentModel;
+  private final ModifiableBindingType modifiableBindingType;
+  private final Key key;
+  private final RequestKind kind;
+  private String methodName;
+
+  AbstractMethodModifiableBindingExpression(
+      GeneratedComponentModel generatedComponentModel,
+      ModifiableBindingType modifiableBindingType,
+      Key key,
+      RequestKind kind) {
+    this.generatedComponentModel = generatedComponentModel;
+    this.modifiableBindingType = modifiableBindingType;
+    this.key = key;
+    this.kind = kind;
+  }
+
+  @Override
+  final Expression getDependencyExpression(ClassName requestingClass) {
+    addUnimplementedMethod();
+    return Expression.create(key.type(), CodeBlock.of("$L()", methodName));
+  }
+
+  private void addUnimplementedMethod() {
+    if (methodName == null) {
+      // Only add the method once in case of repeated references to the missing binding.
+      methodName = chooseMethodName();
+      generatedComponentModel.addModifiableBindingMethod(
+          modifiableBindingType,
+          key,
+          kind,
+          MethodSpec.methodBuilder(methodName)
+              .addModifiers(PUBLIC, ABSTRACT)
+              .returns(requestTypeName(kind, TypeName.get(key.type())))
+              .build());
+    }
+  }
+
+  /** Returns a unique 'getter' method name for the current component. */
+  abstract String chooseMethodName();
+}
diff --git a/java/dagger/internal/codegen/BUILD b/java/dagger/internal/codegen/BUILD
index f2e4139..ee0887e 100644
--- a/java/dagger/internal/codegen/BUILD
+++ b/java/dagger/internal/codegen/BUILD
@@ -227,6 +227,7 @@
 java_library(
     name = "writing",
     srcs = [
+        "AbstractMethodModifiableBindingExpression.java",
         "AnnotationCreatorGenerator.java",
         "BindingExpression.java",
         "BindingMethodImplementation.java",
@@ -266,7 +267,8 @@
         "MembersInjectorProviderCreationExpression.java",
         "MethodBindingExpression.java",
         "MissingBindingExpression.java",
-        "MissingBindingMethods.java",
+        "ModifiableBindingMethods.java",
+        "ModifiableBindingType.java",
         "MonitoringModuleGenerator.java",
         "MonitoringModuleProcessingStep.java",
         "OptionalBindingExpression.java",
diff --git a/java/dagger/internal/codegen/ComponentBindingExpressions.java b/java/dagger/internal/codegen/ComponentBindingExpressions.java
index 50a11f8..f9c081f 100644
--- a/java/dagger/internal/codegen/ComponentBindingExpressions.java
+++ b/java/dagger/internal/codegen/ComponentBindingExpressions.java
@@ -41,7 +41,7 @@
 import com.squareup.javapoet.MethodSpec;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
 import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import dagger.internal.codegen.MissingBindingMethods.MissingBindingMethod;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
 import dagger.model.DependencyRequest;
 import dagger.model.Key;
 import dagger.model.RequestKind;
@@ -251,25 +251,23 @@
   }
 
   /**
-   * Returns the implementation of a method encapsulating a missing binding in a supertype
+   * Returns the implementation of a method encapsulating a modifiable binding in a supertype
    * implementation of this subcomponent. Returns {@link Optional#empty()} when the binding cannot
-   * be satisfied by the current binding graph. This is only relevant for ahead-of-time
+   * or should not be modified by the current binding graph. This is only relevant for ahead-of-time
    * subcomponents.
    */
-  Optional<MethodSpec> getMissingBindingMethodImplementation(MissingBindingMethod missingBinding) {
-    // TODO(b/72748365): investigate beder@'s comment about having intermediate component 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
-    if (resolvableBinding(missingBinding.key(), missingBinding.kind())) {
+  Optional<MethodSpec> getModifiableBindingMethod(ModifiableBindingMethod modifiableBindingMethod) {
+    if (shouldOverrideModifiableBindingMethod(modifiableBindingMethod)) {
       Expression bindingExpression =
           getDependencyExpression(
-              missingBinding.key(), missingBinding.kind(), generatedComponentModel.name());
-      MethodSpec unimplementedMethod = missingBinding.unimplementedMethod();
+              modifiableBindingMethod.key(),
+              modifiableBindingMethod.kind(),
+              generatedComponentModel.name());
+      MethodSpec baseMethod = modifiableBindingMethod.baseMethod();
       return Optional.of(
-          MethodSpec.methodBuilder(unimplementedMethod.name)
+          MethodSpec.methodBuilder(baseMethod.name)
               .addModifiers(PUBLIC)
-              .returns(unimplementedMethod.returnType)
+              .returns(baseMethod.returnType)
               .addAnnotation(Override.class)
               .addStatement("return $L", bindingExpression.codeBlock())
               .build());
@@ -282,12 +280,13 @@
       return expressions.get(key, requestKind);
     }
     Optional<BindingExpression> expression = Optional.empty();
-    if (resolvedInThisComponent(key, requestKind)) {
+    ModifiableBindingType modifiableBindingType = getModifiableBindingType(key, requestKind);
+    if (modifiableBindingType.isModifiable()) {
+      expression =
+          Optional.of(createModifiableBindingExpression(modifiableBindingType, key, requestKind));
+    } else if (resolvedInThisComponent(key, requestKind)) {
       ResolvedBindings resolvedBindings = graph.resolvedBindings(requestKind, key);
       expression = Optional.of(createBindingExpression(resolvedBindings, requestKind));
-    } else if (!resolvableBinding(key, requestKind) && generatedComponentModel.isAbstract()) {
-      expression =
-          Optional.of(new MissingBindingExpression(generatedComponentModel, key, requestKind));
     }
     if (expression.isPresent()) {
       expressions.put(key, requestKind, expression.get());
@@ -300,9 +299,6 @@
   /** Creates a binding expression. */
   private BindingExpression createBindingExpression(
       ResolvedBindings resolvedBindings, RequestKind requestKind) {
-    if (generatedInstanceForAbstractSubcomponent(resolvedBindings)) {
-      return new GeneratedInstanceBindingExpression(resolvedBindings);
-    }
     switch (resolvedBindings.bindingType()) {
       case MEMBERS_INJECTION:
         checkArgument(requestKind.equals(RequestKind.MEMBERS_INJECTION));
@@ -325,13 +321,81 @@
   }
 
   /**
-   * Returns true if the binding exposes an instance of a generated type, but no concrete
-   * implementation of that type is available.
+   * Creates a binding expression for a binding that may be modified across implementations of a
+   * subcomponent. This is only relevant for ahead-of-time subcomponents.
    */
-  private boolean generatedInstanceForAbstractSubcomponent(ResolvedBindings resolvedBindings) {
-    return !resolvedBindings.contributionBindings().isEmpty()
-        && resolvedBindings.contributionBinding().requiresGeneratedInstance()
-        && generatedComponentModel.isAbstract();
+  private BindingExpression createModifiableBindingExpression(
+      ModifiableBindingType type, Key key, RequestKind requestKind) {
+    switch (type) {
+      case GENERATED_INSTANCE:
+        ResolvedBindings resolvedBindings = graph.resolvedBindings(requestKind, key);
+        return new GeneratedInstanceBindingExpression(
+            generatedComponentModel, resolvedBindings, requestKind);
+      case MISSING:
+        return new MissingBindingExpression(generatedComponentModel, key, requestKind);
+      default:
+        throw new IllegalStateException(
+            String.format(
+                "Building binding expression for unsupported ModifiableBindingType [%s].", type));
+    }
+  }
+
+  /**
+   * The reason why a binding may need to be modified across implementations of a subcomponent, if
+   * at all. This is only relevant for ahead-of-time subcomponents.
+   */
+  private ModifiableBindingType getModifiableBindingType(Key key, RequestKind requestKind) {
+    if (!compilerOptions.aheadOfTimeSubcomponents()) {
+      return ModifiableBindingType.NONE;
+    }
+
+    // When generating a final (concrete) implementation of a (sub)component the binding is no
+    // longer considered modifiable. It cannot be further modified by a subclass implementation.
+    if (!generatedComponentModel.isAbstract()) {
+      return ModifiableBindingType.NONE;
+    }
+
+    if (resolvedInThisComponent(key, requestKind)) {
+      ResolvedBindings resolvedBindings = graph.resolvedBindings(requestKind, key);
+      if (resolvedBindings.contributionBindings().isEmpty()) {
+        // TODO(ronshapiro): Confirm whether a resolved binding must have a single contribution
+        // binding.
+        return ModifiableBindingType.NONE;
+      }
+
+      ContributionBinding binding = resolvedBindings.contributionBinding();
+      if (binding.requiresGeneratedInstance()) {
+        return ModifiableBindingType.GENERATED_INSTANCE;
+      }
+    } else if (!resolvableBinding(key, requestKind)) {
+      return ModifiableBindingType.MISSING;
+    }
+
+    // TODO(b/72748365): Add support for remaining types.
+    return ModifiableBindingType.NONE;
+  }
+
+  /**
+   * 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.
+   */
+  private boolean shouldOverrideModifiableBindingMethod(
+      ModifiableBindingMethod modifiableBindingMethod) {
+    switch (modifiableBindingMethod.type()) {
+      case GENERATED_INSTANCE:
+        return !generatedComponentModel.isAbstract();
+      case MISSING:
+        // TODO(b/72748365): investigate beder@'s comment about having intermediate component
+        // 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());
+      default:
+        throw new IllegalStateException(
+            String.format(
+                "Overriding modifiable binding method with unsupported ModifiableBindingType [%s].",
+                modifiableBindingMethod.type()));
+    }
   }
 
   /**
diff --git a/java/dagger/internal/codegen/ComponentModelBuilder.java b/java/dagger/internal/codegen/ComponentModelBuilder.java
index 4716b3d..b38df3e 100644
--- a/java/dagger/internal/codegen/ComponentModelBuilder.java
+++ b/java/dagger/internal/codegen/ComponentModelBuilder.java
@@ -48,7 +48,7 @@
 import com.squareup.javapoet.ParameterSpec;
 import com.squareup.javapoet.TypeSpec;
 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.MissingBindingMethods.MissingBindingMethod;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
 import java.util.List;
 import java.util.Optional;
 import javax.lang.model.element.ExecutableElement;
@@ -589,15 +589,15 @@
         // Since we're overriding a subcomponent implementation we add to its implementation given
         // an expanded binding graph.
 
-        // Implement missing binding methods.
-        for (MissingBindingMethod missingBindingMethod :
-            generatedComponentModel.getMissingBindingMethods()) {
+        // Override modifiable binding methods.
+        for (ModifiableBindingMethod modifiableBindingMethod :
+            generatedComponentModel.getModifiableBindingMethods()) {
           bindingExpressions
-              .getMissingBindingMethodImplementation(missingBindingMethod)
+              .getModifiableBindingMethod(modifiableBindingMethod)
               .ifPresent(
                   method ->
-                      generatedComponentModel.addImplementedMissingBindingMethod(
-                          missingBindingMethod, method));
+                      generatedComponentModel.addImplementedModifiableBindingMethod(
+                          modifiableBindingMethod, method));
         }
       } else {
         super.addInterfaceMethods();
diff --git a/java/dagger/internal/codegen/GeneratedComponentModel.java b/java/dagger/internal/codegen/GeneratedComponentModel.java
index d0dac71..c50ad9c 100644
--- a/java/dagger/internal/codegen/GeneratedComponentModel.java
+++ b/java/dagger/internal/codegen/GeneratedComponentModel.java
@@ -16,6 +16,9 @@
 
 package dagger.internal.codegen;
 
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
 import static com.google.common.base.Preconditions.checkState;
 import static com.squareup.javapoet.TypeSpec.classBuilder;
 import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
@@ -34,7 +37,7 @@
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.TypeSpec;
 import dagger.internal.ReferenceReleasingProviderManager;
-import dagger.internal.codegen.MissingBindingMethods.MissingBindingMethod;
+import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
 import dagger.model.Key;
 import dagger.model.RequestKind;
 import java.util.ArrayList;
@@ -101,10 +104,12 @@
     ABSENT_OPTIONAL_METHOD,
 
     /**
-     * A method encapsulating a missing binding to be overridden by a subclass when generating a
-     * component ancestor. Only relevant for ahead-of-time subcomponents.
+     * A method that encapsulates a modifiable binding. A binding is modifiable if it can change
+     * across implementations of a subcomponent. This is only relevant for ahead-of-time
+     * subcomponents.
      */
-    MISSING_BINDING_METHOD
+    MODIFIABLE_BINDING_METHOD,
+    ;
   }
 
   /** A type of nested class that this component model can generate. */
@@ -138,7 +143,7 @@
   private final ListMultimap<TypeSpecKind, TypeSpec> typeSpecsMap =
       MultimapBuilder.enumKeys(TypeSpecKind.class).arrayListValues().build();
   private final List<Supplier<TypeSpec>> switchingProviderSupplier = new ArrayList<>();
-  private final MissingBindingMethods missingBindingMethods = new MissingBindingMethods();
+  private final ModifiableBindingMethods modifiableBindingMethods = new ModifiableBindingMethods();
 
   private GeneratedComponentModel(
       ClassName name,
@@ -252,16 +257,22 @@
     methodSpecsMap.putAll(methodKind, methodSpecs);
   }
 
-  /** Adds the given (abstract) method representing an encapsulated missing binding. */
-  void addUnimplementedMissingBindingMethod(Key key, RequestKind kind, MethodSpec methodSpec) {
-    missingBindingMethods.addUnimplementedMethod(key, kind, methodSpec);
-    methodSpecsMap.put(MethodSpecKind.MISSING_BINDING_METHOD, methodSpec);
+  /**
+   * Adds the given method to the component. In this case, the method represents an encapsulation of
+   * a modifiable binding between implementations of a subcomponent. This is only relevant for
+   * ahead-of-time subcomponents.
+   */
+  void addModifiableBindingMethod(
+      ModifiableBindingType type, Key key, RequestKind kind, MethodSpec methodSpec) {
+    modifiableBindingMethods.addMethod(type, key, kind, methodSpec);
+    methodSpecsMap.put(MethodSpecKind.MODIFIABLE_BINDING_METHOD, methodSpec);
   }
 
-  /** Adds the implementation for the given {@link MissingBindingMethod}. */
-  void addImplementedMissingBindingMethod(MissingBindingMethod method, MethodSpec methodSpec) {
-    missingBindingMethods.methodImplemented(method);
-    methodSpecsMap.put(MethodSpecKind.MISSING_BINDING_METHOD, methodSpec);
+  /** Adds the implementation for the given {@link ModifiableBindingMethod} to the component. */
+  void addImplementedModifiableBindingMethod(
+      ModifiableBindingMethod method, MethodSpec methodSpec) {
+    modifiableBindingMethods.methodImplemented(method);
+    methodSpecsMap.put(MethodSpecKind.MODIFIABLE_BINDING_METHOD, methodSpec);
   }
 
   /** Adds the given type to the component. */
@@ -301,6 +312,21 @@
     return componentMethodNames.getUniqueName(name);
   }
 
+  /**
+   * Returns a new, unique method name for a "getter" method exposing this binding and binding kind
+   * for this component.
+   */
+  String getUniqueGetterMethodName(ContributionBinding binding, RequestKind requestKind) {
+    // TODO(user): Use a better name for @MapKey binding instances.
+    // TODO(user): Include the binding method as part of the method name.
+    String bindingName = LOWER_CAMEL.to(UPPER_CAMEL, BindingVariableNamer.name(binding));
+    String kindName =
+        requestKind.equals(RequestKind.INSTANCE)
+            ? ""
+            : UPPER_UNDERSCORE.to(UPPER_CAMEL, requestKind.name());
+    return getUniqueMethodName("get" + bindingName + kindName);
+  }
+
   /** Claims a new method name for the component. Does nothing if method name already exists. */
   void claimMethodName(Name name) {
     componentMethodNames.claim(name);
@@ -312,22 +338,21 @@
   }
 
   /**
-   * Returns the unimplemented {@link MissingBindingMethod}s for this subcomponent implementation
-   * and its superclasses.
+   * Returns the {@link ModifiableBindingMethod}s for this subcomponent implementation and its
+   * superclasses.
    */
-  ImmutableList<MissingBindingMethod> getMissingBindingMethods() {
-    ImmutableList.Builder<MissingBindingMethod> missingBindingMethodsBuilder =
+  ImmutableList<ModifiableBindingMethod> getModifiableBindingMethods() {
+    ImmutableList.Builder<ModifiableBindingMethod> modifiableBindingMethodsBuilder =
         ImmutableList.builder();
     if (supermodel.isPresent()) {
-      ImmutableList<MissingBindingMethod> bindingsUnsatisfiedBySuperclasses =
-          supermodel.get().getMissingBindingMethods();
-      bindingsUnsatisfiedBySuperclasses
-          .stream()
-          .filter(method -> !missingBindingMethods.isMethodImplemented(method))
-          .forEach(missingBindingMethodsBuilder::add);
+      ImmutableList<ModifiableBindingMethod> superclassModifiableBindingMethods =
+          supermodel.get().getModifiableBindingMethods();
+      superclassModifiableBindingMethods.stream()
+          .filter(method -> !modifiableBindingMethods.isFinalized(method))
+          .forEach(modifiableBindingMethodsBuilder::add);
     }
-    missingBindingMethodsBuilder.addAll(missingBindingMethods.getUnimplementedMethods());
-    return missingBindingMethodsBuilder.build();
+    modifiableBindingMethodsBuilder.addAll(modifiableBindingMethods.getMethods());
+    return modifiableBindingMethodsBuilder.build();
   }
 
   /** Generates the component and returns the resulting {@link TypeSpec.Builder}. */
diff --git a/java/dagger/internal/codegen/GeneratedInstanceBindingExpression.java b/java/dagger/internal/codegen/GeneratedInstanceBindingExpression.java
index 1121544..dffa052 100644
--- a/java/dagger/internal/codegen/GeneratedInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/GeneratedInstanceBindingExpression.java
@@ -16,28 +16,36 @@
 
 package dagger.internal.codegen;
 
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.model.Key;
+import dagger.model.RequestKind;
 
 /**
- * A {@link BindingExpression} that invokes a method that encapsulates 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.
+ * An {@link AbstractMethodModifiableBindingExpression} 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 BindingExpression {
-  private final Key key;
+final class GeneratedInstanceBindingExpression extends AbstractMethodModifiableBindingExpression {
+  private final GeneratedComponentModel generatedComponentModel;
+  private final ContributionBinding binding;
+  private final RequestKind requestKind;
 
-  GeneratedInstanceBindingExpression(ResolvedBindings resolvedBindings) {
-    this.key = resolvedBindings.key();
+  GeneratedInstanceBindingExpression(
+      GeneratedComponentModel generatedComponentModel,
+      ResolvedBindings resolvedBindings,
+      RequestKind requestKind) {
+    super(
+        generatedComponentModel,
+        ModifiableBindingType.GENERATED_INSTANCE,
+        resolvedBindings.key(),
+        requestKind);
+    this.generatedComponentModel = generatedComponentModel;
+    this.binding = resolvedBindings.contributionBinding();
+    this.requestKind = requestKind;
   }
 
   @Override
-  final Expression getDependencyExpression(ClassName requestingClass) {
-    // TODO(b/72748365): Implement method encapsulating binding to invoke in this expression.
-    return Expression.create(key.type(), CodeBlock.of("null"));
+  String chooseMethodName() {
+    return generatedComponentModel.getUniqueGetterMethodName(binding, requestKind);
   }
 }
diff --git a/java/dagger/internal/codegen/MissingBindingExpression.java b/java/dagger/internal/codegen/MissingBindingExpression.java
index 65eb5d9..dbc95e9 100644
--- a/java/dagger/internal/codegen/MissingBindingExpression.java
+++ b/java/dagger/internal/codegen/MissingBindingExpression.java
@@ -19,59 +19,33 @@
 import static com.google.common.base.CaseFormat.LOWER_CAMEL;
 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
 import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
-import static dagger.internal.codegen.RequestKinds.requestTypeName;
 import static dagger.internal.codegen.SourceFiles.simpleVariableName;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PUBLIC;
 
 import com.google.auto.common.MoreTypes;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeName;
 import dagger.model.Key;
 import dagger.model.RequestKind;
 
 /**
- * A {@link BindingExpression} that invokes a method that encapsulates 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.
+ * A {@link AbstractMethodModifiableBindingExpression} 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 BindingExpression {
+final class MissingBindingExpression extends AbstractMethodModifiableBindingExpression {
   private final GeneratedComponentModel generatedComponentModel;
   private final Key key;
   private final RequestKind kind;
-  private String methodName;
 
   MissingBindingExpression(
       GeneratedComponentModel generatedComponentModel, Key key, RequestKind kind) {
+    super(generatedComponentModel, ModifiableBindingType.MISSING, key, kind);
     this.generatedComponentModel = generatedComponentModel;
     this.key = key;
     this.kind = kind;
   }
 
   @Override
-  final Expression getDependencyExpression(ClassName requestingClass) {
-    addUnimplementedMethod();
-    return Expression.create(key.type(), CodeBlock.of("$L()", methodName));
-  }
-
-  private void addUnimplementedMethod() {
-    if (methodName == null) {
-      // Only add the method once in case of repeated references to the missing binding.
-      methodName = chooseMethodName();
-      generatedComponentModel.addUnimplementedMissingBindingMethod(
-          key,
-          kind,
-          MethodSpec.methodBuilder(methodName)
-              .addModifiers(PUBLIC, ABSTRACT)
-              .returns(requestTypeName(kind, TypeName.get(key.type())))
-              .build());
-    }
-  }
-
-  private String chooseMethodName() {
+  String chooseMethodName() {
     return generatedComponentModel.getUniqueMethodName(
         "get"
             + LOWER_CAMEL.to(UPPER_CAMEL, simpleVariableName(MoreTypes.asTypeElement(key.type())))
diff --git a/java/dagger/internal/codegen/MissingBindingMethods.java b/java/dagger/internal/codegen/MissingBindingMethods.java
deleted file mode 100644
index 473166a..0000000
--- a/java/dagger/internal/codegen/MissingBindingMethods.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2018 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.checkState;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.squareup.javapoet.MethodSpec;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A registry for those methods which each wrap a binding that is unsatisfiable by a subcomponent in
- * isolation, but can be satisfied by an ancestor component. This is useful when generating
- * ahead-of-time subcomponents: An instance of this class is associated with a single subcomponent
- * implementation. We generate an implementation of a given subcomponent once for each of it's
- * ancestor components and for any one implementation the {@link MissingBindingMethod}s of it's
- * superclasses tell us what missing bindings have yet to be satisfied so we can attempt to satisfy
- * them.
- */
-final class MissingBindingMethods {
-  private final Map<KeyAndKind, MissingBindingMethod> missingBindingMethods = Maps.newHashMap();
-  private final Set<KeyAndKind> implementedMissingBindingMethods = Sets.newHashSet();
-
-  /** Record an unimplemented method encapsulating a missing binding. */
-  void addUnimplementedMethod(Key key, RequestKind kind, MethodSpec unimplementedMethod) {
-    KeyAndKind keyAndKind = KeyAndKind.create(key, kind);
-    checkState(
-        !implementedMissingBindingMethods.contains(keyAndKind),
-        "Adding an missing binding method for a method marked as implemented for the current "
-            + "subcomponent implementation. The binding is for a %s-%s.",
-        key,
-        kind);
-    missingBindingMethods.put(
-        keyAndKind, MissingBindingMethod.create(key, kind, unimplementedMethod));
-  }
-
-  /** Returns all unimplemented {@link MissingBindingMethod}s */
-  ImmutableList<MissingBindingMethod> getUnimplementedMethods() {
-    // We will never register a binding as missing and also as implemented for the same instance of
-    // MissingBindingMethods, so there's no need to filter missingBindingMethods.
-    return ImmutableList.copyOf(missingBindingMethods.values());
-  }
-
-  /** Mark the {@link MissingBindingMethod} as having been implemented. */
-  void methodImplemented(MissingBindingMethod method) {
-    KeyAndKind keyAndKind = KeyAndKind.create(method.key(), method.kind());
-    checkState(
-        !missingBindingMethods.containsKey(keyAndKind),
-        "Indicating a missing binding method as implemented when it was registered as missing for "
-            + "the current subcomponent implementation. The binding is for a %s-%s.",
-        method.key(),
-        method.kind());
-    implementedMissingBindingMethods.add(keyAndKind);
-  }
-
-  /** Whether a given binding has been marked as implemented. */
-  boolean isMethodImplemented(MissingBindingMethod method) {
-    return implementedMissingBindingMethods.contains(
-        KeyAndKind.create(method.key(), method.kind()));
-  }
-
-  @AutoValue
-  abstract static class MissingBindingMethod {
-    private static MissingBindingMethod create(
-        Key key, RequestKind kind, MethodSpec unimplementedMethod) {
-      return new AutoValue_MissingBindingMethods_MissingBindingMethod(
-          key, kind, unimplementedMethod);
-    }
-
-    abstract Key key();
-
-    abstract RequestKind kind();
-
-    abstract MethodSpec unimplementedMethod();
-  }
-
-  @AutoValue
-  abstract static class KeyAndKind {
-    private static KeyAndKind create(Key key, RequestKind kind) {
-      return new AutoValue_MissingBindingMethods_KeyAndKind(key, kind);
-    }
-
-    abstract Key key();
-
-    abstract RequestKind kind();
-  }
-}
diff --git a/java/dagger/internal/codegen/ModifiableBindingMethods.java b/java/dagger/internal/codegen/ModifiableBindingMethods.java
new file mode 100644
index 0000000..766031b
--- /dev/null
+++ b/java/dagger/internal/codegen/ModifiableBindingMethods.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 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.checkState;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.squareup.javapoet.MethodSpec;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A registry for those methods which each wrap a binding whose definition may be modified across
+ * each class in the class hierarchy implementing a subcomponent. Subcomponent implementations are
+ * spread across a class hierarchy when generating ahead-of-time subcomponents. There is one
+ * subcomponent implementation class for each of the subcomponent's ancestor components. An instance
+ * of {@link ModifiableBindingMethod} is associated with a single class in this hierarchy. For a
+ * given subcomponent implementation class we can use the {@link ModifiableBindingMethod}s of its
+ * superclasses to know what binding methods to attempt to modify.
+ */
+final class ModifiableBindingMethods {
+  private final Map<KeyAndKind, ModifiableBindingMethod> methods = Maps.newHashMap();
+  private final Set<KeyAndKind> finalizedMethods = Sets.newHashSet();
+
+  /** Register a method encapsulating a modifiable binding. */
+  void addMethod(
+      ModifiableBindingType type, Key key, RequestKind kind, MethodSpec unimplementedMethod) {
+    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));
+  }
+
+  /** 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());
+  }
+
+  /**
+   * 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.
+   */
+  void methodImplemented(ModifiableBindingMethod method) {
+    if (method.type().finalizedOnModification()) {
+      KeyAndKind keyAndKind = KeyAndKind.create(method.key(), method.kind());
+      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.",
+          method.key(),
+          method.kind(),
+          method.type());
+      finalizedMethods.add(keyAndKind);
+    }
+  }
+
+  /** Whether a given binding has been marked as finalized. */
+  boolean isFinalized(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) {
+      return new AutoValue_ModifiableBindingMethods_ModifiableBindingMethod(
+          type, key, kind, unimplementedMethod);
+    }
+
+    abstract ModifiableBindingType type();
+
+    abstract Key key();
+
+    abstract RequestKind kind();
+
+    abstract MethodSpec baseMethod();
+  }
+
+  @AutoValue
+  abstract static class KeyAndKind {
+    private static KeyAndKind create(Key key, RequestKind kind) {
+      return new AutoValue_ModifiableBindingMethods_KeyAndKind(key, kind);
+    }
+
+    abstract Key key();
+
+    abstract RequestKind kind();
+  }
+}
diff --git a/java/dagger/internal/codegen/ModifiableBindingType.java b/java/dagger/internal/codegen/ModifiableBindingType.java
new file mode 100644
index 0000000..d2c5f6f
--- /dev/null
+++ b/java/dagger/internal/codegen/ModifiableBindingType.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 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 com.google.common.collect.ImmutableSet;
+
+/**
+ * A label for a binding indicating whether, and how, it may be redefined across implementations of
+ * a subcomponent.
+ *
+ * <p>A subcomponent has multiple implementations only when generating ahead-of-time subcomponents.
+ * Specifically, each subcomponent type in a component hierarchy is implemented as an abstract
+ * class, and descendent components are implemented as abstract inner classes. A consequence of this
+ * is that a given subcomponent has an implementation for each ancestor component. Each
+ * implementation represents a different sub-binding-graph of the full subcomponent. A binding is
+ * modifiable if it's definition may change depending on the characteristics of its ancestor
+ * components.
+ */
+enum ModifiableBindingType {
+  /** A binding that is not modifiable */
+  NONE,
+
+  /**
+   * A binding that is missing when generating the abstract base class implementation of a
+   * subcomponent.
+   */
+  MISSING,
+
+  /**
+   * A binding that requires an instance of a generated type. These binding are modifiable in the
+   * sense that they are encapsulated in a method when they are first required, possibly in an
+   * abstract implementation of a subcomponent, where, in general, no concrete instances of
+   * generated types are available, and the method is satisfied in a final concrete implementation.
+   */
+  GENERATED_INSTANCE,
+
+  /**
+   * Multibindings may have contributions come from any ancestor component. Therefore, each
+   * implementation of a subcomponent may have newly available contributions, and so the binding
+   * method is reimplemented with each subcomponent implementation.
+   */
+  MULTIBINDING,
+
+  /**
+   * A Optional binding that may be empty when looking at a partial binding graph, but bound to a
+   * value when considering the complete binding graph, thus modifiable across subcomponent
+   * implementations.
+   */
+  OPTIONAL,
+
+  /**
+   * If a binding is defined according to an {@code @Inject} annotated constructor on the object it
+   * is valid for that binding to be redefined a single time by an {@code @Provides} annotated
+   * module method. It is possible that the {@code @Provides} binding isn't available in a partial
+   * binding graph, but becomes available when considering a more complete binding graph, therefore
+   * such bindings are modifiable across subcomponent implementations.
+   */
+  INJECTION,
+  ;
+
+  private static final ImmutableSet<ModifiableBindingType> TYPES_FINALIZED_ON_MODIFICATION =
+      ImmutableSet.of(MISSING, GENERATED_INSTANCE, OPTIONAL, INJECTION);
+
+  boolean isModifiable() {
+    return !equals(NONE);
+  }
+
+  /**
+   * 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);
+  }
+}
diff --git a/java/dagger/internal/codegen/PrivateMethodBindingExpression.java b/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
index e19f9b8..bd92437 100644
--- a/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
+++ b/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
@@ -16,9 +16,6 @@
 
 package dagger.internal.codegen;
 
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
@@ -56,7 +53,7 @@
   protected void addMethod() {
     if (methodName == null) {
       // Have to set methodName field before implementing the method in order to handle recursion.
-      methodName = chooseMethodName();
+      methodName = generatedComponentModel.getUniqueGetterMethodName(binding, requestKind);
       // TODO(user): Fix the order that these generated methods are written to the component.
       generatedComponentModel.addMethod(
           PRIVATE_METHOD,
@@ -73,23 +70,4 @@
     checkState(methodName != null, "addMethod() must be called before methodName()");
     return methodName;
   }
-
-  private String chooseMethodName() {
-    // TODO(user): Use a better name for @MapKey binding instances.
-    // TODO(user): Include the binding method as part of the method name.
-    return generatedComponentModel.getUniqueMethodName(
-        "get" + bindingName() + dependencyKindName());
-  }
-
-  /** Returns the canonical method name suffix for the binding. */
-  private String bindingName() {
-    return LOWER_CAMEL.to(UPPER_CAMEL, BindingVariableNamer.name(binding));
-  }
-
-  /** Returns a canonical method name suffix for the request kind. */
-  private String dependencyKindName() {
-    return requestKind.equals(RequestKind.INSTANCE)
-        ? ""
-        : UPPER_UNDERSCORE.to(UPPER_CAMEL, requestKind.name());
-  }
 }