From f57e9ec1caa9694b3384f2ef843d367faea1a139 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Sun, 4 Oct 2026 17:48:04 +0100 Subject: [PATCH] The arrays of a tuple literal built at run time can be changed (#486) Two shapes failed to compile: - A constant array in a tuple that has a run-time value (`let t = [x, [1]]`) kept its constant-array type, so `t[1].push(2)` was "Can't resolve property 'push'". createTupleFromArrayLiteral now makes it an array of its own, a heap copy, as it already did for a constant tuple holding an array (#484). - A `const` of a tuple built at run time (`const nt = [x, [2, [3, 4]]]`) was folded into the CreateTuple value, so `nt[1][1].push(5)` had no reference to change the array through ("Can't get reference of the array"), and nothing counted the arrays in it. Such a const now has storage when the tuple owns a block, as a `let` has and as a record literal's const has since #485. Under own, `const r = [i, [i, i]]; r[1].push(5)` now gives the error the `let` form already gave ("'this value' takes a second reference") instead of "Can't get reference of the array". Closes #486 Co-Authored-By: Claude Opus 5.5 --- tslang/lib/TypeScript/MLIRGenImpl.h | 12 +++++++++ tslang/test/tester/CMakeLists.txt | 3 +++ .../tester/tests/00tuple_runtime_arrays.ts | 27 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 tslang/test/tester/tests/00tuple_runtime_arrays.ts diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index 0863c8e67..328f1f392 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -1611,12 +1611,18 @@ class MLIRGenImpl // And a tuple literal folded to a constant with an array in it (`const st = [1, [6, 7]]`, // `const o = { items: [1, 2] }`): its arrays are static headers over constant data, so // it is widened to a tuple of heap copies once, here, like a const array literal (#479). + // + // And a tuple literal built at run time (`const nt = [x, [2, [3, 4]]]`), whose arrays + // are born with no count: folded, nothing counted them, and `nt[1][1].push(5)` had no + // reference to change the array through (#486). MLIRTypeHelper mth(builder.getContext(), compileOptions); needsIdentityStorage = mth.hasBoundMethodField(type) || isa(type) || MLIRTypeHelper::isConstTupleHoldingArray(type) || ((isa(type) || MLIRTypeHelper::isSharedHandleType(type) || (readsLocalSlot(initial) && mth.ownsHeapMemory(location, type)) || (readsLiteralSlot(mth, location, initial) && isa(type) + && mth.ownsHeapMemory(location, type)) + || (initial && initial.getDefiningOp() && isa(type) && mth.ownsHeapMemory(location, type))) && !(varClass == VariableType::ConstRef) && !(initial && initial.getDefiningOp())); @@ -9698,6 +9704,12 @@ class MLIRGenImpl { CAST(value, location, mth.convertConstTupleTypeToTupleType(value.getType()), value, genContext); } + // and a constant array (`[x, [1]]`) is an array of its own, which push and pop change: + // kept as the literal's constant data, `t[1].push(2)` did not resolve (#486) + else if (isa(value.getType())) + { + CAST(value, location, mth.convertConstArrayTypeToArrayType(value.getType()), value, genContext); + } fieldInfos.push_back({mlir::Attribute(), value.getType(), false, mlir_ts::AccessLevel::Public}); arrayValues.push_back(value); diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index aaefe1548..909c83f7e 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -348,6 +348,7 @@ tslang_add_test(NAME test-compile-00-array-rest-copy COMMAND test-runner "${PROJ tslang_add_test(NAME test-compile-00-arrays6 COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array6.ts") tslang_add_test(NAME test-compile-00-array-static-nested COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_static_nested.ts") tslang_add_test(NAME test-compile-00-array-literal-copy COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_literal_copy.ts") +tslang_add_test(NAME test-compile-00-tuple-runtime-arrays COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00tuple_runtime_arrays.ts") tslang_add_test(NAME test-compile-00-const-record-owned-fields COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_record_owned_fields.ts") tslang_add_test(NAME test-compile-00-array-length-valid COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_length_valid.ts") tslang_add_test(NAME test-compile-00-string-view-copy COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00string_view_copy.ts") @@ -853,6 +854,7 @@ tslang_add_test(NAME test-jit-00-array-rest-copy COMMAND test-runner -jit "${PRO tslang_add_test(NAME test-jit-00-arrays6 COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array6.ts") tslang_add_test(NAME test-jit-00-array-static-nested COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_static_nested.ts") tslang_add_test(NAME test-jit-00-array-literal-copy COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_literal_copy.ts") +tslang_add_test(NAME test-jit-00-tuple-runtime-arrays COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00tuple_runtime_arrays.ts") tslang_add_test(NAME test-jit-00-const-record-owned-fields COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_record_owned_fields.ts") tslang_add_test(NAME test-jit-00-array-length-valid COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_length_valid.ts") tslang_add_test(NAME test-jit-00-string-view-copy COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00string_view_copy.ts") @@ -1687,6 +1689,7 @@ set(TSLANG_CORPUS 00array6.ts 00array_static_nested.ts 00array_literal_copy.ts + 00tuple_runtime_arrays.ts 00const_record_owned_fields.ts 00array_length_valid.ts 00string_view_copy.ts diff --git a/tslang/test/tester/tests/00tuple_runtime_arrays.ts b/tslang/test/tester/tests/00tuple_runtime_arrays.ts new file mode 100644 index 000000000..9e95ee903 --- /dev/null +++ b/tslang/test/tester/tests/00tuple_runtime_arrays.ts @@ -0,0 +1,27 @@ +// The arrays of a tuple literal built at run time can be changed (#486): a constant array in it +// (`[x, [1]]`) is a heap array of its own, not the literal's constant data that push did not +// resolve on, and a `const` of such a tuple has storage, as a `let` has, so `nt[1][1].push(5)` +// has a reference to change the array through and the arrays are counted. +function g(t: [number, number[]]) { t[1].push(9); return t[1].length; } +function main() { + let x = 3; + let t = [x, [1]]; + t[1].push(2); + assert(t[1].length == 2 && t[1][1] == 2, "let, constant array in a run-time tuple"); + const c = [x, [1]]; + c[1].pop(); + assert(c[1].length == 0, "const, constant array in a run-time tuple"); + const nt = [x, [2, [3, 4]]]; + nt[1][1].push(5); + assert(nt[1][1].length == 3, "const, nested"); + const s = ["a" + x, [x, x]]; + s[1].push(1); + assert(s[0] == "a3" && s[1].length == 3, "a string and an array"); + assert(g([x, [1, 2]]) == 3, "to a tuple parameter"); + for (let i = 0; i < 3; i++) { + const e = [i, [i]]; + e[1].push(i); + assert(e[1].length == 2, "a new array each pass"); + } + print("done."); +}