Merge "Change how function instances are compared for skipping" into androidx-main
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt
index 897c193..905e566 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt
@@ -501,7 +501,7 @@
                   sourceInformation(%composer, "C(Wrapper)<block(...>:Test.kt#2487m")
                   val %dirty = %changed
                   if (%changed and 0b1110 === 0) {
-                    %dirty = %dirty or if (%composer.changed(block)) 0b0100 else 0b0010
+                    %dirty = %dirty or if (%composer.changedInstance(block)) 0b0100 else 0b0010
                   }
                   if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
                     if (isTraceInProgress()) {
@@ -648,7 +648,7 @@
                   sourceInformation(%composer, "C(composeVector)<emit>:Test.kt#2487m")
                   val %dirty = %changed
                   if (%changed and 0b1110 === 0) {
-                    %dirty = %dirty or if (%composer.changed(composable)) 0b0100 else 0b0010
+                    %dirty = %dirty or if (%composer.changedInstance(composable)) 0b0100 else 0b0010
                   }
                   if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
                     if (isTraceInProgress()) {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ContextReceiversTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ContextReceiversTransformTests.kt
index 7d0cee9..9edb6f7 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ContextReceiversTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ContextReceiversTransformTests.kt
@@ -544,7 +544,7 @@
               sourceInformation(%composer, "C(Test)<b("yay...>:Test.kt")
               val %dirty = %changed
               if (%changed and 0b001110000000 === 0) {
-                %dirty = %dirty or if (%composer.changed(b)) 0b000100000000 else 0b10000000
+                %dirty = %dirty or if (%composer.changedInstance(b)) 0b000100000000 else 0b10000000
               }
               if (%dirty and 0b001010000001 !== 0b10000000 || !%composer.skipping) {
                 if (isTraceInProgress()) {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
index c53597f..e569c06 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
@@ -167,7 +167,7 @@
               if (%default and 0b0010 !== 0) {
                 %dirty = %dirty or 0b00110000
               } else if (%changed and 0b01110000 === 0) {
-                %dirty = %dirty or if (%composer.changed(onTextLayout)) 0b00100000 else 0b00010000
+                %dirty = %dirty or if (%composer.changedInstance(onTextLayout)) 0b00100000 else 0b00010000
               }
               if (%default and 0b0100 !== 0) {
                 %dirty = %dirty or 0b000110000000
@@ -273,7 +273,7 @@
               if (%default and 0b0001 !== 0) {
                 %dirty = %dirty or 0b0110
               } else if (%changed and 0b1110 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+                %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
               }
               if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
                 if (%default and 0b0001 !== 0) {
@@ -565,7 +565,7 @@
               if (%default and 0b00100000 !== 0) {
                 %dirty = %dirty or 0b00110000000000000000
               } else if (%changed and 0b01110000000000000000 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b00100000000000000000 else 0b00010000000000000000
+                %dirty = %dirty or if (%composer.changedInstance(content)) 0b00100000000000000000 else 0b00010000000000000000
               }
               if (%dirty and 0b01011011011011011011 !== 0b00010010010010010010 || !%composer.skipping) {
                 if (%default and 0b0010 !== 0) {
@@ -623,7 +623,7 @@
               if (%default and 0b1000 !== 0) {
                 %dirty = %dirty or 0b110000000000
               } else if (%changed and 0b0001110000000000 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b100000000000 else 0b010000000000
+                %dirty = %dirty or if (%composer.changedInstance(content)) 0b100000000000 else 0b010000000000
               }
               if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
                 if (%default and 0b0001 !== 0) {
@@ -960,7 +960,7 @@
               if (%default and 0b0010 !== 0) {
                 %dirty = %dirty or 0b00110000
               } else if (%changed and 0b01110000 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b00100000 else 0b00010000
+                %dirty = %dirty or if (%composer.changedInstance(content)) 0b00100000 else 0b00010000
               }
               if (%dirty and 0b01011011 !== 0b00010010 || !%composer.skipping) {
                 if (%default and 0b0001 !== 0) {
@@ -1100,7 +1100,7 @@
               sourceInformation(%composer, "C(SomeThing)<conten...>:Test.kt")
               val %dirty = %changed
               if (%changed and 0b1110 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+                %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
               }
               if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
                 if (isTraceInProgress()) {
@@ -1435,7 +1435,7 @@
                 %dirty = %dirty or if (%composer.changed(y)) 0b0100 else 0b0010
               }
               if (%changed and 0b01110000 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b00100000 else 0b00010000
+                %dirty = %dirty or if (%composer.changedInstance(content)) 0b00100000 else 0b00010000
               }
               if (%dirty and 0b01011011 !== 0b00010010 || !%composer.skipping) {
                 if (isTraceInProgress()) {
@@ -3454,7 +3454,7 @@
               sourceInformation(%composer, "C(Example)<invoke...>:Test.kt")
               val %dirty = %changed
               if (%changed and 0b1110 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+                %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
               }
               if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
                 if (isTraceInProgress()) {
@@ -3540,7 +3540,7 @@
               if (%default and 0b0100 !== 0) {
                 %dirty = %dirty or 0b000110000000
               } else if (%changed and 0b001110000000 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b000100000000 else 0b10000000
+                %dirty = %dirty or if (%composer.changedInstance(content)) 0b000100000000 else 0b10000000
               }
               if (%dirty and 0b001011011011 !== 0b10010010 || !%composer.skipping) {
                 if (%default and 0b0001 !== 0) {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
index b9a59ea..803e2de 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
@@ -678,7 +678,7 @@
               if (%default and 0b0010 !== 0) {
                 %dirty = %dirty or 0b00110000
               } else if (%changed and 0b01110000 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b00100000 else 0b00010000
+                %dirty = %dirty or if (%composer.changedInstance(content)) 0b00100000 else 0b00010000
               }
               if (%dirty and 0b01011011 !== 0b00010010 || !%composer.skipping) {
                 if (%default and 0b0010 !== 0) {
@@ -805,7 +805,7 @@
               sourceInformation(%composer, "C(TestLambda):Test.kt")
               val %dirty = %changed
               if (%changed and 0b1110 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+                %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
               }
               if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
                 if (isTraceInProgress()) {
@@ -871,7 +871,7 @@
           sourceInformation(%composer, "C(TestLambda):Test.kt")
           val %dirty = %changed
           if (%changed and 0b1110 === 0) {
-            %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+            %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
           }
           if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
             if (isTraceInProgress()) {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt
index 0153148..e438684 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt
@@ -137,7 +137,7 @@
           sourceInformation(%composer, "C(Test)<conten...>:Test.kt")
           val %dirty = %changed
           if (%changed and 0b1110 === 0) {
-            %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+            %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
           }
           if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
             if (isTraceInProgress()) {
@@ -535,7 +535,7 @@
           sourceInformation(%composer, "C(Test)*<it()>:Test.kt")
           val %dirty = %changed
           if (%changed and 0b1110 === 0) {
-            %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+            %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
           }
           if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
             if (isTraceInProgress()) {
@@ -645,7 +645,7 @@
           sourceInformation(%composer, "C(Wrapper)<conten...>:Test.kt")
           val %dirty = %changed
           if (%changed and 0b1110 === 0) {
-            %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+            %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
           }
           if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
             if (isTraceInProgress()) {
@@ -671,37 +671,37 @@
           if (%default and 0b0001 !== 0) {
             %dirty = %dirty or 0b0110
           } else if (%changed and 0b1110 === 0) {
-            %dirty = %dirty or if (%composer.changed(one)) 0b0100 else 0b0010
+            %dirty = %dirty or if (%composer.changedInstance(one)) 0b0100 else 0b0010
           }
           if (%default and 0b0010 !== 0) {
             %dirty = %dirty or 0b00110000
           } else if (%changed and 0b01110000 === 0) {
-            %dirty = %dirty or if (%composer.changed(two)) 0b00100000 else 0b00010000
+            %dirty = %dirty or if (%composer.changedInstance(two)) 0b00100000 else 0b00010000
           }
           if (%default and 0b0100 !== 0) {
             %dirty = %dirty or 0b000110000000
           } else if (%changed and 0b001110000000 === 0) {
-            %dirty = %dirty or if (%composer.changed(three)) 0b000100000000 else 0b10000000
+            %dirty = %dirty or if (%composer.changedInstance(three)) 0b000100000000 else 0b10000000
           }
           if (%default and 0b1000 !== 0) {
             %dirty = %dirty or 0b110000000000
           } else if (%changed and 0b0001110000000000 === 0) {
-            %dirty = %dirty or if (%composer.changed(four)) 0b100000000000 else 0b010000000000
+            %dirty = %dirty or if (%composer.changedInstance(four)) 0b100000000000 else 0b010000000000
           }
           if (%default and 0b00010000 !== 0) {
             %dirty = %dirty or 0b0110000000000000
           } else if (%changed and 0b1110000000000000 === 0) {
-            %dirty = %dirty or if (%composer.changed(five)) 0b0100000000000000 else 0b0010000000000000
+            %dirty = %dirty or if (%composer.changedInstance(five)) 0b0100000000000000 else 0b0010000000000000
           }
           if (%default and 0b00100000 !== 0) {
             %dirty = %dirty or 0b00110000000000000000
           } else if (%changed and 0b01110000000000000000 === 0) {
-            %dirty = %dirty or if (%composer.changed(six)) 0b00100000000000000000 else 0b00010000000000000000
+            %dirty = %dirty or if (%composer.changedInstance(six)) 0b00100000000000000000 else 0b00010000000000000000
           }
           if (%default and 0b01000000 !== 0) {
             %dirty = %dirty or 0b000110000000000000000000
           } else if (%changed and 0b001110000000000000000000 === 0) {
-            %dirty = %dirty or if (%composer.changed(content)) 0b000100000000000000000000 else 0b10000000000000000000
+            %dirty = %dirty or if (%composer.changedInstance(content)) 0b000100000000000000000000 else 0b10000000000000000000
           }
           if (%dirty and 0b001011011011011011011011 !== 0b10010010010010010010 || !%composer.skipping) {
             if (%default and 0b0001 !== 0) {
@@ -1020,7 +1020,7 @@
           sourceInformation(%composer, "C(Test2)<Layout...>:Test.kt")
           val %dirty = %changed
           if (%changed and 0b1110 === 0) {
-            %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+            %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
           }
           if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
             if (isTraceInProgress()) {
@@ -1094,7 +1094,7 @@
           sourceInformation(%composer, "C(Test5)<Compos...>:Test.kt")
           val %dirty = %changed
           if (%changed and 0b1110 === 0) {
-            %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+            %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
           }
           if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
             if (isTraceInProgress()) {
@@ -1272,7 +1272,7 @@
           sourceInformation(%composer, "C(Test)<rememb...>:Test.kt")
           val %dirty = %changed
           if (%changed and 0b1110 === 0) {
-            %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+            %dirty = %dirty or if (%composer.changedInstance(content)) 0b0100 else 0b0010
           }
           if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
             if (isTraceInProgress()) {
@@ -1426,7 +1426,7 @@
           sourceInformation(%composer, "C(Test)<decora...>:Test.kt")
           val %dirty = %changed
           if (%changed and 0b1110 === 0) {
-            %dirty = %dirty or if (%composer.changed(decorator)) 0b0100 else 0b0010
+            %dirty = %dirty or if (%composer.changedInstance(decorator)) 0b0100 else 0b0010
           }
           if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
             if (isTraceInProgress()) {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
index 85953db..134e3f3 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
@@ -152,6 +152,7 @@
 import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
 import org.jetbrains.kotlin.ir.util.functions
 import org.jetbrains.kotlin.ir.util.getPropertyGetter
+import org.jetbrains.kotlin.ir.util.isFunction
 import org.jetbrains.kotlin.ir.util.isLocal
 import org.jetbrains.kotlin.ir.util.isVararg
 import org.jetbrains.kotlin.ir.util.kotlinFqName
@@ -503,6 +504,12 @@
             it.name.identifier == "changed" && it.valueParameters.first().type.isNullableAny()
         }
 
+    private val changedInstanceFunction = composerIrClass.functions
+        .firstOrNull() {
+            it.name.identifier == "changedInstance" &&
+                it.valueParameters.first().type.isNullableAny()
+        } ?: changedFunction
+
     private fun IrType.toPrimitiveType(): PrimitiveType? = when {
         isInt() -> PrimitiveType.INT
         isBoolean() -> PrimitiveType.BOOLEAN
@@ -2150,7 +2157,8 @@
         val expr = value.unboxValueIfInline()
         val descriptor = type
             .toPrimitiveType()
-            .let { changedPrimitiveFunctions[it] } ?: changedFunction
+            .let { changedPrimitiveFunctions[it] }
+            ?: if (type.isFunction()) changedInstanceFunction else changedFunction
         return irMethodCall(irCurrentComposer(), descriptor).also {
             it.putValueArgument(0, expr)
         }
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index 2c2e001..8b0cc2a 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -108,6 +108,7 @@
     method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(float value);
     method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(long value);
     method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(double value);
+    method @androidx.compose.runtime.ComposeCompilerApi public default boolean changedInstance(Object? value);
     method public void collectParameterInformation();
     method @androidx.compose.runtime.ComposeCompilerApi public <T> void createNode(kotlin.jvm.functions.Function0<? extends T> factory);
     method @androidx.compose.runtime.ComposeCompilerApi public void deactivateToEndGroup(boolean changed);
diff --git a/compose/runtime/runtime/api/public_plus_experimental_current.txt b/compose/runtime/runtime/api/public_plus_experimental_current.txt
index 0801888..c69dd48 100644
--- a/compose/runtime/runtime/api/public_plus_experimental_current.txt
+++ b/compose/runtime/runtime/api/public_plus_experimental_current.txt
@@ -114,6 +114,7 @@
     method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(float value);
     method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(long value);
     method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(double value);
+    method @androidx.compose.runtime.ComposeCompilerApi public default boolean changedInstance(Object? value);
     method public void collectParameterInformation();
     method @androidx.compose.runtime.InternalComposeApi public <T> T! consume(androidx.compose.runtime.CompositionLocal<T> key);
     method @androidx.compose.runtime.ComposeCompilerApi public <T> void createNode(kotlin.jvm.functions.Function0<? extends T> factory);
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index b50e6f8..684ac84 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -110,6 +110,7 @@
     method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(float value);
     method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(long value);
     method @androidx.compose.runtime.ComposeCompilerApi public default boolean changed(double value);
+    method @androidx.compose.runtime.ComposeCompilerApi public default boolean changedInstance(Object? value);
     method public void collectParameterInformation();
     method @androidx.compose.runtime.ComposeCompilerApi public <T> void createNode(kotlin.jvm.functions.Function0<? extends T> factory);
     method @androidx.compose.runtime.ComposeCompilerApi public void deactivateToEndGroup(boolean changed);
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
index 44515859..318a06f 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
@@ -19,6 +19,7 @@
 )
 package androidx.compose.runtime
 
+import androidx.compose.runtime.Composer.Companion.equals
 import androidx.compose.runtime.collection.IdentityArrayMap
 import androidx.compose.runtime.collection.IdentityArraySet
 import androidx.compose.runtime.external.kotlinx.collections.immutable.PersistentMap
@@ -916,6 +917,21 @@
     @ComposeCompilerApi
     fun changed(value: Double): Boolean = changed(value)
 
+    /**
+     * A Compose compiler plugin API. DO NOT call directly.
+     *
+     * Check [value] is different than the value used in the previous composition using `===`
+     * instead of `==` equality. This is used,  for example, to check parameter values to determine
+     * if they have changed for values that use value equality but, for correct behavior, the
+     * composer needs reference equality.
+     *
+     * @param value the value to check
+     * @return `true` if the value is === equal to the previous value and returns `false` when
+     * [value] is different.
+     */
+    @ComposeCompilerApi
+    fun changedInstance(value: Any?): Boolean = changed(value)
+
     // Scopes
 
     /**
@@ -1662,6 +1678,16 @@
     }
 
     @ComposeCompilerApi
+    override fun changedInstance(value: Any?): Boolean {
+        return if (nextSlot() !== value) {
+            updateValue(value)
+            true
+        } else {
+            false
+        }
+    }
+
+    @ComposeCompilerApi
     override fun changed(value: Char): Boolean {
         val next = nextSlot()
         if (next is Char) {
diff --git a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
index a20604d..82ba839 100644
--- a/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
+++ b/compose/runtime/runtime/src/commonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
@@ -3214,6 +3214,31 @@
             Text("Hello!")
         }
     }
+
+    @Test // Regression test for b/249050560
+    fun testFunctionInstances() = compositionTest {
+        var state by mutableStateOf(0)
+        functionInstance = { -1 }
+
+        compose {
+            val localStateCopy = state
+            fun localStateReader() = localStateCopy
+            updateInstance(::localStateReader)
+        }
+
+        assertEquals(state, functionInstance())
+
+        state = 10
+        advance()
+        assertEquals(state, functionInstance())
+    }
+}
+
+var functionInstance: () -> Int = { 0 }
+
+@Composable
+fun updateInstance(newInstance: () -> Int) {
+    functionInstance = newInstance
 }
 
 var stateA by mutableStateOf(1000)