[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/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()));
+    }
   }
 
   /**