Repository: quarkusio/quarkus
Issue: ArC - it's not possible to inject Instance<> into an observer method parameter

The code generated by `io.quarkus.arc.processor.BuiltinBean.INSTANCE` only considers injection into a bean.

Changed files:
independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceProvider.java
independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceImpl.java
independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ComponentsProviderGenerator.java
independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/observers/injection/SimpleObserverInjectionTest.java
independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java
independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceBean.java
independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/producer/disposer/DisposerTest.java
independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java

diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java
index 0011390beea..8684ea34295 100644
--- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java
+++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java
@@ -43,10 +43,23 @@ enum BuiltinBean {
                 ctx.injectionPoint, ctx.annotationLiterals);
         ResultHandle javaMemberHandle = BeanGenerator.getJavaMemberHandle(ctx.constructor, ctx.injectionPoint,
                 ctx.reflectionRegistration);
+        ResultHandle beanHandle;
+        switch (ctx.targetInfo.kind()) {
+            case OBSERVER:
+                // For observers the first argument is always the declaring bean
+                beanHandle = ctx.constructor.invokeInterfaceMethod(
+                        MethodDescriptors.SUPPLIER_GET, ctx.constructor.getMethodParam(0));
+                break;
+            case BEAN:
+                beanHandle = ctx.constructor.getThis();
+                break;
+            default:
+                throw new IllegalStateException("Unsupported target info: " + ctx.targetInfo);
+        }
         ResultHandle instanceProvider = ctx.constructor.newInstance(
                 MethodDescriptor.ofConstructor(InstanceProvider.class, java.lang.reflect.Type.class, Set.class,
                         InjectableBean.class, Set.class, Member.class, int.class),
-                parameterizedType, qualifiers, ctx.constructor.getThis(), annotationsHandle, javaMemberHandle,
+                parameterizedType, qualifiers, beanHandle, annotationsHandle, javaMemberHandle,
                 ctx.constructor.load(ctx.injectionPoint.getPosition()));
         ResultHandle instanceProviderSupplier = ctx.constructor.newInstance(
                 MethodDescriptors.FIXED_VALUE_SUPPLIER_CONSTRUCTOR, instanceProvider);
diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ComponentsProviderGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ComponentsProviderGenerator.java
index cf412e94f23..6d77b403dd5 100644
--- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ComponentsProviderGenerator.java
+++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ComponentsProviderGenerator.java
@@ -414,6 +414,9 @@ void addBean(BeanInfo bean, ResultHandle beanIdToBeanHandle,
             }
             if (bean.getDisposer() != null) {
                 for (InjectionPointInfo injectionPoint : bean.getDisposer().getInjection().injectionPoints) {
+                    if (BuiltinBean.resolvesTo(injectionPoint)) {
+                        continue;
+                    }
                     params.add(addBeansMethod.newInstance(
                             MethodDescriptors.MAP_VALUE_SUPPLIER_CONSTRUCTOR,
                             beanIdToBeanHandle, addBeansMethod.load(injectionPoint.getResolvedBean().getIdentifier())));
diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java
index 1c5aea637fc..0d145c1dc27 100644
--- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java
+++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java
@@ -144,7 +144,7 @@ private void addBuiltInBeans() {
         // BeanManager, Event<?>, Instance<?>
         beans.add(new BeanManagerBean());
         beans.add(new EventBean());
-        beans.add(new InstanceBean());
+        beans.add(InstanceBean.INSTANCE);
     }
 
     public void init() {
diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceBean.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceBean.java
index fd94809501e..eeb8e7eae53 100644
--- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceBean.java
+++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceBean.java
@@ -15,6 +15,8 @@ public class InstanceBean extends BuiltInBean<Instance<?>> {
     public static final Set<Type> INSTANCE_TYPES = Collections
             .unmodifiableSet(new HashSet<>(Arrays.asList(Instance.class, Object.class)));
 
+    static final InstanceBean INSTANCE = new InstanceBean();
+
     @Override
     public Set<Type> getTypes() {
         return INSTANCE_TYPES;
diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceImpl.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceImpl.java
index 18804b2f30b..60c4b83f468 100644
--- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceImpl.java
+++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceImpl.java
@@ -35,9 +35,11 @@
     private final CreationalContextImpl<?> creationalContext;
     private final Set<InjectableBean<?>> resolvedBeans;
 
-    private final Type injectionPointType;
     private final Type requiredType;
     private final Set<Annotation> requiredQualifiers;
+
+    // The following fields are only needed for InjectionPoint metadata
+    private final Type injectionPointType;
     private final InjectableBean<?> targetBean;
     private final Set<Annotation> annotations;
     private final Member javaMember;
diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceProvider.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceProvider.java
index 9b8ad432ec4..2ac2c92024d 100644
--- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceProvider.java
+++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/InstanceProvider.java
@@ -32,10 +32,15 @@ public InstanceProvider(Type type, Set<Annotation> qualifiers, InjectableBean<?>
         this.position = position;
     }
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     @Override
     public Instance<T> get(CreationalContext<Instance<T>> creationalContext) {
-        return new InstanceImpl<T>(targetBean, requiredType, qualifiers, CreationalContextImpl.unwrap(creationalContext),
+        InstanceImpl<T> instance = new InstanceImpl<T>(targetBean, requiredType, qualifiers,
+                CreationalContextImpl.unwrap(creationalContext),
                 annotations, javaMember, position);
+        CreationalContextImpl.addDependencyToParent(InstanceBean.INSTANCE, instance,
+                (CreationalContext) creationalContext);
+        return instance;
     }
 
 }
diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/observers/injection/SimpleObserverInjectionTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/observers/injection/SimpleObserverInjectionTest.java
index 32910fcf379..2ffe315367f 100644
--- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/observers/injection/SimpleObserverInjectionTest.java
+++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/observers/injection/SimpleObserverInjectionTest.java
@@ -13,6 +13,7 @@
 import javax.annotation.PreDestroy;
 import javax.enterprise.context.Dependent;
 import javax.enterprise.event.Observes;
+import javax.enterprise.inject.Instance;
 import javax.inject.Singleton;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
@@ -40,8 +41,12 @@ public void testObserverInjection() {
     static class StringObserver {
 
         @SuppressWarnings({ "rawtypes", "unchecked" })
-        void observeString(@Observes AtomicReference value, Fool fool) {
+        void observeString(@Observes AtomicReference value, Fool fool, Instance<Fool> fools) {
             value.set(fool.id);
+            fools.forEach(f -> {
+                // Fool is @Dependent!
+                assertNotEquals(fool.id, f.id);
+            });
         }
 
     }
diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/producer/disposer/DisposerTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/producer/disposer/DisposerTest.java
index 478e6f6ca77..705516ecc6b 100644
--- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/producer/disposer/DisposerTest.java
+++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/producer/disposer/DisposerTest.java
@@ -2,18 +2,22 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import io.quarkus.arc.Arc;
 import io.quarkus.arc.InstanceHandle;
 import io.quarkus.arc.test.ArcTestContainer;
 import io.quarkus.arc.test.MyQualifier;
 import java.math.BigDecimal;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
+import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 import javax.enterprise.context.ApplicationScoped;
 import javax.enterprise.context.Dependent;
 import javax.enterprise.inject.Disposes;
+import javax.enterprise.inject.Instance;
 import javax.enterprise.inject.Produces;
 import javax.enterprise.util.TypeLiteral;
 import javax.inject.Singleton;
@@ -25,7 +29,7 @@ public class DisposerTest {
 
     @RegisterExtension
     ArcTestContainer container = new ArcTestContainer(StringProducer.class, LongProducer.class, BigDecimalProducer.class,
-            MyQualifier.class);
+            MyQualifier.class, Pong.class);
 
     @AfterAll
     public static void afterAll() {
@@ -41,6 +45,9 @@ public void testDisposers() {
         assertEquals(LongProducer.DISPOSED.get(), longValue);
         // String is only injected in Long disposer
         assertNotNull(StringProducer.DISPOSED.get());
+        // Pong should be destroyed when the disposer invocation completes
+        assertTrue(Pong.DESTROYED.get());
+
         // A new instance is created for produce and dispose
         assertEquals(2, StringProducer.DESTROYED.get());
         // Both producer and produced bean are application scoped
@@ -61,9 +68,31 @@ Long produce() {
             return System.currentTimeMillis();
         }
 
-        void dipose(@Disposes Long value, @MyQualifier String injectedString) {
+        void dipose(@Disposes Long value, @MyQualifier String injectedString, Instance<Pong> pongs) {
             assertNotNull(injectedString);
             DISPOSED.set(value);
+            pongs.forEach(p -> {
+                assertEquals("OK", p.id);
+            });
+        }
+
+    }
+
+    @Dependent
+    static class Pong {
+
+        static final AtomicBoolean DESTROYED = new AtomicBoolean();
+
+        String id;
+
+        @PostConstruct
+        void init() {
+            id = "OK";
+        }
+
+        @PreDestroy
+        void destroy() {
+            DESTROYED.set(true);
         }
 
     }