From 0f2d20155696c99e8765ec30a39180dbebf278f3 Mon Sep 17 00:00:00 2001 From: Tzvetan Mikov Date: Sat, 8 Aug 2026 17:29:05 -0700 Subject: [PATCH] Report an error for super() in an arrow in a typed class constructor Typed classes only support `super()` as the first statement of the derived constructor. `genClassDeclaration()` rejects a constructor that doesn't start with one, and `genCallExpr()` builds the call out of the constructor's own `this` parameter, new.target parameter and class type. An arrow nested in the constructor gets its own `FunctionContext`, and `genCapturingFunction()` doesn't set `superClassNode_` or `typedClassContext` on it. A `super()` in that arrow reached `genCallExpr()` with a null `superClassNode_` and hit an assertion: class A { x: number; constructor() { this.x = 1; } } class B extends A { constructor() { super(); (() => { super(); })(); } } Nothing is wrong with that program as far as the compiler is concerned. The required first statement is there, and no diagnostic was emitted before the assertion fired. Release builds drop the assertion and call `genExpression()` on a null node. Making it work would mean routing the constructor's captured `this` and new.target into the arrow. That is a feature, and it cuts against the restriction `genClassDeclaration()` deliberately imposes. Report the restriction instead. The assertion goes away with it, since it no longer guards anything reachable. Co-Authored-By: Claude Opus 5 (1M context) --- lib/IRGen/ESTreeIRGen-expr.cpp | 16 +++++++-- test/IRGen/flow/super-in-arrow-error.js | 45 +++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 test/IRGen/flow/super-in-arrow-error.js diff --git a/lib/IRGen/ESTreeIRGen-expr.cpp b/lib/IRGen/ESTreeIRGen-expr.cpp index 7980cc50ba7..72a7f033765 100644 --- a/lib/IRGen/ESTreeIRGen-expr.cpp +++ b/lib/IRGen/ESTreeIRGen-expr.cpp @@ -551,6 +551,19 @@ Value *ESTreeIRGen::genCallExpr(ESTree::CallExpressionNode *call) { if (curFunction()->hasLegacyClassContext()) { return genLegacyDirectSuper(call); } + if (!curFunction()->superClassNode_) { + // This is a typed class, where super() is only supported as the first + // statement of the derived constructor itself; genClassDeclaration + // rejects a constructor which doesn't start with one. SemanticResolver + // has already verified that we are lexically inside a derived + // constructor, so the only way to get here is a super() nested in an + // arrow function, which has its own FunctionContext and therefore no + // superclass, 'this' or new.target of the constructor. + Mod->getContext().getSourceErrorManager().error( + call->getSourceRange(), + "super() is only supported as the first statement of a derived class constructor"); + return Builder.getLiteralUndefined(); + } if (curFunction()->calledSuperConstructor_) { // Found another super() call than the one that actually initializes the // base class. @@ -562,9 +575,6 @@ Value *ESTreeIRGen::genCallExpr(ESTree::CallExpressionNode *call) { // Check for a super() call. // Call with the passed-in 'this'. thisVal = curFunction()->jsParams[0]; - assert( - curFunction()->superClassNode_ && - "SemanticResolver must check super() is in a class with a superclass"); callee = genExpression(curFunction()->superClassNode_); newTarget = Builder.createGetNewTargetInst( curFunction()->function->getNewTargetParam()); diff --git a/test/IRGen/flow/super-in-arrow-error.js b/test/IRGen/flow/super-in-arrow-error.js new file mode 100644 index 00000000000..ee6d49ea981 --- /dev/null +++ b/test/IRGen/flow/super-in-arrow-error.js @@ -0,0 +1,45 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// RUN: (! %hermesc -typed -dump-ir %s 2>&1 ) | %FileCheck --match-full-lines %s + +// In a typed class, super() is only supported as the first statement of the +// derived constructor. A super() nested in an arrow function must report an +// error instead of crashing IRGen. + +class A { + x: number; + constructor() { + this.x = 1; + } +} + +class B extends A { + constructor() { + super(); + (() => { + super(); + })(); + } +} +// CHECK: {{.*}}super-in-arrow-error.js:25:7: error: super() is only supported as the first statement of a derived class constructor +// CHECK-NEXT: super(); +// CHECK-NEXT: ^~~~~~~ + +class C extends A { + constructor() { + super(); + var f = async () => { + super(); + }; + } +} +// CHECK: {{.*}}super-in-arrow-error.js:37:7: error: super() is only supported as the first statement of a derived class constructor +// CHECK-NEXT: super(); +// CHECK-NEXT: ^~~~~~~ + +// CHECK: Emitted 2 errors. exiting.