Status Update
Comments
[Deleted User] <[Deleted User]> #2
Does the program work without the compiler switch. That is, is `"-P", "plugin:androidx.compose.compiler.plugins.kotlin:experimentalStrongSkipping=true",` required to reproduce or is just changing the runtime enough?
as...@google.com <as...@google.com> #3
Sorry for that, I will try to extract an example when I'm free.
[Deleted User] <[Deleted User]> #4
[Deleted User] <[Deleted User]> #5
[Deleted User] <[Deleted User]> #6
And interestingly, if we insert delay(1000) between two dialog suspend usage
dialogState.bgWork {
delay(5000)
}
delay(1000)
dialogState.showSelectActions {
onSelect("a") {
}
onSelect("b") {
}
}
It will not crash
as...@google.com <as...@google.com> #7
As for the order of `onForgotten`, it may be you are seeing and issue that I fixed in a subsequent version as this, for the snapshot builds, was broken for a few weeks when using the non-skipping group optimization even indirectly.
[Deleted User] <[Deleted User]> #8
[Deleted User] <[Deleted User]> #9
I tried cutting down the example myself and was not able to reproduce this.
The primary dependency chagnes I made were:
compose-material3 = { module = "androidx.compose.material3:material3", version = "1.3.0-SNAPSHOT" }
compose-foundation = { module = "androidx.compose.foundation:foundation-android", version = "1.7.0-SNAPSHOT" }
I only included the part of the Dialog.kt that was needed by the above,
class DialogState {
var content: (@Composable () -> Unit)? by mutableStateOf(null)
@Composable
fun Intercept() = content?.invoke()
fun dismiss() {
content = null
}
suspend inline fun <R> dialog(crossinline block: @Composable (CancellableContinuation<R>) -> Unit) = suspendCancellableCoroutine<R> { cont ->
cont.invokeOnCancellation { dismiss() }
val realContinuation = object : CancellableContinuation<R> by cont {
override fun resumeWith(result: Result<R>) {
dismiss()
cont.resumeWith(result)
}
}
content = { block(realContinuation) }
}
suspend inline fun showSelectActions(
title: String? = null,
builder: ActionScope.() -> Unit,
) {
val (items, actions) = buildList { builder(ActionScope { action, that -> add(action to that) }) }.unzip()
val selected = showSelectItem(items, title)
actions[selected].invoke()
}
suspend fun showSelectItem(
items: List<String>,
title: String?,
respectDefaultWidth: Boolean = true,
): Int = showNoButton(respectDefaultWidth) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
if (title != null) {
Text(
text = title,
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
style = MaterialTheme.typography.headlineSmall,
)
}
LazyColumn {
itemsIndexed(items) { index, text ->
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.tertiary) {
Text(
text = text,
modifier = Modifier.clickable { dismissWith(index) }.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 16.dp),
style = MaterialTheme.typography.titleMedium,
)
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
suspend fun <R> showNoButton(respectDefaultWidth: Boolean = true, block: @Composable DismissDialogScope<R>.() -> Unit): R {
return dialog { cont ->
val impl = remember(cont) {
DismissDialogScope<R> {
cont.resume(it)
}
}
BasicAlertDialog(
onDismissRequest = { cont.cancel() },
properties = DialogProperties(usePlatformDefaultWidth = respectDefaultWidth),
content = {
Surface(
modifier = with(Modifier) { if (!respectDefaultWidth) defaultMinSize(280.dp) else width(280.dp) },
shape = AlertDialogDefaults.shape,
color = AlertDialogDefaults.containerColor,
tonalElevation = AlertDialogDefaults.TonalElevation,
content = { block(impl) },
)
},
)
}
}
}
fun interface ActionScope {
fun onSelect(action: String, that: suspend () -> Unit)
}
fun interface DismissDialogScope<R> {
fun dismissWith(value: R)
}
This shows the "Please wait" and then selection between "a" and "b" as I expected.
as...@google.com <as...@google.com> #10
This requires compose compiler 1.5.11-dev-k2.0.0-Beta4-21f5e479a96
I will upload a sample project soon
as...@google.com <as...@google.com> #11
as...@google.com <as...@google.com> #12
[Deleted User] <[Deleted User]> #13
Sorry! I didn't see you updated this. I must have missed the notification. Trying now.
[Deleted User] <[Deleted User]> #14
I have the exception reproducing locally, investigating.
as...@google.com <as...@google.com> #15
The issue is in the call to dialog
when it inlines the crossinline
lambda. This lambda instances are getting the same key at both call sights and, when the second dialog shows, it believes it is recomposing the first and then throws as it didn't create the state in this slot table.
The simple work-around now is to change dialog
's parameter to noinline
instead of crossinline
. This ensures that the keys produced for the lambdas are different.
I am investigating what change caused this.
as...@google.com <as...@google.com> #16
The code generation for crossinline
lambda seems to have always been wrong but the extra groups the compiler create that the change removes prevented crashes in most cases. By removing otherwise unnecessary groups, the missing group around a crossinline
lambda now causes a crash.
The work-around above, changing the lambda to noinline
, is valid because it avoids crossinline
and, therefore, avoids the crash.
The key symptom is that currentComposer.inserting
is never true
for the crossinline
lambda passed into the second dialog which indicates it is recomposing over state it didn't produce.
A group is required because a crossinline
lambda can be inlined into a lambda instance. The instance created gets the same key but its content has effectively changed as the crossinline
lambda acts as a branch point. The runtime assumes that when a different branch of code is executed, it executes in a block with a different key than the previous branch taken. As there is no branch group, the code is recomposed over state it didn't create, which is never valid. Recomposing over state it didn't create will not always create an exception and this was the case for the code generated prior to enabling the optimization to remove otherwise unnecessary groups.
as...@google.com <as...@google.com> #17
Branch: androidx-main
commit 7862dc2cf51ebc69f6760b0c6cf8a2133f11cd94
Author: Chuck Jazdzewski <chuckj@google.com>
Date: Mon Feb 26 15:02:15 2024
Fix code generation for composable crossinline lambda
If a crossinline lambda was called in a lambda instance then
it was possible for the composition to get out of sync potentially
causing crashes. This is hard to reproduce without
"nonSkippingGroupOptimization" enabled as the group around
remember causes most exceptions avoided, however the code was
incorrect as and subtle crashes were still possible.
Fixes: 325502738
Test: ./gradlew :compose:c:c-h:i-t:tDUT :compose:c:c-h:r-t:jvmTest
Change-Id: Icb2fd2f5a946144cc105737c4a4051f02a901b78
M compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromCrossInlinedLambda[useFir = false].txt
M compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.ControlFlowTransformTests/testEarlyReturnFromCrossInlinedLambda[useFir = true].txt
M compose/compiler/compiler-hosted/runtime-tests/build.gradle
M compose/compiler/compiler-hosted/runtime-tests/src/commonTest/kotlin/androidx/compose/compiler/test/CompositionTests.kt
M compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
M compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrInlineReferenceLocator.kt
[Deleted User] <[Deleted User]> #18
[Deleted User] <[Deleted User]> #19
Yes it is an IntDef, will try this workaround and come back
[Deleted User] <[Deleted User]> #20
[Deleted User] <[Deleted User]> #22
Removing the annotation working thanks,
ap...@google.com <ap...@google.com> #23
Branch: androidx-main
commit 2495d2c92c9ed8f63bf98c9886f122b38453036c
Author: Andrei Shikov <ashikov@google.com>
Date: Tue Nov 07 08:38:02 2023
Fix crash during overridden composable types lookup
Some annotations defined in current module can be in partially incomplete state (either completely unbound or parent is missing). When we lookup overriden functions to potentially update them, it results in a crash.
Practically, we only need to update functions in a different module, as deep copy already changes functions in the current one, so this change does exactly that.
Relnote: "Fixes a crash during lookup of overridden composable types"
Test: Compiler transform tests
Fixes: 297665426
Change-Id: Ib6d2c0ec3e18716f345a61b4df465cb64d268fec
M compose/compiler/compiler-hosted/integration-tests/src/jvmTest/kotlin/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
A compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTestsNoSource/testComposable[useFir = false].txt
A compose/compiler/compiler-hosted/integration-tests/src/test/resources/golden/androidx.compose.compiler.plugins.kotlin.FunctionBodySkippingTransformTestsNoSource/testComposable[useFir = true].txt
M compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt
M compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
pr...@google.com <pr...@google.com> #24
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.compose.compiler:compiler-hosted:1.5.5
Description
Jetpack Compose version: 1.5.0 Jetpack Compose component used: null Android Studio Build: AI-223.8836.35.2231.10406996 Kotlin version: 1.9.0
After upgrading the compose compiler to 1.5.2 with bom version 2023.08.00 project not compiling. With 1.5.1 it working fine. The mentioned file doesn't have any
lateinit
property. Haveby
delegatedkoin
injection onlyStack trace (if applicable):