Regenerate semantics id when the layout node is reused.

Bug: 281908278
Change-Id: I6868da7db3aa166ecc4351787a6e3f3b37332d8a
Test: the newly added test case will fail if not regenerate the id.
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
index f27b445..d8504fad 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/semantics/SemanticsTests.kt
@@ -26,9 +26,12 @@
 import androidx.compose.material.Surface
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReusableContent
 import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
@@ -65,6 +68,7 @@
 import kotlin.math.max
 import org.junit.After
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Rule
@@ -953,6 +957,25 @@
             .assertContentDescriptionEquals("hello world")
             .assertTestPropertyEquals("bar")
     }
+
+    @Test
+    fun testRegenerateSemanticsId() {
+        var reuseKey by mutableStateOf(0)
+        rule.setContent {
+            ReusableContent(reuseKey) {
+                Box(
+                    Modifier.testTag(TestTag)
+                )
+            }
+        }
+        val oldId = rule.onNodeWithTag(TestTag).fetchSemanticsNode().id
+        rule.runOnIdle {
+            reuseKey = 1
+        }
+        val newId = rule.onNodeWithTag(TestTag).fetchSemanticsNode().id
+
+        assertNotEquals(oldId, newId)
+    }
 }
 
 private fun SemanticsNodeInteraction.assertDoesNotHaveProperty(property: SemanticsPropertyKey<*>) {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
index aa215e9..574419a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
@@ -82,7 +82,7 @@
     // subcompose multiple times into the same LayoutNode and define offsets.
     private val isVirtual: Boolean = false,
     // The unique semantics ID that is used by all semantics modifiers attached to this LayoutNode.
-    override val semanticsId: Int = generateSemanticsId()
+    override var semanticsId: Int = generateSemanticsId()
 ) : ComposeNodeLifecycleCallback,
     Remeasurement,
     OwnerScope,
@@ -1321,6 +1321,7 @@
         }
         // resetModifierState detaches all nodes, so we need to re-attach them upon reuse.
         nodes.attach()
+        semanticsId = generateSemanticsId()
     }
 
     override fun onDeactivate() {