Prevent removing derived state from composition when it is read in other scopes

Derived state observations were previously unconditionally removed from the recompose scope and composition together, which broke other scopes which might be still observing derived state.

This change only removes derived state instances if it is no longer observed by other scopes.

Fixes: 236618362
Test: CompositionAndDerivedStateTests#observingDerivedStateInMultipleScopes
Change-Id: I92e7c8702efecb33bea3ea3effe9ec47e2a6ae5b
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
index e318d61..1dc664c 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
@@ -919,7 +919,10 @@
     }
 
     internal fun removeDerivedStateObservation(state: DerivedState<*>) {
-        derivedStates.removeScope(state)
+        // remove derived state if it is not observed in other scopes
+        if (state !in observations) {
+            derivedStates.removeScope(state)
+        }
     }
 
     /**
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt
index c73242e..125ac7d 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionAndDerivedStateTests.kt
@@ -387,6 +387,54 @@
     }
 
     @Test
+    fun observingDerivedStateInMultipleScopes() = compositionTest {
+        var observeInFirstScope by mutableStateOf(true)
+        var count by mutableStateOf(0)
+
+        compose {
+            val items by remember {
+                derivedStateOf {
+                    List(count) { it }
+                }
+            }
+
+            Linear {
+                if (observeInFirstScope) {
+                    Text("List of size ${items.size}")
+                }
+            }
+
+            Linear {
+                Text("List of size ${items.size}")
+            }
+        }
+
+        validate {
+            Linear {
+                Text("List of size 0")
+            }
+
+            Linear {
+                Text("List of size 0")
+            }
+        }
+
+        observeInFirstScope = false
+        advance()
+        count++
+        advance()
+
+        validate {
+            Linear {
+            }
+
+            Linear {
+                Text("List of size 1")
+            }
+        }
+    }
+
+    @Test
     fun changingTheDerivedStateInstanceShouldClearDependencies() = compositionTest {
         var reload by mutableStateOf(0)