Merge "Revert "Allow newly created state objects to be accessible in other snapshots"" into androidx-main
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index 0163760..886c585 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -864,14 +864,12 @@
     property public abstract boolean readOnly;
     property public abstract androidx.compose.runtime.snapshots.Snapshot root;
     field public static final androidx.compose.runtime.snapshots.Snapshot.Companion Companion;
-    field public static final int PreexistingSnapshotId = 1; // 0x1
   }
 
   public static final class Snapshot.Companion {
     method public androidx.compose.runtime.snapshots.Snapshot getCurrent();
     method public inline <T> T global(kotlin.jvm.functions.Function0<? extends T> block);
     method public boolean isApplyObserverNotificationPending();
-    method public boolean isInSnapshot();
     method public void notifyObjectsInitialized();
     method public <T> T observe(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver, optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver, kotlin.jvm.functions.Function0<? extends T> block);
     method @SuppressCompatibility @androidx.compose.runtime.InternalComposeApi public int openSnapshotCount();
@@ -884,7 +882,6 @@
     method public inline <T> T withoutReadObservation(kotlin.jvm.functions.Function0<? extends T> block);
     property public final androidx.compose.runtime.snapshots.Snapshot current;
     property public final boolean isApplyObserverNotificationPending;
-    property public final boolean isInSnapshot;
   }
 
   public final class SnapshotApplyConflictException extends java.lang.Exception {
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 5240627..d282011 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -901,7 +901,6 @@
     property public abstract boolean readOnly;
     property public abstract androidx.compose.runtime.snapshots.Snapshot root;
     field public static final androidx.compose.runtime.snapshots.Snapshot.Companion Companion;
-    field public static final int PreexistingSnapshotId = 1; // 0x1
   }
 
   public static final class Snapshot.Companion {
@@ -909,7 +908,6 @@
     method public androidx.compose.runtime.snapshots.Snapshot getCurrent();
     method public inline <T> T global(kotlin.jvm.functions.Function0<? extends T> block);
     method public boolean isApplyObserverNotificationPending();
-    method public boolean isInSnapshot();
     method @kotlin.PublishedApi internal androidx.compose.runtime.snapshots.Snapshot makeCurrentNonObservable(androidx.compose.runtime.snapshots.Snapshot? previous);
     method public void notifyObjectsInitialized();
     method public <T> T observe(optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? readObserver, optional kotlin.jvm.functions.Function1<java.lang.Object,kotlin.Unit>? writeObserver, kotlin.jvm.functions.Function0<? extends T> block);
@@ -927,7 +925,6 @@
     property public final androidx.compose.runtime.snapshots.Snapshot current;
     property @kotlin.PublishedApi internal final androidx.compose.runtime.snapshots.Snapshot? currentThreadSnapshot;
     property public final boolean isApplyObserverNotificationPending;
-    property public final boolean isInSnapshot;
   }
 
   public final class SnapshotApplyConflictException extends java.lang.Exception {
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
index 3354ba0..cdea706 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
@@ -130,13 +130,7 @@
     value: Double
 ) : StateObjectImpl(), MutableDoubleState, SnapshotMutableState<Double> {
 
-    private var next = DoubleStateStateRecord(value).also {
-        if (Snapshot.isInSnapshot) {
-            it.next = DoubleStateStateRecord(value).also { next ->
-                next.snapshotId = Snapshot.PreexistingSnapshotId
-            }
-        }
-    }
+    private var next = DoubleStateStateRecord(value)
 
     override val firstStateRecord: StateRecord
         get() = next
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
index ff9a419..e5212f1 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
@@ -126,13 +126,7 @@
     value: Float
 ) : StateObjectImpl(), MutableFloatState, SnapshotMutableState<Float> {
 
-    private var next = FloatStateStateRecord(value).also {
-        if (Snapshot.isInSnapshot) {
-            it.next = FloatStateStateRecord(value).also { next ->
-                next.snapshotId = Snapshot.PreexistingSnapshotId
-            }
-        }
-    }
+    private var next = FloatStateStateRecord(value)
 
     override val firstStateRecord: StateRecord
         get() = next
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
index b01dc0d..75a7db0 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
@@ -129,13 +129,7 @@
     value: Int
 ) : StateObjectImpl(), MutableIntState, SnapshotMutableState<Int> {
 
-    private var next = IntStateStateRecord(value).also {
-        if (Snapshot.isInSnapshot) {
-            it.next = IntStateStateRecord(value).also { next ->
-                next.snapshotId = Snapshot.PreexistingSnapshotId
-            }
-        }
-    }
+    private var next = IntStateStateRecord(value)
 
     override val firstStateRecord: StateRecord
         get() = next
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
index f1270a2..f3b3678 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
@@ -126,13 +126,7 @@
     value: Long
 ) : StateObjectImpl(), MutableLongState, SnapshotMutableState<Long> {
 
-    private var next = LongStateStateRecord(value).also {
-        if (Snapshot.isInSnapshot) {
-            it.next = LongStateStateRecord(value).also { next ->
-                next.snapshotId = Snapshot.PreexistingSnapshotId
-            }
-        }
-    }
+    private var next = LongStateStateRecord(value)
 
     override val firstStateRecord: StateRecord
         get() = next
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
index 9fab395..0bd7bd7 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotState.kt
@@ -139,13 +139,7 @@
             }
         }
 
