Steps to reproduce
- Checkout repo
- Run
./gradlew test jacocoTestReport
- Open
build/reports/jacoco/test/html/org.example/index.source.html

4. Change kotlin version to 2.3.21
5. Run `./gradlew test jacocoTestReport` and refresh browser
Expected behaviour
Give same amount of branch coverage
Actual behaviour
Increases number of branches, but coverage decreases
Extra info
Kotlin upgrade in our product cause significant coverage drop, which is enough to prevent upgrade to kotlin. Sometimes it was +120 branches per composable. I tried to make small as possible repro.
I wasn't sure at first whether this is the right place to report it, but similar problems were fixed here in #1616 and #1911. Let me know if it belongs elsewhere.
I have no idea if it even makes sense, but AI explained it like this:
AI Analysis
The difference is in the lambda memoization check inside the CC(remember) region (between sourceInformationMarkerStart/End). javap -c of ExampleKt.example, the check for the captured state parameter:
Kotlin 2.3.21 — an unconditional changedInstance() call, no extra branches, which roughly corresponds to:
val invalidated = $composer.changedInstance(state) or
($changed and 0b1110000 == 0b0100000) // onDone
Kotlin 2.4.10 — the same capture now consults the caller-provided $changed bits before falling back to changedInstance(), adding three conditionals, which roughly corresponds to:
val invalidated = ($changed and 0b0001110 == 0b0000100 ||
($changed and 0b0001000 != 0 && $composer.changedInstance(state))) or
($changed and 0b1110000 == 0b0100000) // onDone
The result is used as if (invalidated || rememberedValue === Empty) { create and cache the lambda } else { reuse cached }. Some of these branch outcomes depend on bits computed by the caller, so they cannot be reliably exercised by tests. It would be great if KotlinComposeFilter also filtered this pattern.
Steps to reproduce
./gradlew test jacocoTestReportbuild/reports/jacoco/test/html/org.example/index.source.htmlExpected behaviour
Give same amount of branch coverage
Actual behaviour
Increases number of branches, but coverage decreases
Extra info
Kotlin upgrade in our product cause significant coverage drop, which is enough to prevent upgrade to kotlin. Sometimes it was +120 branches per composable. I tried to make small as possible repro.
I wasn't sure at first whether this is the right place to report it, but similar problems were fixed here in #1616 and #1911. Let me know if it belongs elsewhere.
I have no idea if it even makes sense, but AI explained it like this:
AI Analysis
The difference is in the lambda memoization check inside the
CC(remember)region (betweensourceInformationMarkerStart/End).javap -cofExampleKt.example, the check for the capturedstateparameter:Kotlin 2.3.21 — an unconditional
changedInstance()call, no extra branches, which roughly corresponds to:Kotlin 2.4.10 — the same capture now consults the caller-provided
$changedbits before falling back tochangedInstance(), adding three conditionals, which roughly corresponds to:The result is used as
if (invalidated || rememberedValue === Empty) { create and cache the lambda } else { reuse cached }. Some of these branch outcomes depend on bits computed by the caller, so they cannot be reliably exercised by tests. It would be great ifKotlinComposeFilteralso filtered this pattern.