Correctly transform @Composable getValue operators

Allows usage of @Composable annotation on getValue operator and marks generated getter for delegate as composable in IR to ensure it is correctly transformed later.

Fixes: 181718338
Test: Updated compiler tests
Change-Id: Ib40ff82e608811673f64d80d2accd5121e8a4b7c
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
index f70db8d..7aa9356 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
@@ -945,10 +945,10 @@
             class StableDelegateProp {
               var p1: StableDelegate = StableDelegate()
                 get() {
-                  return <this>.p1%delegate.getValue()
+                  return <this>.p1%delegate.getValue(<this>, ::p1)
                 }
                 set(value) {
-                  return <this>.p1%delegate.setValue()
+                  return <this>.p1%delegate.setValue(<this>, ::p1, <set-?>)
                 }
               static val %stable: Int = 0
             }
@@ -956,10 +956,10 @@
             class UnstableDelegateProp {
               var p1: UnstableDelegate = UnstableDelegate()
                 get() {
-                  return <this>.p1%delegate.getValue()
+                  return <this>.p1%delegate.getValue(<this>, ::p1)
                 }
                 set(value) {
-                  return <this>.p1%delegate.setValue()
+                  return <this>.p1%delegate.setValue(<this>, ::p1, <set-?>)
                 }
               static val %stable: Int = 8
             }
@@ -1166,10 +1166,10 @@
             class StableDelegateProp {
               var p1: StableDelegate = StableDelegate()
                 get() {
-                  return <this>.p1%delegate.getValue()
+                  return <this>.p1%delegate.getValue(<this>, ::p1)
                 }
                 set(value) {
-                  return <this>.p1%delegate.setValue()
+                  return <this>.p1%delegate.setValue(<this>, ::p1, <set-?>)
                 }
               static val %stable: Int = 0
             }
@@ -1177,10 +1177,10 @@
             class UnstableDelegateProp {
               var p1: UnstableDelegate = UnstableDelegate()
                 get() {
-                  return <this>.p1%delegate.getValue()
+                  return <this>.p1%delegate.getValue(<this>, ::p1)
                 }
                 set(value) {
-                  return <this>.p1%delegate.setValue()
+                  return <this>.p1%delegate.setValue(<this>, ::p1, <set-?>)
                 }
               static val %stable: Int = UnstableDelegate.%stable
             }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
index 8e53da5..bbe9866 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
@@ -1668,4 +1668,23 @@
             }
         """
     )
+
+    @Test
+    fun testComposableInlineFieldDelegate_noPropertyRefInit() = validateBytecode(
+        """
+            import kotlin.reflect.KProperty
+
+            class FooInline
+
+            @Composable
+            inline operator fun FooInline.getValue(thisRef: Any?, property: KProperty<*>) = 0
+
+            @Composable fun Test(foo: FooInline): Int {
+                val value by foo
+                return value
+            }
+        """,
+    ) {
+        assertFalse(it.contains("INVOKESTATIC kotlin/jvm/internal/Reflection.property0 (Lkotlin/jvm/internal/PropertyReference0;)Lkotlin/reflect/KProperty0;"))
+    }
 }
\ No newline at end of file
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 1dc65de..e3ca786 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
@@ -88,7 +88,7 @@
               if (isTraceInProgress()) {
                 traceEventStart(<>, %changed, -1, <>)
               }
-              bar
+              <get-bar>(%composer, 0)
               if (isTraceInProgress()) {
                 traceEventEnd()
               }
@@ -680,4 +680,287 @@
             """.trimIndent()
         )
     }
