Fix bridge generation when erased precedes a non-erased parameter#25923
Open
tanishiking wants to merge 1 commit intoscala:mainfrom
Open
Fix bridge generation when erased precedes a non-erased parameter#25923tanishiking wants to merge 1 commit intoscala:mainfrom
erased precedes a non-erased parameter#25923tanishiking wants to merge 1 commit intoscala:mainfrom
Conversation
Fixes scala#25726. The following crashed during erasure with "assertion failed: bad adapt for this.run(): (x$0: Int): String": ```scala class Ev trait Svc[A]: def run(using erased Ev)(a: A): String class ISvc extends Svc[Int]: def run(using erased Ev)(a: Int): String = a.toString ``` Since `A` is specialized to `Int`, the post-erasure signatures of the parent (`(a: Object): String`) and the child (`(a: Int): String`) differ, so a bridge method is generated in `ISvc`. However, while bridge body has application based on post-erasure arguments, Erasure's typer expected bridge method to have pre-erasure form: For example, `Bridges.bridgeRhs` built a single flat `Apply(this.run, [a])` one argument list, that matches the post-erasure method shape. On the other hand, `Erasure.Typer.typedApply` re-types the tree using the *pre-erasure* type `(ev: Ev)(a: A): String` and filters out arguments in erased slots. For the bridge function, it mistook `a` for the erased slot, and drop it. As a result, the `Apply` become something like `this.run()` and `adaptToType` failed on the dangling `` The Apply became `this.run()` and `adaptToType` failed. The same mismatch breaks every shape where `erased` appears before a non-erased parameter: ```scala (using erased Ev)(a: A) (erased x, y) (erased x, y)(z) ``` This commit fixes the problem by: (1) Now, `bridgeRHS` reconstructs the pre-erasure list shape in `.applyMemberArgs`. For each pre-erasure parameter list we emit nested `Apply`s that contains only that list's non-erased arguments. (2) For bridge, we attach `Erasure.PostErasureArgs` to those `Apply`s, that represents all args in the list are non-erased args. `Erasure.Typer.typedApply` skips its erased-argument filter when that attachment is there (because the args are already in post-erasure form). For `(using erased Ev)(a: A)` the bridge produces ```scala Apply(Apply(this.run, []), [a]) // instead of Apply(this.run, [a]) ```
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 #25726.
The following crashed during erasure with
"assertion failed: bad adapt for this.run(): (x$0: Int): String":
Since
Ais specialized toInt, the post-erasure signatures of the parent ((a: Object): String) and the child ((a: Int): String) differ, so a bridge method is generated inISvc.However, while bridge body has application based on post-erasure arguments, Erasure's typer expected bridge method to have pre-erasure form:
For example,
Bridges.bridgeRhsbuilt a single flatApply(this.run, [a])one argument list, that matches the post-erasure method shape.On the other hand,
Erasure.Typer.typedApplyre-types the tree using the pre-erasure type(ev: Ev)(a: A): Stringand filters out arguments in erased slots. For the bridge function, it mistookafor the erased slot, and drop it.As a result, the
Applybecome something likethis.run()andadaptToTypefailed on the dangling ``The Apply became
this.run()and `adaptToType` failed.The same mismatch breaks every shape where
erasedappears before a non-erased parameter:This commit fixes the problem by:
(1) Now,
bridgeRHSreconstructs the pre-erasure list shape in.applyMemberArgs.For each pre-erasure parameter list we emit nested
Applysthat contains only that list's non-erased arguments.
(2) For bridge, we attach
Erasure.PostErasureArgsto thoseApplys,that represents all args in the list are non-erased args.
Erasure.Typer.typedApplyskips its erased-argument filter whenthat attachment is there (because the args are already in
post-erasure form).
For
(using erased Ev)(a: A)the bridge producesHow much have you relied on LLM-based tools in this contribution?
Extensively for problem investigation, minimally for writing code.
How was the solution tested?
testCompilation i25726