Merge "Provide composition local map to layout nodes in backwards compatible way" into androidx-main am: 71d9446cbf

Original change: https://android-review.googlesource.com/c/platform/frameworks/support/+/2506542

Change-Id: I6bf3527db54771b4cd891a16d2adc8eb99348d67
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/compose/ui/ui/api/current.ignore b/compose/ui/ui/api/current.ignore
index 2001639..750e9a7 100644
--- a/compose/ui/ui/api/current.ignore
+++ b/compose/ui/ui/api/current.ignore
@@ -1,3 +1,7 @@
 // Baseline format: 1.0
 RemovedClass: androidx.compose.ui.platform.AndroidComposeView_androidKt:
     Removed class androidx.compose.ui.platform.AndroidComposeView_androidKt
+
+// see b/275084979 for details. API not actually removed, but metalave confused by JvmName + Deprecated
+RemovedMethod: androidx.compose.ui.ComposedModifierKt#materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier):
+    Removed method androidx.compose.ui.ComposedModifierKt.materialize(androidx.compose.runtime.Composer,androidx.compose.ui.Modifier)
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 5a1455d..7d40ab0 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -113,7 +113,8 @@
 
   public final class ComposedModifierKt {
     method public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
-    method public static androidx.compose.ui.Modifier materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+    method public static androidx.compose.ui.Modifier materializeModifier(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+    method @Deprecated public static androidx.compose.ui.Modifier materializeWithCompositionLocalInjection(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
   }
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface Modifier {
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index fa8d56d..ac7252f 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -117,7 +117,8 @@
     method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, Object? key2, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
     method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object? key1, Object? key2, Object? key3, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
     method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, String fullyQualifiedName, Object![]? keys, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
-    method public static androidx.compose.ui.Modifier materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+    method public static androidx.compose.ui.Modifier materializeModifier(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+    method @Deprecated public static androidx.compose.ui.Modifier materializeWithCompositionLocalInjection(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
   }
 
   @kotlin.RequiresOptIn(message="This API is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalComposeUiApi {
diff --git a/compose/ui/ui/api/restricted_current.ignore b/compose/ui/ui/api/restricted_current.ignore
index 2001639..ef0ff9c 100644
--- a/compose/ui/ui/api/restricted_current.ignore
+++ b/compose/ui/ui/api/restricted_current.ignore
@@ -1,3 +1,7 @@
 // Baseline format: 1.0
 RemovedClass: androidx.compose.ui.platform.AndroidComposeView_androidKt:
     Removed class androidx.compose.ui.platform.AndroidComposeView_androidKt
+
+
+RemovedMethod: androidx.compose.ui.ComposedModifierKt#materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier):
+    Removed method androidx.compose.ui.ComposedModifierKt.materialize(androidx.compose.runtime.Composer,androidx.compose.ui.Modifier)
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index c8fa151..60531e4 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -113,7 +113,8 @@
 
   public final class ComposedModifierKt {
     method public static androidx.compose.ui.Modifier composed(androidx.compose.ui.Modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.platform.InspectorInfo,kotlin.Unit> inspectorInfo, kotlin.jvm.functions.Function1<? super androidx.compose.ui.Modifier,? extends androidx.compose.ui.Modifier> factory);
-    method public static androidx.compose.ui.Modifier materialize(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+    method public static androidx.compose.ui.Modifier materializeModifier(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
+    method @Deprecated public static androidx.compose.ui.Modifier materializeWithCompositionLocalInjection(androidx.compose.runtime.Composer, androidx.compose.ui.Modifier modifier);
   }
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface Modifier {
@@ -1956,7 +1957,8 @@
     method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static inline void Layout(java.util.List<? extends kotlin.jvm.functions.Function0<kotlin.Unit>> contents, optional androidx.compose.ui.Modifier modifier, androidx.compose.ui.layout.MultiContentMeasurePolicy measurePolicy);
     method @Deprecated @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static void MultiMeasureLayout(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content, androidx.compose.ui.layout.MeasurePolicy measurePolicy);
     method @kotlin.PublishedApi internal static kotlin.jvm.functions.Function0<kotlin.Unit> combineAsVirtualLayouts(java.util.List<? extends kotlin.jvm.functions.Function0<kotlin.Unit>> contents);
-    method @kotlin.PublishedApi internal static kotlin.jvm.functions.Function1<androidx.compose.runtime.SkippableUpdater<androidx.compose.ui.node.ComposeUiNode>,kotlin.Unit> materializerOf(androidx.compose.ui.Modifier modifier);
+    method @Deprecated @kotlin.PublishedApi internal static kotlin.jvm.functions.Function1<androidx.compose.runtime.SkippableUpdater<androidx.compose.ui.node.ComposeUiNode>,kotlin.Unit> materializerOf(androidx.compose.ui.Modifier modifier);
+    method @kotlin.PublishedApi internal static kotlin.jvm.functions.Function1<androidx.compose.runtime.SkippableUpdater<androidx.compose.ui.node.ComposeUiNode>,kotlin.Unit> modifierMaterializerOf(androidx.compose.ui.Modifier modifier);
   }
 
   @kotlin.jvm.JvmDefaultWithCompatibility public interface LayoutModifier extends androidx.compose.ui.Modifier.Element {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/CompositionLocalMapInjectionTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/CompositionLocalMapInjectionTest.kt
new file mode 100644
index 0000000..3526180
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/CompositionLocalMapInjectionTest.kt
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.modifier
+
+import android.view.View
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Applier
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.ReusableComposeNode
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.runtime.currentComposer
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.UiComposable
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.materializerOfWithCompositionLocalInjection
+import androidx.compose.ui.materializeWithCompositionLocalInjectionInternal
+import androidx.compose.ui.node.ComposeUiNode
+import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ObserverNode
+import androidx.compose.ui.node.currentValueOf
+import androidx.compose.ui.node.observeReads
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class CompositionLocalMapInjectionTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun consumeInDraw() {
+        // No assertions needed. This not crashing is the test
+        rule.setContent {
+            CompositionLocalProvider(SomeLocal provides 1) {
+                OldBox(modifierOf { ConsumeInDrawNode() }.size(10.dp))
+            }
+        }
+    }
+
+    @Test
+    fun consumeInLayout() {
+        // No assertions needed. This not crashing is the test
+        rule.setContent {
+            CompositionLocalProvider(SomeLocal provides 1) {
+                OldBox(modifierOf { ConsumeInLayoutNode() }.size(10.dp))
+            }
+        }
+    }
+
+    @Test
+    fun consumeInAttach() {
+        // No assertions needed. This not crashing is the test
+        rule.setContent {
+            CompositionLocalProvider(SomeLocal provides 1) {
+                OldBox(modifierOf { ConsumeInAttachNode() }.size(10.dp))
+            }
+        }
+    }
+
+    @Test
+    fun consumeInDrawGetsNotifiedOfChanges() {
+        val node = ConsumeInDrawNode()
+        var state by mutableStateOf(1)
+        rule.setContent {
+            CompositionLocalProvider(SomeLocal provides state) {
+                OldBox(modifierOf { node }.size(10.dp))
+            }
+        }
+        assertThat(node.int).isEqualTo(state)
+        state = 2
+        rule.runOnIdle {
+            assertThat(node.int).isEqualTo(state)
+        }
+    }
+
+    @Test
+    fun consumeInLayoutGetsNotifiedOfChanges() {
+        val node = ConsumeInLayoutNode()
+        var state by mutableStateOf(1)
+        rule.setContent {
+            CompositionLocalProvider(SomeLocal provides state) {
+                OldBox(modifierOf { node }.size(10.dp))
+            }
+        }
+        assertThat(node.int).isEqualTo(state)
+        state = 2
+        rule.runOnIdle {
+            assertThat(node.int).isEqualTo(state)
+        }
+    }
+
+    @Test
+    fun consumeInAttachGetsNotifiedOfChanges() {
+        val node = ConsumeInAttachNode()
+        var state by mutableStateOf(1)
+        rule.setContent {
+            CompositionLocalProvider(SomeLocal provides state) {
+                OldBox(modifierOf { node }.size(10.dp))
+            }
+        }
+        assertThat(node.int).isEqualTo(state)
+        state = 2
+        rule.runOnIdle {
+            assertThat(node.int).isEqualTo(state)
+        }
+    }
+
+    @Test
+    fun consumeInDrawSkippableUpdate() {
+        // No assertions needed. This not crashing is the test
+        rule.setContent {
+            CompositionLocalProvider(SomeLocal provides 1) {
+                OldBoxSkippableUpdate(modifierOf { ConsumeInDrawNode() }.size(10.dp))
+            }
+        }
+    }
+
+    @Test
+    fun consumeInLayoutSkippableUpdate() {
+        // No assertions needed. This not crashing is the test
+        rule.setContent {
+            CompositionLocalProvider(SomeLocal provides 1) {
+                OldBoxSkippableUpdate(modifierOf { ConsumeInLayoutNode() }.size(10.dp))
+            }
+        }
+    }
+
+    @Test
+    fun consumeInAttachSkippableUpdate() {
+        // No assertions needed. This not crashing is the test
+        rule.setContent {
+            CompositionLocalProvider(SomeLocal provides 1) {
+                OldBoxSkippableUpdate(modifierOf { ConsumeInAttachNode() }.size(10.dp))
+            }
+        }
+    }
+
+    @Test
+    fun consumeInDrawGetsNotifiedOfChangesSkippableUpdate() {
+        val node = ConsumeInDrawNode()
+        var state by mutableStateOf(1)
+        rule.setContent {
+            CompositionLocalProvider(SomeLocal provides state) {
+                OldBoxSkippableUpdate(modifierOf { node }.size(10.dp))
+            }
+        }
+        assertThat(node.int).isEqualTo(state)
+        state = 2
+        rule.runOnIdle {
+            assertThat(node.int).isEqualTo(state)
+        }
+    }
+
+    @Test
+    fun consumeInLayoutGetsNotifiedOfChangesSkippableUpdate() {
+        val node = ConsumeInLayoutNode()
+        var state by mutableStateOf(1)
+        rule.setContent {
+            CompositionLocalProvider(SomeLocal provides state) {
+                OldBoxSkippableUpdate(modifierOf { node }.size(10.dp))
+            }
+        }
+        assertThat(node.int).isEqualTo(state)
+        state = 2
+        rule.runOnIdle {
+            assertThat(node.int).isEqualTo(state)
+        }
+    }
+
+    @Test
+    fun consumeInAttachGetsNotifiedOfChangesSkippableUpdate() {
+        val node = ConsumeInAttachNode()
+        var state by mutableStateOf(1)
+        rule.setContent {
+            CompositionLocalProvider(SomeLocal provides state) {
+                OldBoxSkippableUpdate(modifierOf { node }.size(10.dp))
+            }
+        }
+        assertThat(node.int).isEqualTo(state)
+        state = 2
+        rule.runOnIdle {
+            assertThat(node.int).isEqualTo(state)
+        }
+    }
+}
+
+val SomeLocal = compositionLocalOf<Int> { error("unprovided value") }
+
+inline fun <reified T : Modifier.Node> modifierOf(crossinline fn: () -> T) =
+    object : ModifierNodeElement<T>() {
+        override fun create() = fn()
+        override fun hashCode() = System.identityHashCode(this)
+        override fun equals(other: Any?) = other === this
+        override fun update(node: T) = node
+    }
+
+class ConsumeInDrawNode : CompositionLocalConsumerModifierNode, DrawModifierNode, Modifier.Node() {
+    var view: View? = null
+    var int: Int? = null
+    override fun ContentDrawScope.draw() {
+        // Consume Static local
+        view = currentValueOf(LocalView)
+        // Consume Freshly Provided Local
+        int = currentValueOf(SomeLocal)
+    }
+}
+
+class ConsumeInLayoutNode :
+    CompositionLocalConsumerModifierNode,
+    LayoutModifierNode,
+    Modifier.Node() {
+    var view: View? = null
+    var int: Int? = null
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        // Consume Static local
+        view = currentValueOf(LocalView)
+        // Consume Freshly Provided Local
+        int = currentValueOf(SomeLocal)
+        val placeable = measurable.measure(constraints)
+        return layout(constraints.minWidth, constraints.maxWidth) {
+            placeable.place(0, 0)
+        }
+    }
+}
+
+class ConsumeInAttachNode : CompositionLocalConsumerModifierNode, ObserverNode, Modifier.Node() {
+    var view: View? = null
+    var int: Int? = null
+    private fun readLocals() {
+        // Consume Static local
+        view = currentValueOf(LocalView)
+        // Consume Freshly Provided Local
+        int = currentValueOf(SomeLocal)
+    }
+    override fun onAttach() {
+        observeReads { readLocals() }
+    }
+    override fun onObservedReadsChanged() {
+        observeReads { readLocals() }
+    }
+}
+
+// This composable is intentionally written to look like the "old" version of Layout, before
+// aosp/2318839. This function allows us to emulate what a module targeting an older version of
+// compose UI would have inlined into their function body. See b/275067189 for more details.
+@UiComposable
+@Composable
+inline fun OldLayoutSkippableUpdate(
+    content: @Composable @UiComposable () -> Unit,
+    modifier: Modifier = Modifier,
+    measurePolicy: MeasurePolicy
+) {
+    val density = LocalDensity.current
+    val layoutDirection = LocalLayoutDirection.current
+    val viewConfiguration = LocalViewConfiguration.current
+    @Suppress("DEPRECATION")
+    ReusableComposeNode<ComposeUiNode, Applier<Any>>(
+        factory = ComposeUiNode.Constructor,
+        update = {
+            set(measurePolicy, ComposeUiNode.SetMeasurePolicy)
+            set(density, ComposeUiNode.SetDensity)
+            set(layoutDirection, ComposeUiNode.SetLayoutDirection)
+            set(viewConfiguration, ComposeUiNode.SetViewConfiguration)
+        },
+        // The old version of Layout called a function called "materializerOf". The function below
+        // has the same JVM signature as that function used to have, so the code that this source
+        // generates will be essentially identical to what will have been generated in older versions
+        // of UI, despite this name being different now.
+        skippableUpdate = materializerOfWithCompositionLocalInjection(modifier),
+        content = content
+    )
+}
+
+@Suppress("NOTHING_TO_INLINE")
+@Composable
+@UiComposable
+internal inline fun OldLayout(
+    modifier: Modifier = Modifier,
+    measurePolicy: MeasurePolicy
+) {
+    val density = LocalDensity.current
+    val layoutDirection = LocalLayoutDirection.current
+    val viewConfiguration = LocalViewConfiguration.current
+    // The old version of Layout called a function called "materialize". The function below
+    // has the same JVM signature as that function used to have, so the code that this source
+    // generates will be essentially identical to what will have been generated in older versions
+    // of UI, despite this name being different now.
+    val materialized = currentComposer.materializeWithCompositionLocalInjectionInternal(modifier)
+    ReusableComposeNode<ComposeUiNode, Applier<Any>>(
+        factory = ComposeUiNode.Constructor,
+        update = {
+            set(measurePolicy, ComposeUiNode.SetMeasurePolicy)
+            set(density, ComposeUiNode.SetDensity)
+            set(layoutDirection, ComposeUiNode.SetLayoutDirection)
+            set(viewConfiguration, ComposeUiNode.SetViewConfiguration)
+            set(materialized, ComposeUiNode.SetModifier)
+        },
+    )
+}
+private val EmptyBoxMeasurePolicy = MeasurePolicy { _, constraints ->
+    layout(constraints.minWidth, constraints.minHeight) {}
+}
+
+@Composable fun OldBoxSkippableUpdate(modifier: Modifier = Modifier) {
+    OldLayoutSkippableUpdate({ }, modifier, EmptyBoxMeasurePolicy)
+}
+
+@Composable fun OldBox(modifier: Modifier = Modifier) {
+    OldLayout(modifier, EmptyBoxMeasurePolicy)
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt
index a06b7ea..a1872aa 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposedModifier.kt
@@ -18,7 +18,10 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composer
+import androidx.compose.runtime.CompositionLocalMap
 import androidx.compose.runtime.Stable
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.requireLayoutNode
 import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.platform.InspectorValueInfo
 import androidx.compose.ui.platform.NoInspectorInfo
@@ -244,6 +247,8 @@
  * You almost certainly do not need to call this function directly.
  */
 @Suppress("ModifierFactoryExtensionFunction")
+// "materialize" JVM name is taken below to solve a backwards-incompatibility
+@JvmName("materializeModifier")
 fun Composer.materialize(modifier: Modifier): Modifier {
     if (modifier.all { it !is ComposedModifier }) {
         return modifier
@@ -273,3 +278,64 @@
     endReplaceableGroup()
     return result
 }
+
+/**
+ * This class is only used for backwards compatibility purposes to inject the CompositionLocalMap
+ * into LayoutNodes that were created by inlined code of older versions of the Layout composable.
+ * More details can be found at https://issuetracker.google.com/275067189
+ */
+internal class CompositionLocalMapInjectionNode(map: CompositionLocalMap) : Modifier.Node() {
+    var map: CompositionLocalMap = map
+        set(value) {
+            field = value
+            requireLayoutNode().compositionLocalMap = value
+        }
+    override fun onAttach() {
+        requireLayoutNode().compositionLocalMap = map
+    }
+}
+
+/**
+ * This class is only used for backwards compatibility purposes to inject the CompositionLocalMap
+ * into LayoutNodes that were created by inlined code of older versions of the Layout composable.
+ * More details can be found at https://issuetracker.google.com/275067189
+ */
+internal class CompositionLocalMapInjectionElement(
+    val map: CompositionLocalMap
+) : ModifierNodeElement<CompositionLocalMapInjectionNode>() {
+    override fun create() = CompositionLocalMapInjectionNode(map)
+    override fun update(node: CompositionLocalMapInjectionNode) = node.also { it.map = map }
+    override fun hashCode(): Int = map.hashCode()
+    override fun equals(other: Any?): Boolean {
+        return other is CompositionLocalMapInjectionElement && other.map == map
+    }
+    override fun InspectorInfo.inspectableProperties() {
+        name = "<Injected CompositionLocalMap>"
+    }
+}
+
+/**
+ * This function exists solely for solving a backwards-incompatibility with older compilations
+ * that used an older version of the `Layout` composable. New code paths should not call this.
+ * More details can be found at https://issuetracker.google.com/275067189
+ */
+@Suppress("ModifierFactoryExtensionFunction")
+@JvmName("materialize")
+@Deprecated(
+    "Kept for backwards compatibility only. If you are recompiling, use materialize.",
+    ReplaceWith("materialize"),
+    DeprecationLevel.HIDDEN
+)
+fun Composer.materializeWithCompositionLocalInjection(modifier: Modifier): Modifier =
+    materializeWithCompositionLocalInjectionInternal(modifier)
+
+// This method is here to be called from tests since the deprecated hidden API cannot be.
+@Suppress("ModifierFactoryExtensionFunction")
+internal fun Composer.materializeWithCompositionLocalInjectionInternal(
+    modifier: Modifier
+): Modifier {
+    return if (modifier === Modifier)
+        modifier
+    else
+        materialize(CompositionLocalMapInjectionElement(currentCompositionLocalMap).then(modifier))
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt
index 001261d..2b45ead 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/Layout.kt
@@ -28,6 +28,7 @@
 import androidx.compose.ui.UiComposable
 import androidx.compose.ui.graphics.GraphicsLayerScope
 import androidx.compose.ui.materialize
+import androidx.compose.ui.materializeWithCompositionLocalInjectionInternal
 import androidx.compose.ui.node.ComposeUiNode
 import androidx.compose.ui.node.LayoutNode
 import androidx.compose.ui.unit.Constraints
@@ -173,7 +174,13 @@
     }
 }
 
+/**
+ * This function uses a JVM-Name because the original name now has a different implementation for
+ * backwards compatibility [materializerOfWithCompositionLocalInjection].
+ * More details can be found at https://issuetracker.google.com/275067189
+ */
 @PublishedApi
+@JvmName("modifierMaterializerOf")
 internal fun materializerOf(
     modifier: Modifier
 ): @Composable SkippableUpdater<ComposeUiNode>.() -> Unit = {
@@ -183,6 +190,26 @@
     }
 }
 
+/**
+ * This function exists solely for solving a backwards-incompatibility with older compilations
+ * that used an older version of the `Layout` composable. New code paths should not call this.
+ * More details can be found at https://issuetracker.google.com/275067189
+ */
+@JvmName("materializerOf")
+@Deprecated(
+    "Needed only for backwards compatibility. Do not use.",
+    level = DeprecationLevel.WARNING
+)
+@PublishedApi
+internal fun materializerOfWithCompositionLocalInjection(
+    modifier: Modifier
+): @Composable SkippableUpdater<ComposeUiNode>.() -> Unit = {
+    val materialized = currentComposer.materializeWithCompositionLocalInjectionInternal(modifier)
+    update {
+        set(materialized, ComposeUiNode.SetModifier)
+    }
+}
+
 @Suppress("ComposableLambdaParameterPosition")
 @Composable
 @UiComposable