-    private var next: StateStateRecord<T> = StateStateRecord(value).also {
-        if (Snapshot.isInSnapshot) {
-            it.next = StateStateRecord(value).also { next ->
-                next.snapshotId = Snapshot.PreexistingSnapshotId
-            }
-        }
-    }
+    private var next: StateStateRecord<T> = StateStateRecord(value)
 
     override val firstStateRecord: StateRecord
         get() = next
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
index 5383c37..23e538b 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/Snapshot.kt
@@ -298,11 +298,6 @@
         val current get() = currentSnapshot()
 
         /**
-         * Return `true` if the thread is currently in the context of a snapshot.
-         */
-        val isInSnapshot: Boolean get() = threadSnapshot.get() != null
-
-        /**
          * Returns whether any threads are currently in the process of notifying observers about
          * changes to the global snapshot.
          */
@@ -310,14 +305,6 @@
             get() = pendingApplyObserverCount.get() > 0
 
         /**
-         * All new state objects initial state records should be [PreexistingSnapshotId] which then
-         * allows snapshots outside the creating snapshot to access the object with its initial
-         * state.
-         */
-        @Suppress("ConstPropertyName")
-        const val PreexistingSnapshotId = 1
-
-        /**
          * Take a snapshot of the current value of all state objects. The values are preserved until
          * [Snapshot.dispose] is called on the result.
          *
@@ -1021,15 +1008,6 @@
             // in a nested snapshot that was committed then changed.
             val current = readable(first, snapshotId, invalidSnapshots) ?: return@forEach
             val previous = readable(first, id, start) ?: return@forEach
-            if (previous.snapshotId == PreexistingSnapshotId) {
-                // A previous record might not be found if the state object was created in a
-                // nested snapshot that didn't have any other modifications. The `apply()` for
-                // a nested snapshot considers such snapshots no-op snapshots and just closes them
-                // which allows this object's previous record to be missing or be the record created
-                // during initial construction. In these cases taking applied is the right choice
-                // this indicates there was no conflicting writes.
-                return@forEach
-            }
             if (current != previous) {
                 val applied = readable(first, id, this.invalid) ?: readError()
                 val merged = optimisticMerges?.get(current) ?: run {
@@ -1846,9 +1824,8 @@
  */
 private var openSnapshots = SnapshotIdSet.EMPTY
 
