diff --git a/tslang/lib/TypeScript/MLIRGenCast.cpp b/tslang/lib/TypeScript/MLIRGenCast.cpp index ae26d491d..ccc1a9022 100644 --- a/tslang/lib/TypeScript/MLIRGenCast.cpp +++ b/tslang/lib/TypeScript/MLIRGenCast.cpp @@ -941,7 +941,11 @@ namespace mlirgen return std::nullopt; } - if (constArrayType.getElementType() == arrayType.getElementType()) + // the same element type needs no conversion - unless the elements are arrays: the cast below + // copies only the outer data to the heap, and the inner arrays kept pointing at the literal's + // constant data, so `nested[1].push(4)` reallocated a global and `nested[1][0] = 9` wrote one + if (constArrayType.getElementType() == arrayType.getElementType() + && !isa(arrayType.getElementType())) { return std::nullopt; } diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index d429687fa..1d7ca0834 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -299,6 +299,7 @@ tslang_add_test(NAME test-compile-00-const-array-to-array-elements COMMAND test- tslang_add_test(NAME test-compile-00-array-widen-elements-now COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_widen_elements_now.ts") tslang_add_test(NAME test-compile-00-const-array-nested-elements COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_array_nested_elements.ts") tslang_add_test(NAME test-compile-00-const-array-mutation COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_array_mutation.ts") +tslang_add_test(NAME test-compile-00-nested-array-literal-mutation COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00nested_array_literal_mutation.ts") tslang_add_test(NAME test-compile-00-array-move-wide-elements COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_move_wide_elements.ts") tslang_add_test(NAME test-compile-00-bigint-struct-layout COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00bigint_struct_layout.ts") tslang_add_test(NAME test-compile-00-union-member-padding COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00union_member_padding.ts") @@ -736,6 +737,7 @@ tslang_add_test(NAME test-jit-00-const-array-to-array-elements COMMAND test-runn tslang_add_test(NAME test-jit-00-array-widen-elements-now COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_widen_elements_now.ts") tslang_add_test(NAME test-jit-00-const-array-nested-elements COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_array_nested_elements.ts") tslang_add_test(NAME test-jit-00-const-array-mutation COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_array_mutation.ts") +tslang_add_test(NAME test-jit-00-nested-array-literal-mutation COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00nested_array_literal_mutation.ts") tslang_add_test(NAME test-jit-00-array-move-wide-elements COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_move_wide_elements.ts") tslang_add_test(NAME test-jit-00-funcs-expression-generic COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_expression_generic.ts") tslang_add_test(NAME test-jit-00-funcs-expression-iterator COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00funcs_expression_iterator.ts") @@ -1480,6 +1482,7 @@ set(TSLANG_CORPUS 00add_promotes_both_operands.ts 00conditional_owned_result.ts 00const_array_nested_elements.ts + 00nested_array_literal_mutation.ts 00const_array_to_array_elements.ts 00array_widen_elements_now.ts 00owned_array_splice.ts diff --git a/tslang/test/tester/tests/00nested_array_literal_mutation.ts b/tslang/test/tester/tests/00nested_array_literal_mutation.ts new file mode 100644 index 000000000..8d10cc466 --- /dev/null +++ b/tslang/test/tester/tests/00nested_array_literal_mutation.ts @@ -0,0 +1,35 @@ +// A nested array literal's inner arrays are mutable. Turning the literal into an array copied only +// the outer data to the heap: the inner arrays kept pointing at the literal's constant data, so a +// push onto one reallocated a global (a crash in the allocator) and an element write wrote one. +const moduleNested = [[1, 2], [3]]; + +function main() { + const nested = [[1, 2], [3]]; + nested[1].push(4); + nested[0][0] = 9; + assert(nested[1].length == 2, "const: push onto an inner array"); + assert(nested[1][1] == 4, "const: pushed element"); + assert(nested[0][0] == 9, "const: element write in an inner array"); + + let reassignable = [[5], [6, 7]]; + reassignable[0].push(8); + assert(reassignable[0].length == 2, "let: push onto an inner array"); + assert(reassignable[0][1] == 8, "let: pushed element"); + + const deep = [[[1]], [[2, 3]]]; + deep[1][0].push(4); + assert(deep[1][0].length == 3, "three levels: push onto the innermost array"); + + moduleNested[0].push(10); + assert(moduleNested[0].length == 3, "module: push onto an inner array"); + assert(moduleNested[0][2] == 10, "module: pushed element"); + + // a fresh literal each time: the first call's writes are not seen by the second + for (let i = 0; i < 2; i++) { + const fresh = [[0]]; + assert(fresh[0][0] == 0, "fresh literal per iteration"); + fresh[0][0] = 1; + } + + print("done."); +}