Patch declaration parents in ClassSpecializer - #362
Open
chaudhary-lakshay wants to merge 1 commit into
Open
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #284.
Problem
@Burston a class whose constructor parameter defaults toburstValues(...)containing lambdas fails to compile:Cause
BurstValuesArgument.expression()deep-copies each value withparameter.parentas 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
ClassSpecializerthen inserts those copies into the generated subclass constructors and the generated no-args constructor, but never repairs parents afterward.FunctionSpecializerdoes callpatchDeclarationParents()on its generated functions, which is why the sameburstValueswith lambdas works fine on a function parameter and only breaks on a constructor parameter.The result is that every copied lambda's local
IrSimpleFunctionstill claimsTestClass.<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.2and2.11.0,ClassSpecializer.ktis unchanged and theArgument.ktdiff is only an import move plus formatting. The one substantive change iskotlin = "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:
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 whatFunctionSpecializeralready 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 existingBurstValuesWithInlineFunctions.kt. It fails on unpatched trunk with the IR validation error above and passes with the fix. Full:burst-kotlin-plugin-tests:testsuite is green.It uses
loadClassInstance<CoffeeTest>("CoffeeTest")rather thanCoffeeTest()for the default specialization, sincebox()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
BurstLambdassample against a patched build.