Fix super() and this in async arrows inside class constructors - #2127
Open
tmikov wants to merge 1 commit into
Open
Fix super() and this in async arrows inside class constructors#2127tmikov wants to merge 1 commit into
this in async arrows inside class constructors#2127tmikov wants to merge 1 commit into
Conversation
An async arrow becomes three nested IR functions: an outer async
function, an outer generator function, and the inner generator arrow
holding the user code. `genAsyncFunction()` and `genGeneratorFunction()`
pass `capturedState` and `typedClassContext` down to the nested
`FunctionContext`s, but not `legacyClassContext`.
By the time the arrow's parameters and body are emitted,
`hasLegacyClassContext()` is false, so IRGen takes the typed-class path
for `super()` and `this`. This crashed:
class A {}
new class extends A {
constructor() {
(async(a = super(), b) => {})();
}
}
on the "SemanticResolver must check super() is in a class with a
superclass" assertion, because `superClassNode_` is only set for typed
classes. A `super()` in the arrow body instead of a parameter
initializer crashed the same way.
`this` was worse, because it didn't crash. `genThisExpression()` skipped
the derived-constructor TDZ guard and returned the raw `?CHECKED_this`
slot. The IR verifier rejects that. A release build has no verifier and
leaks the empty sentinel into JS instead of throwing ReferenceError.
Pass `legacyClassContext` down in both places. Non-arrow async and
generator methods are unaffected: only `emitFunctionEpilogue()` and
`initCaptureStateInES5FunctionHelper()` consult the class context in
these outer functions, and both also require a constructor
`constructorKind` or `DefinitionKind`. The outer `ES5Function` wrappers
never have one.
Fixes facebook#2126
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
micleo2
reviewed
Aug 10, 2026
| FunctionContext outerFnContext{this, outerFn, functionNode->getSemInfo()}; | ||
| // Propagate the enclosing class context so that the inner function, which | ||
| // holds the actual user code, can use super() and 'this'. | ||
| outerFnContext.legacyClassContext = legacyClassContext; |
Contributor
There was a problem hiding this comment.
I'm not sure if we should always be doing this copy. In practice I think it shouldn't matter since we correctly validate super() calls earlier in SemanticResolver, but I think we should only be doing this copy in if (isAsyncArrow) { branch?
|
@tmikov has imported this pull request. If you are a Meta employee, you can view this in D115505066. |
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.
An async arrow becomes three nested IR functions: an outer async
function, an outer generator function, and the inner generator arrow
holding the user code.
genAsyncFunction()andgenGeneratorFunction()pass
capturedStateandtypedClassContextdown to the nestedFunctionContexts, but notlegacyClassContext.By the time the arrow's parameters and body are emitted,
hasLegacyClassContext()is false, so IRGen takes the typed-class pathfor
super()andthis. This crashed:on the "SemanticResolver must check super() is in a class with a
superclass" assertion, because
superClassNode_is only set for typedclasses. A
super()in the arrow body instead of a parameterinitializer crashed the same way.
thiswas worse, because it didn't crash.genThisExpression()skippedthe derived-constructor TDZ guard and returned the raw
?CHECKED_thisslot. The IR verifier rejects that. A release build has no verifier and
leaks the empty sentinel into JS instead of throwing ReferenceError.
Pass
legacyClassContextdown in both places. Non-arrow async andgenerator methods are unaffected: only
emitFunctionEpilogue()andinitCaptureStateInES5FunctionHelper()consult the class context inthese outer functions, and both also require a constructor
constructorKindorDefinitionKind. The outerES5Functionwrappersnever have one.
Fixes #2126
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com