-/** The first snapshot created must be at least on more than the
- * [Snapshot.PreexistingSnapshotId] */
-private var nextSnapshotId = Snapshot.PreexistingSnapshotId + 1
+/** The first snapshot created must be at least on more than the INVALID_SNAPSHOT */
+private var nextSnapshotId = INVALID_SNAPSHOT + 1
 
 /**
  * A tracking table for pinned snapshots. A pinned snapshot is the lowest snapshot id that the
@@ -2192,16 +2169,9 @@
 
     // Otherwise, make a copy of the readable data and mark it as born in this snapshot, making it
     // writable.
-    @Suppress("UNCHECKED_CAST")
-    val newData = sync {
-        // Verify that some other thread didn't already create this.
-        val newReadData = readable(state.firstStateRecord, id, snapshot.invalid) ?: readError()
-        if (newReadData.snapshotId == id)
-            newReadData
-        else newReadData.newWritableRecordLocked(state, snapshot)
-    } as T
+    val newData = readData.newWritableRecord(state, snapshot)
 
-    if (readData.snapshotId != Snapshot.PreexistingSnapshotId) snapshot.recordModified(state)
+    snapshot.recordModified(state)
 
     return newData
 }
@@ -2222,7 +2192,7 @@
     val newData = sync { newOverwritableRecordLocked(state) }
     newData.snapshotId = id
 
-    if (candidate.snapshotId != Snapshot.PreexistingSnapshotId) snapshot.recordModified(state)
+    snapshot.recordModified(state)
 
     return newData
 }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
index c661d37..22fda4b 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateList.kt
@@ -33,15 +33,7 @@
 @Stable
 class SnapshotStateList<T> : StateObject, MutableList<T>, RandomAccess {
     override var firstStateRecord: StateRecord =
-        persistentListOf<T>().let { list ->
-            StateListStateRecord(list).also {
-                if (Snapshot.isInSnapshot) {
-                    it.next = StateListStateRecord(list).also { next ->
-                        next.snapshotId = Snapshot.PreexistingSnapshotId
-                    }
-                }
-            }
-        }
+        StateListStateRecord<T>(persistentListOf())
         private set
 
     override fun prependStateRecord(value: StateRecord) {
@@ -221,7 +213,7 @@
                     oldList = current.list
                 }
                 val newList = block(oldList!!)
-                if (newList === oldList) {
+                if (newList == oldList) {
                     result = false
                     break
                 }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt
index 42701db..0f015a4 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotStateMap.kt
@@ -33,15 +33,7 @@
 @Stable
 class SnapshotStateMap<K, V> : StateObject, MutableMap<K, V> {
     override var firstStateRecord: StateRecord =
-        persistentHashMapOf<K, V>().let { map ->
-            StateMapStateRecord(map).also {
-                if (Snapshot.isInSnapshot) {
-                    it.next = StateMapStateRecord(map).also { next ->
-                        next.snapshotId = Snapshot.PreexistingSnapshotId
-                    }
-                }
-            }
-        }
+        StateMapStateRecord<K, V>(persistentHashMapOf())
         private set
 
     override fun prependStateRecord(value: StateRecord) {
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
index c662be9..9d6add5 100644
--- a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
+++ b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotStateListTests.kt
@@ -587,7 +587,7 @@
 
             repeat(100) { index ->
                 repeat(10) {
-                    assertTrue(list.contains(index * 100 + it), "Missing ${index * 100 + it}")
+                    assertTrue(list.contains(index * 100 + it))
                 }
             }
         }
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
index 687e8d3..340b2b7 100644
--- a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
+++ b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/snapshots/SnapshotTests.kt
@@ -22,12 +22,6 @@
 import androidx.compose.runtime.State
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableDoubleStateOf
-import androidx.compose.runtime.mutableFloatStateOf
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableLongStateOf
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.mutableStateMapOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.neverEqualPolicy
 import androidx.compose.runtime.referentialEqualityPolicy
@@ -43,7 +37,6 @@
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
 import kotlin.test.assertFalse
-import kotlin.test.assertNotEquals
 import kotlin.test.assertNotSame
 import kotlin.test.assertSame
 import kotlin.test.assertTrue
@@ -1272,47 +1265,6 @@
         assertEquals(1, current.writeCount)
     }
 
-    @Test
-    fun testSnapshotStateIsBornAccessible() {
-        fun <T, V> test(create: () -> T, read: (T) -> V, update: (T) -> V) {
-            val snapshot = takeMutableSnapshot()
-            val created: Any? = null
-            val modified = observeChanges(snapshot) {
-                val (state, initial) = snapshot.enter {
-                    val state = create()
-                    state to read(state)
-                }
-
-                // Ensure the value is accessible and has its initial state
-                assertEquals(initial, read(state))
-                val newValue = snapshot.enter {
-                    update(state)
-                }
-
-                // Ensure the test actually modified it
-                assertNotEquals(initial, newValue)
-
-                // Ensure the value still has its initial state
-                assertEquals(initial, read(state))
-                snapshot.apply().check()
-
-                // Ensure the value now has the modified state
-                assertEquals(newValue, read(state))
-            }
-
-            // The object is not considered modified
-            assertFalse(created in modified)
-        }
-
-        test({ mutableStateOf("A") }, { it.value }) { it.value = "B"; "B" }
-        test({ mutableIntStateOf(1) }, { it.value }, { it.value = 2; 2 })
-        test({ mutableLongStateOf(1L) }, { it.value }, { it.value = 2L; 2L })
-        test({ mutableFloatStateOf(1f) }, { it.value }, { it.value = 2f; 2f })
-        test({ mutableDoubleStateOf(1.0) }, { it.value }, { it.value = 2.0; 2.0 })
-        test({ mutableStateListOf<Int>() }, { it.isEmpty() }, { it.add(1); it.isEmpty() })
-        test({ mutableStateMapOf<Int, Int>() }, { it.isEmpty() }, { it[23] = 42; it.isEmpty() })
-    }
-
     private fun usedRecords(state: StateObject): Int {
         var used = 0
         var current: StateRecord? = state.firstStateRecord
@@ -1353,20 +1305,6 @@
     return changes
 }
 
-internal fun observeChanges(snapshot: Snapshot, block: () -> Unit): Set<Any> {
-    var changes = setOf<Any>()
-    val removeObserver = Snapshot.registerApplyObserver { states, changedSnapshot ->
-        if (changedSnapshot == snapshot) changes = states
-    }
-    try {
-        block()
-        Snapshot.sendApplyNotifications()
-    } finally {
-        removeObserver.dispose()
-    }
-    return changes
-}
-
 internal fun readsOf(block: () -> Unit): Int {
     var reads = 0
     val snapshot = takeSnapshot(readObserver = { reads++ })