Skip to content

Patch declaration parents in ClassSpecializer - #362

Open
chaudhary-lakshay wants to merge 1 commit into
cashapp:mainfrom
chaudhary-lakshay:fix-284-constructor-lambda-parents
Open

Patch declaration parents in ClassSpecializer#362
chaudhary-lakshay wants to merge 1 commit into
cashapp:mainfrom
chaudhary-lakshay:fix-284-constructor-lambda-parents

Conversation

@chaudhary-lakshay

Copy link
Copy Markdown

Fixes #284.

Problem

@Burst on a class whose constructor parameter defaults to burstValues(...) containing lambdas fails to compile:

Platform declaration clash: The following declarations have the same JVM signature
(access$ExampleUnitTest$_init_$lambda$0()Lkotlin/Unit;)

Cause

BurstValuesArgument.expression() deep-copies each value with parameter.parent as the new parent, which for a constructor parameter is the original constructor:

https://github.com/cashapp/burst/blob/trunk/burst-kotlin-plugin/src/main/kotlin/app/cash/burst/kotlin/Argument.kt#L112

ClassSpecializer then inserts those copies into the generated subclass constructors and the generated no-args constructor, but never repairs parents afterward. FunctionSpecializer does call patchDeclarationParents() on its generated functions, which is why the same burstValues with lambdas works fine on a function parameter and only breaks on a constructor parameter.

The result is that every copied lambda's local IrSimpleFunction still claims TestClass.<init> as its parent, so lowering mints the same synthetic accessor name once per specialization.

Why it appeared in 2.11.0

The bug is latent, not new. Between 2.10.2 and 2.11.0, ClassSpecializer.kt is unchanged and the Argument.kt diff is only an import move plus formatting. The one substantive change is kotlin = "2.2.20""2.3.0"; Kotlin 2.3's lambda naming stopped tolerating the wrong parent.

On current trunk (Kotlin 2.4.10) the IR validator catches it one phase earlier, so the same test data fails with:

error: the compiler plugin 'app.cash.burst.kotlin.BurstIrGenerationExtension' generated invalid IR.
Declaration with wrong parent:

emitted three times — matching two generated subclasses plus the no-args constructor for a three-value burstValues.

Fix

Call patchDeclarationParents() on both generated constructors, mirroring what FunctionSpecializer already does. Per-copy name disambiguation would be the wrong layer: the IR is structurally invalid, and repairing parents lets Kotlin's existing local-declaration naming do the right thing.

Test

New box test BurstValuesWithInlineFunctionsInConstructor.kt, the constructor-parameter counterpart to the existing BurstValuesWithInlineFunctions.kt. It fails on unpatched trunk with the IR validation error above and passes with the fix. Full :burst-kotlin-plugin-tests:test suite is green.

It uses loadClassInstance<CoffeeTest>("CoffeeTest") rather than CoffeeTest() for the default specialization, since box() is compiled in the same unit and the frontend binds the call to the default argument that the plugin later strips.

Not verified

I reproduced this on trunk's Kotlin 2.4.10, not on the reporter's exact 2.3.0 + AGP 9 setup, and I did not run the attached BurstLambdas sample against a patched build.

Argument.expression() deep-copies burstValues arguments with the original
constructor as their parent. ClassSpecializer inserted those copies into
generated constructors without repairing parents, so lambdas inside them
still claimed the original constructor.

On Kotlin 2.3+ that produces duplicate synthetic lambda accessors and a
"Platform declaration clash" for constructor parameters; on 2.4 the IR
validator rejects it as "Declaration with wrong parent". FunctionSpecializer
already patched parents, which is why only constructor parameters broke.

Closes cashapp#284
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

burstValues with lambdas cause "Platform declaration clash: The following declarations have the same JVM signature"

1 participant