diff --git a/tslang/lib/TypeScript/MLIRGenExpressions.cpp b/tslang/lib/TypeScript/MLIRGenExpressions.cpp index 6d19b54fc..bdd9cd7c1 100644 --- a/tslang/lib/TypeScript/MLIRGenExpressions.cpp +++ b/tslang/lib/TypeScript/MLIRGenExpressions.cpp @@ -688,6 +688,7 @@ namespace mlirgen if (!staticCondition.value_or(false)) { SymbolTableScopeT varScope(symbolTable); + SafeTypesMapScopeT safeTypesMapScope(safeTypesMap); if (elseSafeCase.safeType) { addSafeCastStatement(elseSafeCase.expr, elseSafeCase.safeType, false, nullptr, genContext); diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index 9785ef562..98fb27683 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -4593,6 +4593,7 @@ class MLIRGenImpl else { SymbolTableScopeT varScope(symbolTable); + SafeTypesMapScopeT safeTypesMapScope(safeTypesMap); if (elseSafeCase.safeType) { addSafeCastStatement(elseSafeCase.expr, elseSafeCase.safeType, false, nullptr, genContext); diff --git a/tslang/lib/TypeScript/MLIRGenStatements.cpp b/tslang/lib/TypeScript/MLIRGenStatements.cpp index f483c8add..d28fba91a 100644 --- a/tslang/lib/TypeScript/MLIRGenStatements.cpp +++ b/tslang/lib/TypeScript/MLIRGenStatements.cpp @@ -537,6 +537,8 @@ namespace mlirgen { builder.setInsertionPointToStart(&ifOp.getElseRegion().front()); SymbolTableScopeT varScope(symbolTable); + // the else narrowing maps a field to a value of this region; it must not outlive it + SafeTypesMapScopeT safeTypesMapScope(safeTypesMap); auto processIf = !literalValue.has_value() || !literalValue.value(); if (processIf) diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index b0c00aba6..630d58d79 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -530,6 +530,7 @@ tslang_add_test(NAME test-compile-00-safe-cast-while COMMAND test-runner "${PROJ tslang_add_test(NAME test-compile-01-safe-cast-while COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/01safe_cast_while.ts") tslang_add_test(NAME test-compile-00-safe-cast-field-access COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_field_access.ts") tslang_add_test(NAME test-compile-00-safe-cast-null-field COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_null_field.ts") +tslang_add_test(NAME test-compile-00-safe-cast-else-scope COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_else_scope.ts") tslang_add_test(NAME test-compile-00-safe-cast-bug COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_bug.ts") tslang_add_test(NAME test-compile-00-optional COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00optional.ts") tslang_add_test(NAME test-compile-01-optional COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/01optional.ts") @@ -973,6 +974,7 @@ tslang_add_test(NAME test-jit-00-safe-cast-while COMMAND test-runner -jit "${PRO tslang_add_test(NAME test-jit-01-safe-cast-while COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/01safe_cast_while.ts") tslang_add_test(NAME test-jit-00-safe-cast-field-access COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_field_access.ts") tslang_add_test(NAME test-jit-00-safe-cast-null-field COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_null_field.ts") +tslang_add_test(NAME test-jit-00-safe-cast-else-scope COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_else_scope.ts") tslang_add_test(NAME test-jit-00-safe-cast-bug COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00safe_cast_bug.ts") tslang_add_test(NAME test-jit-00-optional COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00optional.ts") tslang_add_test(NAME test-jit-01-optional COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/01optional.ts") @@ -1758,6 +1760,7 @@ set(TSLANG_CORPUS 00safe_cast_bug.ts 00safe_cast_field_access.ts 00safe_cast_null_field.ts + 00safe_cast_else_scope.ts 00safe_cast_typeof.ts 00safe_cast_while.ts 00safe_cast.ts diff --git a/tslang/test/tester/tests/00safe_cast_else_scope.ts b/tslang/test/tester/tests/00safe_cast_else_scope.ts new file mode 100644 index 000000000..7362a3b45 --- /dev/null +++ b/tslang/test/tester/tests/00safe_cast_else_scope.ts @@ -0,0 +1,54 @@ +// Narrowing a field in an else branch (or a conditional's false side) must end with that branch. +// It leaked into the rest of the function and into the next method compiled, which then read a +// value of a discarded region: a crash, or a bogus "saving to constant object" (#231). + +class N { + v: number = 1; +} + +class L { + head: N | null = null; + + ifElse(): number { + if (this.head === null) { + return 0; + } else { + return this.head.v; + } + } + + ternary(): number { + return this.head === null ? 0 : this.head.v; + } + + afterIfElse(): boolean { + let r = 0; + if (this.head === null) { + r = 1; + } else { + r = 2; + } + + return this.head === null; + } + + isEmpty(): boolean { + return this.head === null; + } +} + +function main() { + const l = new L(); + assert(l.ifElse() == 0); + assert(l.ternary() == 0); + assert(l.afterIfElse()); + assert(l.isEmpty()); + + l.head = new N(); + assert(l.ifElse() == 1); + assert(l.ternary() == 1); + assert(!l.afterIfElse()); + assert(!l.isEmpty()); + + print("done."); +}