Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6482,6 +6482,16 @@ class MLIRGenImpl
return mlir::failure();
}

// An element's address is taken anew once the value is ready. The one the left side was
// read through was taken before the right side ran, and the right side may grow the array:
// `a[0] = f()` with `f` pushing to `a` moved its elements to a new block, and the store went
// into the one given back (a value lost under every model, a freed block written to under
// rc). The array and the index are the ones the left side evaluated, as JavaScript has it.
if (auto elementRefOp = storeReference.getDefiningOp<mlir_ts::ElementRefOp>())
{
storeReference = builder.clone(*elementRefOp)->getResult(0);
}

// Overwriting owning storage hands the count over: the incoming value gains an
// owner and the outgoing one loses it. Retaining first is what makes `x = x` safe -
// releasing first could drop the last reference and free the value about to be
Expand Down
3 changes: 3 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ tslang_add_test(NAME test-compile-00-const-element-read COMMAND test-runner "${P
tslang_add_test(NAME test-compile-00-const-global-read COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_global_read.ts")
tslang_add_test(NAME test-compile-00-global-argument-reassigned COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_argument_reassigned.ts")
tslang_add_test(NAME test-compile-00-global-argument-view COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_argument_view.ts")
tslang_add_test(NAME test-compile-00-element-store-after-value COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00element_store_after_value.ts")
tslang_add_test(NAME test-compile-00-const-interface-field-read COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_interface_field_read.ts")
tslang_add_test(NAME test-compile-00-argument-place-overwritten COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00argument_place_overwritten.ts")
tslang_add_test(NAME test-compile-00-temporary-read-in-finally COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00temporary_read_in_finally.ts")
Expand Down Expand Up @@ -991,6 +992,7 @@ tslang_add_test(NAME test-jit-00-const-element-read COMMAND test-runner -jit "${
tslang_add_test(NAME test-jit-00-const-global-read COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_global_read.ts")
tslang_add_test(NAME test-jit-00-global-argument-reassigned COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_argument_reassigned.ts")
tslang_add_test(NAME test-jit-00-global-argument-view COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00global_argument_view.ts")
tslang_add_test(NAME test-jit-00-element-store-after-value COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00element_store_after_value.ts")
tslang_add_test(NAME test-jit-00-const-interface-field-read COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_interface_field_read.ts")
tslang_add_test(NAME test-jit-00-argument-place-overwritten COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00argument_place_overwritten.ts")
tslang_add_test(NAME test-jit-00-temporary-read-in-finally COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00temporary_read_in_finally.ts")
Expand Down Expand Up @@ -1966,6 +1968,7 @@ set(TSLANG_CORPUS
00const_global_read.ts
00global_argument_reassigned.ts
00global_argument_view.ts
00element_store_after_value.ts
00const_interface_field_read.ts
00argument_place_overwritten.ts
00temporary_read_in_finally.ts
Expand Down
67 changes: 67 additions & 0 deletions tslang/test/tester/tests/00element_store_after_value.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// `a[i] = f()` stores into the array once `f()` has run, even when `f` grows the array. The
// element's address was taken before the right side ran, and growing moved the elements to a new
// block: the store went into the one given back. The value was lost under every model, and under
// rc a freed block was written to. The array and the index are still the ones the left side
// evaluated first, as in JavaScript.
class V {
constructor(public x: number) {}
}

class H {
a: number[] = [1];
items: V[] = [new V(1)];
}

function growNumbers(a: number[]) {
for (let i = 0; i < 16; i++) a.push(i);
return 7;
}

function growItems(items: V[]) {
for (let i = 0; i < 16; i++) items.push(new V(100 + i));
return new V(8);
}

function numbers() {
const a: number[] = [1];
a[0] = growNumbers(a);
return a.length == 17 && a[0] == 7 && a[1] == 0;
}

function objects() {
const items = [new V(1)];
items[0] = growItems(items);
return items.length == 17 && items[0].x == 8 && items[16].x == 115;
}

function compound() {
const a: number[] = [1];
a[0] += growNumbers(a);
return a[0] == 8;
}

function field() {
const h = new H();
h.a[0] = growNumbers(h.a);
h.items[0] = growItems(h.items);
return h.a[0] == 7 && h.items[0].x == 8;
}

function otherArray() {
let a: number[] = [1, 2];
const old = a;
a[1] = (a = [5], 9);
return old[1] == 9 && a.length == 1 && a[0] == 5;
}

function main() {
for (let i = 0; i < 3; i++) {
assert(numbers(), "a number stored after the call grows the array");
assert(objects(), "an object stored after the call grows the array");
assert(compound(), "a compound assignment after the call grows the array");
assert(field(), "an element of a field stored after the call grows the array");
assert(otherArray(), "the left side's array, though the right side assigns the variable");
}

print("done.");
}
Loading