From d09b19b60c0df2c908516a1c002f38c3c9279734 Mon Sep 17 00:00:00 2001 From: Changqing Jing Date: Thu, 26 Mar 2026 16:29:18 +0800 Subject: [PATCH 1/4] fix: compiler crashed when duplicate inner function declared in function body --- src/compiler.ts | 15 +++++++++++++++ tests/compiler/duplicate-function-in-scope.json | 7 +++++++ tests/compiler/duplicate-function-in-scope.ts | 16 ++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 tests/compiler/duplicate-function-in-scope.json create mode 100644 tests/compiler/duplicate-function-in-scope.ts diff --git a/src/compiler.ts b/src/compiler.ts index 36f09ab8aa..af751d336c 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -7186,6 +7186,21 @@ export class Compiler extends DiagnosticEmitter { let sourceFunction = flow.sourceFunction; let isNamed = declaration.name.text.length > 0; let isSemanticallyAnonymous = !isNamed || contextualType != Type.void; + + // Check for duplicate named function declarations before creating the + // prototype, since registering a concrete element with a duplicate + // internalName would trigger an assertion error. + if (!isSemanticallyAnonymous) { + let existingLocal = flow.getScopedLocal(declaration.name.text); + if (existingLocal) { + this.error( + DiagnosticCode.Duplicate_identifier_0, + declaration.name.range, declaration.name.text + ); + return this.module.unreachable(); + } + } + let prototype = new FunctionPrototype( isSemanticallyAnonymous ? `${isNamed ? declaration.name.text : "anonymous"}|${sourceFunction.nextAnonymousId++}` diff --git a/tests/compiler/duplicate-function-in-scope.json b/tests/compiler/duplicate-function-in-scope.json new file mode 100644 index 0000000000..0cc93dcb87 --- /dev/null +++ b/tests/compiler/duplicate-function-in-scope.json @@ -0,0 +1,7 @@ +{ + "asc_flags": [], + "stderr": [ + "TS2300: Duplicate identifier 'inner'.", + "EOF" + ] +} diff --git a/tests/compiler/duplicate-function-in-scope.ts b/tests/compiler/duplicate-function-in-scope.ts new file mode 100644 index 0000000000..62c2e68a00 --- /dev/null +++ b/tests/compiler/duplicate-function-in-scope.ts @@ -0,0 +1,16 @@ +// Duplicate named function declarations in the same scope should +// produce a diagnostic instead of crashing the compiler. + +export function test(): void { + function inner(): i32 { + let x: i32 = 0; + return x; + } + function inner(): i32 { + let x: i32 = 0; + return x + 1; + } + inner(); +} + +ERROR("EOF"); From 9a504f329087030f5eeb9096baeb36a7ea7f6642 Mon Sep 17 00:00:00 2001 From: Changqing Jing Date: Thu, 26 Mar 2026 16:36:44 +0800 Subject: [PATCH 2/4] Fix --- tests/compiler/duplicate-function-in-scope.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/compiler/duplicate-function-in-scope.json b/tests/compiler/duplicate-function-in-scope.json index 0cc93dcb87..05cb143081 100644 --- a/tests/compiler/duplicate-function-in-scope.json +++ b/tests/compiler/duplicate-function-in-scope.json @@ -1,7 +1,7 @@ { "asc_flags": [], "stderr": [ - "TS2300: Duplicate identifier 'inner'.", - "EOF" + "EOF", + "TS2300: Duplicate identifier 'inner'." ] } From 70e42c36a9a865134a8cc82d2f442c4e133d95b6 Mon Sep 17 00:00:00 2001 From: Changqing Jing Date: Fri, 27 Mar 2026 09:50:34 +0800 Subject: [PATCH 3/4] Fix review --- src/compiler.ts | 4 ++-- tests/compiler/duplicate-function-in-scope.json | 2 +- tests/compiler/duplicate-function-in-scope.ts | 10 ++-------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index af751d336c..cbd5c25a0e 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -7194,8 +7194,8 @@ export class Compiler extends DiagnosticEmitter { let existingLocal = flow.getScopedLocal(declaration.name.text); if (existingLocal) { this.error( - DiagnosticCode.Duplicate_identifier_0, - declaration.name.range, declaration.name.text + DiagnosticCode.Duplicate_function_implementation, + declaration.name.range ); return this.module.unreachable(); } diff --git a/tests/compiler/duplicate-function-in-scope.json b/tests/compiler/duplicate-function-in-scope.json index 05cb143081..0aeaa4648f 100644 --- a/tests/compiler/duplicate-function-in-scope.json +++ b/tests/compiler/duplicate-function-in-scope.json @@ -2,6 +2,6 @@ "asc_flags": [], "stderr": [ "EOF", - "TS2300: Duplicate identifier 'inner'." + "TS2393: Duplicate function implementation." ] } diff --git a/tests/compiler/duplicate-function-in-scope.ts b/tests/compiler/duplicate-function-in-scope.ts index 62c2e68a00..81dc8d044c 100644 --- a/tests/compiler/duplicate-function-in-scope.ts +++ b/tests/compiler/duplicate-function-in-scope.ts @@ -2,14 +2,8 @@ // produce a diagnostic instead of crashing the compiler. export function test(): void { - function inner(): i32 { - let x: i32 = 0; - return x; - } - function inner(): i32 { - let x: i32 = 0; - return x + 1; - } + function inner(): void {} + function inner(): void {} inner(); } From 59a1ef8c4275d4307381e82e108236b82d4458ad Mon Sep 17 00:00:00 2001 From: Changqing Jing Date: Fri, 27 Mar 2026 12:15:44 +0800 Subject: [PATCH 4/4] Fix review --- src/compiler.ts | 16 +--------------- src/program.ts | 10 +++++++++- tests/compiler/duplicate-function-in-scope.json | 1 + tests/compiler/duplicate-function-in-scope.ts | 6 ++++++ 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index cbd5c25a0e..834f354f70 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1575,6 +1575,7 @@ export class Compiler extends DiagnosticEmitter { forceStdAlternative: bool = false ): bool { if (instance.is(CommonFlags.Compiled)) return !instance.is(CommonFlags.Errored); + if (instance.is(CommonFlags.Errored)) return false; if (!forceStdAlternative) { if (instance.hasDecorator(DecoratorFlags.Builtin)) return true; @@ -7186,21 +7187,6 @@ export class Compiler extends DiagnosticEmitter { let sourceFunction = flow.sourceFunction; let isNamed = declaration.name.text.length > 0; let isSemanticallyAnonymous = !isNamed || contextualType != Type.void; - - // Check for duplicate named function declarations before creating the - // prototype, since registering a concrete element with a duplicate - // internalName would trigger an assertion error. - if (!isSemanticallyAnonymous) { - let existingLocal = flow.getScopedLocal(declaration.name.text); - if (existingLocal) { - this.error( - DiagnosticCode.Duplicate_function_implementation, - declaration.name.range - ); - return this.module.unreachable(); - } - } - let prototype = new FunctionPrototype( isSemanticallyAnonymous ? `${isNamed ? declaration.name.text : "anonymous"}|${sourceFunction.nextAnonymousId++}` diff --git a/src/program.ts b/src/program.ts index 8bcf1bfa19..e54826b19d 100644 --- a/src/program.ts +++ b/src/program.ts @@ -3850,7 +3850,15 @@ export class Function extends TypedElement { flow.setLocalFlag(local.index, LocalFlags.Initialized); } } - registerConcreteElement(program, this); + if (program.instancesByName.has(this.internalName)) { + program.error( + DiagnosticCode.Duplicate_function_implementation, + prototype.declaration.name.range + ); + this.set(CommonFlags.Errored); + } else { + registerConcreteElement(program, this); + } } /** Gets the types of additional locals that are not parameters. */ diff --git a/tests/compiler/duplicate-function-in-scope.json b/tests/compiler/duplicate-function-in-scope.json index 0aeaa4648f..be8607a6b1 100644 --- a/tests/compiler/duplicate-function-in-scope.json +++ b/tests/compiler/duplicate-function-in-scope.json @@ -2,6 +2,7 @@ "asc_flags": [], "stderr": [ "EOF", + "TS2300: Duplicate identifier 'inner'.", "TS2393: Duplicate function implementation." ] } diff --git a/tests/compiler/duplicate-function-in-scope.ts b/tests/compiler/duplicate-function-in-scope.ts index 81dc8d044c..7d91e2abfc 100644 --- a/tests/compiler/duplicate-function-in-scope.ts +++ b/tests/compiler/duplicate-function-in-scope.ts @@ -1,6 +1,12 @@ // Duplicate named function declarations in the same scope should // produce a diagnostic instead of crashing the compiler. +export function testMixed1(): void { + const inner = function (): void {}; + function inner(): void {} + inner(); +} + export function test(): void { function inner(): void {} function inner(): void {}