Fix wrong code generation for this-> in lambda type parameters#4955
Draft
SeanTAllen wants to merge 1 commit intomainfrom
Draft
Fix wrong code generation for this-> in lambda type parameters#4955SeanTAllen wants to merge 1 commit intomainfrom
this-> in lambda type parameters#4955SeanTAllen wants to merge 1 commit intomainfrom
Conversation
Lambda types like `{(this->A!): Bool}` are desugared into anonymous
interfaces during the sugar pass. The desugaring preserved `this->`
verbatim, but inside the generated interface, `this` refers to the
interface's own receiver rather than the enclosing class's receiver.
This semantic mismatch caused incorrect structural subtype checks,
leading to wrong vtable dispatch, incorrect results, or segfaults
when the lambda was forwarded to another function.
The fix resolves `this->` to the enclosing method's receiver cap
during lambda type desugaring. Since the compiler already enforces
that `this->` can only appear in `box` methods, `{(this->A!)}` now
correctly desugars to `{(box->A!)}`.
Closes #4168
Member
Author
|
i made a logic error here when working with claude. this is invalid. |
jemc
reviewed
Mar 11, 2026
|
|
||
| The root cause: lambda types are desugared into anonymous interfaces, and `this->` was copied verbatim into the interface. Once inside the interface, `this` referred to the interface's own receiver instead of the enclosing class's receiver, producing the wrong type for structural subtype checks. | ||
|
|
||
| `this->` is now resolved to the enclosing method's receiver capability during desugaring, so `{(this->A!)}` in a `box` method correctly becomes `{(box->A!)}`. |
Member
There was a problem hiding this comment.
Wrong.
Discussed in sync.
Consider a type Foo, which has a fun box, in which a lambda is born with this->A! as the parameter type
We need the lambda to get sugared to a generic type, where the type parameter has a constraint of Foo #read. Then The generic anonymous lambda type gets instantiated at the call site with a type parameter of this->Foo'ref, which is effectively the way to "pass in" the correct constrained generic cap of the this (of the fun box).
As I said in the sync call, fun box is actually in reality fun #read but we call it fun box for historical/aesthetic reasons.
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.
When a lambda type contains
this->(e.g.,{(this->A!): Bool}), the sugar pass desugars it into an anonymous interface. The desugaring preservedthis->verbatim, but inside the generated interface,thisrefers to the interface's own receiver rather than the enclosing class's receiver. This caused incorrect structural subtype checks, leading to wrong vtable dispatch, incorrect results, or segfaults when the lambda was forwarded to another function rather than called directly.The fix resolves
this->to the enclosing method's receiver cap during lambda type desugaring, so{(this->A!)}in aboxmethod correctly becomes{(box->A!)}.Closes #4168