+
+    @Test
+    fun testDelegateCall() {
+        composerParam(
+            """
+                import kotlin.reflect.KProperty
+
+                class Foo
+                @Composable
+                operator fun Foo.getValue(thisObj: Any?, property: KProperty<*>): Foo = this
+
+                class FooDelegate {
+                    @Composable
+                    operator fun getValue(thisObj: Any?, property: KProperty<*>): FooDelegate = this
+                }
+
+                class Bar {
+                    val foo by Foo()
+                }
+
+                @Composable
+                fun test() {
+                    val foo by Foo()
+                    val fooDelegate by FooDelegate()
+                    val bar = Bar()
+                    println(foo)
+                    println(fooDelegate)
+                    println(bar.foo)
+                }
+            """,
+            """
+                @StabilityInferred(parameters = 0)
+                class Foo {
+                  static val %stable: Int = 0
+                }
+                @Composable
+                fun Foo.getValue(thisObj: Any?, property: KProperty<*>, %composer: Composer?, %changed: Int): Foo {
+                  %composer.startReplaceableGroup(<>)
+                  sourceInformation(%composer, "C(getValue)P(1):Test.kt#2487m")
+                  if (isTraceInProgress()) {
+                    traceEventStart(<>, %changed, -1, <>)
+                  }
+                  val tmp0 = <this>
+                  if (isTraceInProgress()) {
+                    traceEventEnd()
+                  }
+                  %composer.endReplaceableGroup()
+                  return tmp0
+                }
+                @StabilityInferred(parameters = 0)
+                class FooDelegate {
+                  @Composable
+                  fun getValue(thisObj: Any?, property: KProperty<*>, %composer: Composer?, %changed: Int): FooDelegate {
+                    %composer.startReplaceableGroup(<>)
+                    sourceInformation(%composer, "C(getValue)P(1):Test.kt#2487m")
+                    if (isTraceInProgress()) {
+                      traceEventStart(<>, %changed, -1, <>)
+                    }
+                    val tmp0 = <this>
+                    if (isTraceInProgress()) {
+                      traceEventEnd()
+                    }
+                    %composer.endReplaceableGroup()
+                    return tmp0
+                  }
+                  static val %stable: Int = 0
+                }
+                @StabilityInferred(parameters = 0)
+                class Bar {
+                  val foo: Foo = Foo()
+                    @Composable @JvmName(name = "getFoo")
+                    get() {
+                      sourceInformationMarkerStart(%composer, <>, "C<Foo()>:Test.kt#2487m")
+                      if (isTraceInProgress()) {
+                        traceEventStart(<>, %changed, -1, <>)
+                      }
+                      val tmp0 = <this>.foo%delegate.getValue(<this>, ::foo, %composer, 0b001000000000 or 0b01110000 and %changed shl 0b0011)
+                      if (isTraceInProgress()) {
+                        traceEventEnd()
+                      }
+                      sourceInformationMarkerEnd(%composer)
+                      return tmp0
+                    }
+                  static val %stable: Int = 0
+                }
+                @Composable
+                fun test(%composer: Composer?, %changed: Int) {
+                  %composer = %composer.startRestartGroup(<>)
+                  sourceInformation(%composer, "C(test)*<foo>,<fooDel...>,<foo>:Test.kt#2487m")
+                  if (%changed !== 0 || !%composer.skipping) {
+                    if (isTraceInProgress()) {
+                      traceEventStart(<>, %changed, -1, <>)
+                    }
+                    val foo by {
+                      val foo%delegate = Foo()
+                      @Composable
+                      get(%composer: Composer?, %changed: Int) {
+                        sourceInformationMarkerStart(%composer, <>, "C<Foo()>:Test.kt#2487m")
+                        if (isTraceInProgress()) {
+                          traceEventStart(<>, %changed, -1, <>)
+                        }
+                        val tmp0 = foo%delegate.getValue(null, ::foo%delegate, %composer, 0b00110000)
+                        if (isTraceInProgress()) {
+                          traceEventEnd()
+                        }
+                        sourceInformationMarkerEnd(%composer)
+                        return tmp0
+                      }
+                    }
+                    val fooDelegate by {
+                      val fooDelegate%delegate = FooDelegate()
+                      @Composable
+                      get(%composer: Composer?, %changed: Int) {
+                        sourceInformationMarkerStart(%composer, <>, "C<FooDel...>:Test.kt#2487m")
+                        if (isTraceInProgress()) {
+                          traceEventStart(<>, %changed, -1, <>)
+                        }
+                        val tmp0 = fooDelegate%delegate.getValue(null, ::fooDelegate%delegate, %composer, 0b0110)
+                        if (isTraceInProgress()) {
+                          traceEventEnd()
+                        }
+                        sourceInformationMarkerEnd(%composer)
+                        return tmp0
+                      }
+                    }
+                    val bar = Bar()
+                    println(<get-foo>(%composer, 0))
+                    println(<get-fooDelegate>(%composer, 0))
+                    println(bar.<get-foo>(%composer, 0))
+                    if (isTraceInProgress()) {
+                      traceEventEnd()
+                    }
+                  } else {
+                    %composer.skipToGroupEnd()
+                  }
+                  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                    test(%composer, updateChangedFlags(%changed or 0b0001))
+                  }
+                }
+            """,
+        )
+    }
+
+    @Test
+    fun testUnstableDelegateCall() = composerParam(
+        """
+                import kotlin.reflect.KProperty
+
+                class Foo {
+                    var unstableField: Int = 0
+                }
+
+                @Composable
+                inline operator fun Foo.getValue(thisObj: Any?, property: KProperty<*>): Foo = this
+
+                @Composable
+                fun test() {
+                    val foo by Foo()
+                    println(foo)
+                }
+            """,
+        """
+            @StabilityInferred(parameters = 0)
+            class Foo {
+              var unstableField: Int = 0
+              static val %stable: Int = 8
+            }
+            @Composable
+            fun Foo.getValue(thisObj: Any?, property: KProperty<*>, %composer: Composer?, %changed: Int): Foo {
+              %composer.startReplaceableGroup(<>)
+              sourceInformation(%composer, "CC(getValue)P(1):Test.kt#2487m")
+              val tmp0 = <this>
+              %composer.endReplaceableGroup()
+              return tmp0
+            }
+            @Composable
+            fun test(%composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(test)*<foo>:Test.kt#2487m")
+              if (%changed !== 0 || !%composer.skipping) {
+                if (isTraceInProgress()) {
+                  traceEventStart(<>, %changed, -1, <>)
+                }
+                val foo by {
+                  val foo%delegate = Foo()
+                  @Composable
+                  get(%composer: Composer?, %changed: Int) {
+                    sourceInformationMarkerStart(%composer, <>, "C<Foo()>:Test.kt#2487m")
+                    if (isTraceInProgress()) {
+                      traceEventStart(<>, %changed, -1, <>)
+                    }
+                    val tmp0 = foo%delegate.getValue(null, ::foo%delegate, %composer, 0b00111000)
+                    if (isTraceInProgress()) {
+                      traceEventEnd()
+                    }
+                    sourceInformationMarkerEnd(%composer)
+                    return tmp0
+                  }
+                }
+                println(<get-foo>(%composer, 0))
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                test(%composer, updateChangedFlags(%changed or 0b0001))
+              }
+            }
+        """
+    )
+
+    @Test
+    fun testStableDelegateCall() = composerParam(
+        """
+            import kotlin.reflect.KProperty
+
+            class Foo
+
+            @Composable
+            inline operator fun Foo.getValue(thisObj: Any?, property: KProperty<*>): Foo = this
+
+            @Composable
+            fun test(foo: Foo) {
+                val delegated by foo
+                used(delegated)
+            }
+        """,
+        """
+            @StabilityInferred(parameters = 0)
+            class Foo {
+              static val %stable: Int = 0
+            }
+            @Composable
+            fun Foo.getValue(thisObj: Any?, property: KProperty<*>, %composer: Composer?, %changed: Int): Foo {
+              %composer.startReplaceableGroup(<>)
+              sourceInformation(%composer, "CC(getValue)P(1):Test.kt#2487m")
+              val tmp0 = <this>
+              %composer.endReplaceableGroup()
+              return tmp0
+            }
+            @Composable
+            fun test(foo: Foo, %composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(test)<delega...>:Test.kt#2487m")
+              val %dirty = %changed
+              if (%changed and 0b1110 === 0) {
+                %dirty = %dirty or if (%composer.changed(foo)) 0b0100 else 0b0010
+              }
+              if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                if (isTraceInProgress()) {
+                  traceEventStart(<>, %dirty, -1, <>)
+                }
+                val delegated by {
+                  val delegated%delegate = foo
+                  @Composable
+                  get(%composer: Composer?, %changed: Int) {
+                    sourceInformationMarkerStart(%composer, <>, "C<foo>:Test.kt#2487m")
+                    if (isTraceInProgress()) {
+                      traceEventStart(<>, %changed, -1, <>)
+                    }
+                    val tmp0 = delegated%delegate.getValue(null, ::delegated%delegate, %composer, 0b00110000 or 0b1110 and %dirty)
+                    if (isTraceInProgress()) {
+                      traceEventEnd()
+                    }
+                    sourceInformationMarkerEnd(%composer)
+                    return tmp0
+                  }
+                }
+                used(<get-delegated>(%composer, 0))
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                test(foo, %composer, updateChangedFlags(%changed or 0b0001))
+              }
+            }
+        """
+    )
 }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
