From f5c2f35727fdf021cd687dce17ad0d621c374cb3 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Sun, 27 Sep 2026 22:24:47 +0100 Subject: [PATCH] Overload signatures are types only; the implementation is the function A body-less function outside an ambient context is an overload signature, but it was registered as a function. The next declaration of the same name, the implementation, then took the cached signature's parameter list, so its body indexed past the entry block's arguments: an ArrayRef assert in debug and an access violation in release, with or without a call (#343). Function overload signatures are now skipped, and so are method and constructor overload signatures in a class, which crashed the same way. Ambient declarations (`declare`, a .d.ts, imported classes, declaration-only class expressions) and abstract methods keep their meaning. Fixes #343 Co-Authored-By: Claude Opus 5.5 --- tslang/lib/TypeScript/MLIRGenClasses.cpp | 13 ++- tslang/lib/TypeScript/MLIRGenFunctions.cpp | 10 ++ tslang/test/tester/CMakeLists.txt | 3 + .../test/tester/tests/00function_overloads.ts | 99 +++++++++++++++++++ 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 tslang/test/tester/tests/00function_overloads.ts diff --git a/tslang/lib/TypeScript/MLIRGenClasses.cpp b/tslang/lib/TypeScript/MLIRGenClasses.cpp index 3fffecc2b..653eff3d0 100644 --- a/tslang/lib/TypeScript/MLIRGenClasses.cpp +++ b/tslang/lib/TypeScript/MLIRGenClasses.cpp @@ -2099,8 +2099,19 @@ genContext); auto location = loc(classMember); - auto accessLevel = getAccessLevel(classMember); + auto accessLevel = getAccessLevel(classMember); auto funcLikeDeclaration = classMember.as(); + + // an overload signature of a method or a constructor, as for a function: the implementation + // that follows it is the member + auto isAmbientMember = newClassPtr->isDeclaration || newClassPtr->isImport + || (classMember->flags & NodeFlags::Ambient) == NodeFlags::Ambient; + if (!funcLikeDeclaration->body && !classMethodMemberInfo.isAbstract && !isAmbientMember + && (classMember == SyntaxKind::MethodDeclaration || classMember == SyntaxKind::Constructor)) + { + return mlir::success(); + } + if (mlir::failed(getMethodNameOrPropertyName( newClassPtr->isStatic, funcLikeDeclaration, diff --git a/tslang/lib/TypeScript/MLIRGenFunctions.cpp b/tslang/lib/TypeScript/MLIRGenFunctions.cpp index ba806c1fd..86d0773ba 100644 --- a/tslang/lib/TypeScript/MLIRGenFunctions.cpp +++ b/tslang/lib/TypeScript/MLIRGenFunctions.cpp @@ -580,6 +580,16 @@ namespace mlirgen mlir::LogicalResult MLIRGenImpl::mlirGen(FunctionDeclaration functionDeclarationAST, const GenContext &genContext) { + // Outside an ambient context (`declare`, a .d.ts) a function without a body is an overload + // signature, and the implementation that follows it is the function. Registering the + // signature too made the implementation take its parameter list. + auto isAmbient = (functionDeclarationAST->flags & NodeFlags::Ambient) == NodeFlags::Ambient + || hasModifier(functionDeclarationAST, SyntaxKind::DeclareKeyword); + if (!functionDeclarationAST->body && !isAmbient) + { + return mlir::success(); + } + auto funcGenContext = GenContext(genContext); funcGenContext.clearScopeVars(); // declaring function which is nested and object should not have this context (unless it is part of object declaration) diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index a90056cb0..fa3e981ba 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -274,6 +274,7 @@ tslang_add_test(NAME test-compile-00-global-const-object-method COMMAND test-run tslang_add_test(NAME test-compile-00-funcs COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs.ts") tslang_add_test(NAME test-compile-00-funcs-capture COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_capture.ts") tslang_add_test(NAME test-compile-00-funcs-vararg COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_vararg.ts") +tslang_add_test(NAME test-compile-00-function-overloads COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00function_overloads.ts") tslang_add_test(NAME test-compile-01-funcs-vararg COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/01funcs_vararg.ts") # printf is avaliable in JIT only, C has snprintf #tslang_add_test(NAME test-compile-02-funcs-vararg COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/02funcs_vararg.ts") @@ -716,6 +717,7 @@ tslang_add_test(NAME test-jit-00-global-const-object-method COMMAND test-runner tslang_add_test(NAME test-jit-00-funcs COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs.ts") tslang_add_test(NAME test-jit-00-funcs-capture COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_capture.ts") tslang_add_test(NAME test-jit-00-funcs-vararg COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_vararg.ts") +tslang_add_test(NAME test-jit-00-function-overloads COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00function_overloads.ts") tslang_add_test(NAME test-jit-01-funcs-vararg COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/01funcs_vararg.ts") tslang_add_test(NAME test-jit-02-funcs-vararg COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/02funcs_vararg.ts") tslang_add_test(NAME test-jit-00-funcs-bindings COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_bindings.ts") @@ -1634,6 +1636,7 @@ set(TSLANG_CORPUS 00funcs_typed_generic_iterator.ts 00funcs_typeless_function_as_generic.ts 00funcs_vararg.ts + 00function_overloads.ts 00funcs.ts 00generator_manual_next.ts 00generator_manual_next2.ts diff --git a/tslang/test/tester/tests/00function_overloads.ts b/tslang/test/tester/tests/00function_overloads.ts new file mode 100644 index 000000000..2e205a23a --- /dev/null +++ b/tslang/test/tester/tests/00function_overloads.ts @@ -0,0 +1,99 @@ +// Overload signatures: the body-less signatures are types only, and the implementation that +// follows is the one function. Registering a signature as a function crashed the compiler (#343). + +function f(a: number): number; +function f(a: number, b: number): number; +function f(a: number, b?: number) { + return b === undefined ? a : a + b; +} + +function g(s: string): string; +function g(n: number): string; +function g(x: string | number): string { + return typeof x === "string" ? "s" : "n"; +} + +// signatures that are never called still compile +function unused(a: number): number; +function unused(a: number, b: number): number; +function unused(a: number, b?: number) { + return a; +} + +namespace NS { + export function h(a: number): number; + export function h(a: number, b: number): number; + export function h(a: number, b?: number) { + return b === undefined ? a : a * b; + } +} + +function outer() { + function inner(a: number): number; + function inner(a: number, b: number): number; + function inner(a: number, b?: number) { + return b === undefined ? -a : a - b; + } + + return inner(1) + inner(5, 2); +} + +class C { + k: number; + + constructor(); + constructor(k: number); + constructor(k?: number) { + this.k = k === undefined ? 10 : k; + } + + m(a: number): number; + m(a: number, b: number): number; + m(a: number, b?: number) { + return (b === undefined ? a : a + b) * this.k; + } + + static s(a: number): number; + static s(a: number, b: number): number; + static s(a: number, b?: number) { + return b === undefined ? a : a - b; + } +} + +abstract class Base { + abstract get(): number; + + twice() { + return this.get() * 2; + } +} + +class Impl extends Base { + get(): number; + get(scale: number): number; + get(scale?: number) { + return scale === undefined ? 3 : 3 * scale; + } +} + +function main() { + assert(f(1) == 1); + assert(f(1, 2) == 3); + assert(g("a") == "s"); + assert(g(1) == "n"); + assert(NS.h(5) == 5); + assert(NS.h(5, 2) == 10); + assert(outer() == 2); + + const c = new C(); + const c2 = new C(2); + assert(c.m(1) == 10); + assert(c2.m(1, 2) == 6); + assert(C.s(5) == 5); + assert(C.s(5, 2) == 3); + + const b: Base = new Impl(); + assert(b.twice() == 6); + + print("done."); +}