index 82dcf95..48ceddc 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
@@ -5488,7 +5488,7 @@
                 if (isTraceInProgress()) {
                   traceEventStart(<>, %changed, -1, "Test (Test.kt:16)")
                 }
-                val c = current
+                val c = <get-current>(%composer, 0)
                 val cl = calculateSometing(%composer, 0)
                 Layout({ %composer: Composer?, %changed: Int ->
                   sourceInformationMarkerStart(%composer, <>, "C<Text("...>:Test.kt")
@@ -5579,7 +5579,7 @@
                   if (isTraceInProgress()) {
                     traceEventStart(<>, %changed, -1, "HolderHolder.<get-current> (Test.kt:16)")
                   }
-                  val tmp0 = _currentHolder.current
+                  val tmp0 = _currentHolder.<get-current>(%composer, 0)
                   if (isTraceInProgress()) {
                     traceEventEnd()
                   }
@@ -5611,7 +5611,7 @@
                 if (isTraceInProgress()) {
                   traceEventStart(<>, %changed, -1, "Test (Test.kt:28)")
                 }
-                val c = holderHolder.current
+                val c = holderHolder.<get-current>(%composer, 0b0110)
                 val cl = calculateSomething(%composer, 0)
                 Layout({ %composer: Composer?, %changed: Int ->
                   sourceInformationMarkerStart(%composer, <>, "C<Text("...>:Test.kt")
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 edd8cf3..80eb3c3 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
@@ -3405,7 +3405,7 @@
                     wontChange = 123
                   }
                   if (%default and 0b0010 !== 0) {
-                    mightChange = LocalColor.current
+                    mightChange = LocalColor.<get-current>(%composer, 0b0110)
                     %dirty = %dirty and 0b01110000.inv()
                   }
                 } else {
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 87e5fde..f2fa489 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
@@ -373,7 +373,14 @@
                 if (isTraceInProgress()) {
                   traceEventStart(<>, %changed, -1, <>)
                 }
-                <<LOCALDELPROP>>
+                val x by {
+                  val x%delegate = mutableStateOf(
+                    value = 123
+                  )
+                  get() {
+                    return x%delegate.getValue(null, ::x%delegate)
+                  }
+                }
                 B(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
                   sourceInformation(%composer, "C:Test.kt")
                   if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
index 59d3c97..2cd3d0b 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
@@ -501,7 +501,7 @@
                 if (isTraceInProgress()) {
                   traceEventStart(<>, %changed, -1, <>)
                 }
-                val bar = compositionLocalBar.current
+                val bar = compositionLocalBar.<get-current>(%composer, 0b0110)
                 val foo = remember(bar, {
                   Foo()
                 }, %composer, 0)
@@ -542,7 +542,7 @@
                 if (isTraceInProgress()) {
                   traceEventStart(<>, %changed, -1, <>)
                 }
-                val foo = remember(compositionLocalBar.current, {
+                val foo = remember(compositionLocalBar.<get-current>(%composer, 0b0110), {
                   Foo()
                 }, %composer, 0)
                 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 b7d9f1b..9731b2f 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
@@ -1264,7 +1264,12 @@
             if (isTraceInProgress()) {
               traceEventStart(<>, %dirty, -1, <>)
             }
-            <<LOCALDELPROP>>
+            val updatedContent by {
+              val updatedContent%delegate = rememberUpdatedState(content, %composer, 0b1110 and %dirty)
+              get() {
+                return updatedContent%delegate.getValue(null, ::updatedContent%delegate)
+              }
+            }
             Defer(composableLambda(%composer, <>, true) { %composer: Composer?, %changed: Int ->
               sourceInformation(%composer, "C:Test.kt")
               if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
index 5dcbf04..9211731 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
@@ -1318,4 +1318,54 @@
             }
         }
     """)
+
+    @Test
+    fun testComposableValueOperator() {
+        check(
+            """
+            import androidx.compose.runtime.Composable
+            import kotlin.reflect.KProperty
+
+            class Foo
+            class FooDelegate {
+                @Composable
+                operator fun getValue(thisObj: Any?, property: KProperty<*>) {}
+                @Composable
+                operator fun <!COMPOSE_INVALID_DELEGATE!>setValue<!>(thisObj: Any?, property: KProperty<*>, value: Any) {}
+            }
+            @Composable operator fun Foo.getValue(thisObj: Any?, property: KProperty<*>) {}
+            @Composable operator fun Foo.<!COMPOSE_INVALID_DELEGATE!>setValue<!>(thisObj: Any?, property: KProperty<*>, value: Any) {}
+
+            fun <!COMPOSABLE_EXPECTED!>nonComposable<!>() {
+                val fooValue = Foo()
+                val foo by fooValue
+                val fooDelegate by FooDelegate()
+                var mutableFoo by <!COMPOSE_INVALID_DELEGATE!>fooValue<!>
+                val bar = Bar()
+
+                println(<!COMPOSABLE_INVOCATION!>foo<!>)
+                println(<!COMPOSABLE_INVOCATION!>fooDelegate<!>)
+                println(bar.<!COMPOSABLE_INVOCATION!>foo<!>)
+
+                <!COMPOSABLE_INVOCATION!>mutableFoo<!> = Unit
+            }
+
+            @Composable
+            fun TestComposable() {
+                val fooValue = Foo()
+                val foo by fooValue
+                val fooDelegate by FooDelegate()
+                val bar = Bar()
+
+                println(foo)
+                println(fooDelegate)
+                println(bar.foo)
+            }
+
+            class Bar {
+                val foo by Foo()
+            }
+            """
+        )
+    }
 }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableCallChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableCallChecker.kt
index 4b18ff3..848bf03 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableCallChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableCallChecker.kt
@@ -28,6 +28,7 @@
 import org.jetbrains.kotlin.descriptors.PropertyDescriptor
 import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor
 import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
+import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
 import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
 import org.jetbrains.kotlin.descriptors.synthetic.FunctionInterfaceConstructorDescriptor
 import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
@@ -48,6 +49,7 @@
 import org.jetbrains.kotlin.psi.KtPsiUtil
 import org.jetbrains.kotlin.psi.KtTryExpression
 import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL
 import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
 import org.jetbrains.kotlin.resolve.calls.util.getValueArgumentForExpression
 import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
@@ -131,11 +133,15 @@
         reportOn: PsiElement,
         context: CallCheckerContext
     ) {
-        if (!resolvedCall.isComposableInvocation()) {
+        val bindingContext = context.trace.bindingContext
+        if (
+            !resolvedCall.isComposableDelegateReference(bindingContext) &&
+                !resolvedCall.isComposableInvocation()
+        ) {
             checkInlineLambdaCall(resolvedCall, reportOn, context)
             return
         }
-        val bindingContext = context.trace.bindingContext
+
         var node: PsiElement? = reportOn
         loop@while (node != null) {
             when (node) {
@@ -224,6 +230,23 @@
                     // KtPropertyAccessor, the ONLY time we make it into this branch is when the
                     // call was done in the initializer of the property/variable.
                     val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, node]
+
+                    if (resolvedCall.isComposableDelegateOperator()) {
+                        // The call is initializer for fields like `val foo by composableDelegate()`.
+                        // Creating the property doesn't have any requirements from Compose side,
+                        // we will recheck on the property call site instead.
+                        if (
+                            descriptor is VariableDescriptorWithAccessors &&
+                                descriptor.isDelegated
+                        ) {
+                            if (descriptor.isVar) {
+                                // setValue delegate is not allowed for now.
+                                illegalComposableDelegate(context, reportOn)
+                            }
+                            return
+                        }
+                    }
+
                     if (
                         descriptor !is LocalVariableDescriptor &&
                         node.annotationEntries.hasComposableAnnotation(bindingContext)
@@ -313,6 +336,13 @@
         context.trace.report(ComposeErrors.COMPOSABLE_FUNCTION_REFERENCE.on(refExpr))
     }
 
+    private fun illegalComposableDelegate(
+        context: CallCheckerContext,
+        reportOn: PsiElement
+    ) {
+        context.trace.report(ComposeErrors.COMPOSE_INVALID_DELEGATE.on(reportOn))
+    }
+
     override fun checkType(
         expression: KtExpression,
         expressionType: KotlinType,
@@ -416,6 +446,23 @@
     }
 }
 
+fun ResolvedCall<*>.isComposableDelegateReference(bindingContext: BindingContext): Boolean {
+    val descriptor = candidateDescriptor
+    if (descriptor is VariableDescriptorWithAccessors) {
+        val delegateInitCall = bindingContext[DELEGATED_PROPERTY_RESOLVED_CALL, descriptor.getter]
+        return delegateInitCall?.candidateDescriptor?.isMarkedAsComposable() == true
+    } else {
+        return false
+    }
+}
+
+fun ResolvedCall<*>.isComposableDelegateOperator(): Boolean {
+    val descriptor = candidateDescriptor
+    return descriptor is FunctionDescriptor &&
+        descriptor.isOperator &&
+        descriptor.name in OperatorNameConventions.DELEGATED_PROPERTY_OPERATORS
+}
+
 fun ResolvedCall<*>.isComposableInvocation(): Boolean {
     if (this is VariableAsFunctionResolvedCall) {
         if (variableCall.candidateDescriptor.type.hasComposableAnnotation())
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableDeclarationChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableDeclarationChecker.kt
index 6f2e72c..c0aad2e 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableDeclarationChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableDeclarationChecker.kt
@@ -41,6 +41,7 @@
 import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
 import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
 import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.util.OperatorNameConventions
 
 class ComposableDeclarationChecker : DeclarationChecker, StorageComponentContainerContributor {
     override fun registerModuleComponents(
@@ -145,6 +146,18 @@
                 COMPOSABLE_FUN_MAIN.on(declaration.nameIdentifier ?: declaration)
             )
         }
+
+        if (
+            hasComposableAnnotation &&
+            descriptor.isOperator &&
+            descriptor.name == OperatorNameConventions.SET_VALUE
+        ) {
+            context.trace.report(
+                ComposeErrors.COMPOSE_INVALID_DELEGATE.on(
+                    declaration.nameIdentifier ?: declaration
+                )
+            )
+        }
     }
 
     private fun checkType(
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeErrorMessages.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeErrorMessages.kt
index 0398542..b000674 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeErrorMessages.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeErrorMessages.kt
@@ -124,5 +124,9 @@
             ComposeErrors.COMPOSE_APPLIER_DECLARATION_MISMATCH,
             "The composition target of an override must match the ancestor target"
         )
+        MAP.put(
+            ComposeErrors.COMPOSE_INVALID_DELEGATE,
+            "Composable setValue operator is not currently supported."
+        )
     }
 }
\ No newline at end of file
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeErrors.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeErrors.kt
index 3b3f96dd..2d7e47a 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeErrors.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeErrors.kt
@@ -154,6 +154,12 @@
             Severity.WARNING
         )
 
+    @JvmField
+    val COMPOSE_INVALID_DELEGATE =
+        DiagnosticFactory0.create<PsiElement>(
+            Severity.ERROR
+        )
+
     init {
         Errors.Initializer.initializeFactoryNamesAndDefaultErrorMessages(
             ComposeErrors::class.java,
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/Stability.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/Stability.kt
index fd3dc96..1fabebc 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/Stability.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/Stability.kt
@@ -32,6 +32,7 @@
 import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
 import org.jetbrains.kotlin.ir.expressions.IrExpression
 import org.jetbrains.kotlin.ir.expressions.IrGetValue
+import org.jetbrains.kotlin.ir.expressions.IrLocalDelegatedPropertyReference
 import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
 import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
 import org.jetbrains.kotlin.ir.types.IrDynamicType
@@ -438,6 +439,7 @@
                 stability
             }
         }
+        is IrLocalDelegatedPropertyReference -> Stability.Stable
         // some default parameters and consts can be wrapped in composite
         is IrComposite -> {
             if (expr.statements.all { it is IrExpression && stabilityOf(it).knownStable() }) {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
index d3e97ae..667a9c9 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
@@ -67,6 +67,7 @@
 import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
 import org.jetbrains.kotlin.ir.expressions.IrGetValue
 import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
+import org.jetbrains.kotlin.ir.expressions.IrReturn
 import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
 import org.jetbrains.kotlin.ir.expressions.IrVararg
 import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
@@ -117,6 +118,7 @@
 import org.jetbrains.kotlin.ir.util.parentAsClass
 import org.jetbrains.kotlin.ir.util.parentClassOrNull
 import org.jetbrains.kotlin.ir.util.primaryConstructor
+import org.jetbrains.kotlin.ir.util.statements
 import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
 import org.jetbrains.kotlin.load.kotlin.computeJvmDescriptor
 import org.jetbrains.kotlin.name.CallableId
@@ -140,12 +142,19 @@
         context.referenceClass(ComposeClassIds.Composer)?.owner
             ?: error("Cannot find the Composer class in the classpath")
 
+    private val _composableIrClass =
+        context.referenceClass(ComposeClassIds.Composable)?.owner
+            ?: error("Cannot find the Composable annotation class in the classpath")
+
     // this ensures that composer always references up-to-date composer class symbol
     // otherwise, after remapping of symbols in DeepCopyTransformer, it results in duplicated
     // references
     protected val composerIrClass: IrClass
         get() = symbolRemapper.getReferencedClass(_composerIrClass.symbol).owner
 
+    protected val composableIrClass: IrClass
+        get() = symbolRemapper.getReferencedClass(_composableIrClass.symbol).owner
+
     fun referenceFunction(symbol: IrFunctionSymbol): IrFunctionSymbol {
         return symbolRemapper.getReferencedFunction(symbol)
     }
@@ -1087,6 +1096,20 @@
         val stringKey = "$name$signature"
         return stringKey.hashCode()
     }
+
+    /*
+     * Delegated accessors are generated with IrReturn(IrCall(<delegated function>)) structure.
+     * To verify the delegated function is composable, this function is unpacking it and
+     * checks annotation on the symbol owner of the call.
+     */
+    fun IrFunction.isComposableDelegatedAccessor(): Boolean =
+        origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR &&
+            body?.let {
+                val returnStatement = it.statements.singleOrNull() as? IrReturn
+                val callStatement = returnStatement?.value as? IrCall
+                val target = callStatement?.symbol?.owner
+                target?.hasComposableAnnotation()
+            } == true
 }
 
 private val unsafeSymbolsRegex = "[ <>]".toRegex()
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 0dd6696..e4f2e8a3 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
@@ -813,6 +813,9 @@
         if (!returnType.isUnit())
             return false
 
+        if (isComposableDelegatedAccessor())
+            return false
+
         // Do not insert an observe scope if the function hasn't been transformed by the
         // ComposerParamTransformer and has a synthetic "composer param" as its last parameter
         if (composerParam() == null) return false
@@ -847,7 +850,9 @@
         val body = declaration.body!!
 
         val hasExplicitGroups = declaration.hasExplicitGroups
-        val elideGroups = hasExplicitGroups || declaration.hasReadOnlyAnnotation
+        val elideGroups = hasExplicitGroups ||
+            declaration.hasReadOnlyAnnotation ||
+            declaration.isComposableDelegatedAccessor()
 
         val skipPreamble = mutableStatementContainer()
         val bodyPreamble = mutableStatementContainer()
@@ -2860,13 +2865,14 @@
             numChanged = changedParamCount(numRealValueParams, ownerFn.thisParamCount)
         }
 
-        require(
-            numContextParams +
+        val expectedNumParams = numContextParams +
             numRealValueParams +
-                1 + // composer param
-                numChanged +
-                numDefaults == numValueParams
-        )
+            1 + // composer param
+            numChanged +
+            numDefaults
+        require(numValueParams == expectedNumParams) {
+            "Expected $expectedNumParams params for ${ownerFn.name}, but got $numValueParams"
+        }
 
         val composerIndex = numContextParams + numRealValueParams
         val changedArgIndex = composerIndex + 1
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
index 7548c42..006279c 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
@@ -32,7 +32,9 @@
 import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
 import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
 import org.jetbrains.kotlin.ir.declarations.IrFunction
+import org.jetbrains.kotlin.ir.declarations.IrLocalDelegatedProperty
 import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
+import org.jetbrains.kotlin.ir.declarations.IrProperty
 import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
 import org.jetbrains.kotlin.ir.declarations.IrValueParameter
 import org.jetbrains.kotlin.ir.declarations.copyAttributes
@@ -40,6 +42,7 @@
 import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
 import org.jetbrains.kotlin.ir.expressions.IrExpression
 import org.jetbrains.kotlin.ir.expressions.IrGetValue
+import org.jetbrains.kotlin.ir.expressions.IrLocalDelegatedPropertyReference
 import org.jetbrains.kotlin.ir.expressions.IrReturn
 import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
 import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom
@@ -48,6 +51,7 @@
 import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
 import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
 import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
+import org.jetbrains.kotlin.ir.expressions.impl.IrLocalDelegatedPropertyReferenceImpl
 import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
 import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
 import org.jetbrains.kotlin.ir.types.IrSimpleType
@@ -58,6 +62,7 @@
 import org.jetbrains.kotlin.ir.types.isPrimitiveType
 import org.jetbrains.kotlin.ir.types.makeNullable
 import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
+import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
 import org.jetbrains.kotlin.ir.util.constructors
 import org.jetbrains.kotlin.ir.util.copyTo
 import org.jetbrains.kotlin.ir.util.copyTypeParametersFrom
@@ -69,6 +74,7 @@
 import org.jetbrains.kotlin.ir.util.isGetter
 import org.jetbrains.kotlin.ir.util.isVararg
 import org.jetbrains.kotlin.ir.util.patchDeclarationParents
+import org.jetbrains.kotlin.ir.util.primaryConstructor
 import org.jetbrains.kotlin.ir.util.remapTypeParameters
 import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
 import org.jetbrains.kotlin.ir.visitors.acceptVoid
@@ -138,8 +144,67 @@
     override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement =
         super.visitSimpleFunction(declaration.withComposerParamIfNeeded())
 
+    override fun visitLocalDelegatedPropertyReference(
+        expression: IrLocalDelegatedPropertyReference
+    ): IrExpression {
+        val transformedGetter = expression.getter.owner.withComposerParamIfNeeded()
+        return super.visitLocalDelegatedPropertyReference(
+            IrLocalDelegatedPropertyReferenceImpl(
+                expression.startOffset,
+                expression.endOffset,
+                expression.type,
+                expression.symbol,
+                expression.delegate,
+                transformedGetter.symbol,
+                expression.setter,
+                expression.origin
+            )
+        )
+    }
+
+    override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty): IrStatement {
+        if (declaration.getter.isComposableDelegatedAccessor()) {
+            declaration.getter.annotations += createComposableAnnotation()
+        }
+
+        if (declaration.setter?.isComposableDelegatedAccessor() == true) {
+            declaration.setter!!.annotations += createComposableAnnotation()
+        }
+
+        return super.visitLocalDelegatedProperty(declaration)
+    }
+
+    override fun visitProperty(declaration: IrProperty): IrStatement {
+        if (declaration.getter?.isComposableDelegatedAccessor() == true) {
+            declaration.getter!!.annotations += createComposableAnnotation()
+        }
+
+        if (declaration.setter?.isComposableDelegatedAccessor() == true) {
+            declaration.setter!!.annotations += createComposableAnnotation()
+        }
+
+        return super.visitProperty(declaration)
+    }
+
+    private fun createComposableAnnotation() =
+        IrConstructorCallImpl(
+            startOffset = SYNTHETIC_OFFSET,
+            endOffset = SYNTHETIC_OFFSET,
+            type = composableIrClass.defaultType,
+            symbol = composableIrClass.primaryConstructor!!.symbol,
+            typeArgumentsCount = 0,
+            constructorTypeArgumentsCount = 0,
+            valueArgumentsCount = 0
+        )
+
     fun IrCall.withComposerParamIfNeeded(composerParam: IrValueParameter): IrCall {
         val ownerFn = when {
+            symbol.owner.isComposableDelegatedAccessor() -> {
+                if (!symbol.owner.hasComposableAnnotation()) {
+                    symbol.owner.annotations += createComposableAnnotation()
+                }
+                symbol.owner.withComposerParamIfNeeded()
+            }
             symbol.owner.hasComposableAnnotation() ->
                 symbol.owner.withComposerParamIfNeeded()
             isComposableLambdaInvoke() ->
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
index 4782eb0..01fc0be 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
@@ -17,6 +17,7 @@
 package androidx.compose.compiler.plugins.kotlin.lower
 
 import androidx.compose.compiler.plugins.kotlin.KtxNameConventions
+import androidx.compose.compiler.plugins.kotlin.hasComposableAnnotation
 import java.util.Locale
 import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
 import org.jetbrains.kotlin.descriptors.Modality
@@ -107,11 +108,13 @@
 import org.jetbrains.kotlin.ir.util.isAnnotationClass
 import org.jetbrains.kotlin.ir.util.isInterface
 import org.jetbrains.kotlin.ir.util.isObject
+import org.jetbrains.kotlin.ir.util.isSetter
 import org.jetbrains.kotlin.ir.util.kotlinFqName
 import org.jetbrains.kotlin.ir.util.parentAsClass
 import org.jetbrains.kotlin.ir.util.primaryConstructor
 import org.jetbrains.kotlin.ir.util.statements
 import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
+import org.jetbrains.kotlin.ir.visitors.acceptVoid
 import org.jetbrains.kotlin.types.Variance
 import org.jetbrains.kotlin.utils.Printer
 
@@ -384,7 +387,12 @@
                     expression.getValueArgument(0)?.print()
                     print(opSymbol)
                 }
-                // invoke
+                "getValue", "setValue" -> {
+                    (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
+                    print(".")
+                    print(opSymbol)
+                    expression.printArgumentList()
+                }
                 "invoke" -> {
                     (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
                     expression.printArgumentList()
@@ -414,7 +422,7 @@
                     print(" $opSymbol ")
                     expression.getValueArgument(1)?.print()
                 }
-                "iterator", "hasNext", "next", "getValue", "setValue",
+                "iterator", "hasNext", "next",
                 "noWhenBranchMatchedException" -> {
                     (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
                     print(".")
@@ -456,7 +464,7 @@
 
         val prop = (function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner
 
-        if (prop != null) {
+        if (prop != null && !function.hasComposableAnnotation()) {
             val propName = prop.name.asString()
             print(propName)
             if (function == prop.setter) {
@@ -1283,13 +1291,37 @@
     }
 
     override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty) {
-        print("<<LOCALDELPROP>>")
+        declaration.printAnnotations(onePerLine = true)
+        print(if (declaration.isVar) "var " else "val ")
+        print(declaration.name)
+        print(" by ")
+        bracedBlock {
+            declaration.delegate.acceptVoid(this)
+            declaration.getter.scoped { it.printPropertyAccessor() }
+            declaration.setter?.scoped { it.printPropertyAccessor(isSetter = true) }
+        }
+    }
+
+    private fun IrFunction.printPropertyAccessor(isSetter: Boolean = this.isSetter) {
+        if (origin != IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) {
+            println()
+            printAnnotations()
+            println()
+            print(if (isSetter) "set" else "get")
+            print("(")
+            valueParameters.printJoin(", ")
+            print(") ")
+            bracedBlock {
+                body?.acceptVoid(this@IrSourcePrinterVisitor)
+            }
+        }
     }
 
     override fun visitLocalDelegatedPropertyReference(
         expression: IrLocalDelegatedPropertyReference
     ) {
-        print("<<LOCALDELPROPREF>>")
+        print("::")
+        print(expression.delegate.owner.name)
     }
 
     override fun visitLoop(loop: IrLoop) {
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 9c514dc..3f627b5 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
@@ -46,6 +46,7 @@
 import androidx.compose.runtime.snapshots.Snapshot
 import kotlin.coroutines.CoroutineContext
 import kotlin.random.Random
+import kotlin.reflect.KProperty
 import kotlin.test.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
@@ -3714,6 +3715,23 @@
         expectChanges()
         revalidate()
     }
+
+    @Test
+    fun composableDelegates() = compositionTest {
+        val local = compositionLocalOf { "Default" }
+        val delegatedLocal by local
+        compose {
+            Text(delegatedLocal)
+
+            CompositionLocalProvider(local provides "Scoped") {
+                Text(delegatedLocal)
+            }
+        }
+        validate {
+            Text("Default")
+            Text("Scoped")
+        }
+    }
 }
 
 class SomeUnstableClass(val a: Any = "abc")
@@ -3965,4 +3983,7 @@
 @Composable
 private inline fun InlineSubcomposition(
     crossinline content: @Composable () -> Unit
-) = TestSubcomposition { content() }
\ No newline at end of file
+) = TestSubcomposition { content() }
+
+@Composable
+operator fun <T> CompositionLocal<T>.getValue(thisRef: Any?, property: KProperty<*>) = current
\ No newline at end of file