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
9 changes: 6 additions & 3 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1725,7 +1725,8 @@ class MLIRGenImpl
return readsLocalSlot(viewedValue(value));
}

// A value read out of a field, through any casts: what `h.c` is in `const c = h.c`.
// A value read out of a field, through any casts: what `h.c` is in `const c = h.c`, seen
// through an interface too (`const c = ix.c`), and what a handle holds (`const v = s.value`).
static bool readsField(mlir::Value value)
{
while (auto castOp = value ? value.getDefiningOp<mlir_ts::CastOp>() : mlir_ts::CastOp())
Expand All @@ -1734,7 +1735,8 @@ class MLIRGenImpl
}

auto loadOp = value ? value.getDefiningOp<mlir_ts::LoadOp>() : mlir_ts::LoadOp();
return loadOp && loadOp.getReference().getDefiningOp<mlir_ts::PropertyRefOp>();
return loadOp && mlir::isa_and_nonnull<mlir_ts::PropertyRefOp, mlir_ts::InterfaceSymbolRefOp,
mlir_ts::SharedValueRefOp>(loadOp.getReference().getDefiningOp());
}

// A value read out of a global variable, through any casts: what `G` is in `const n = G`.
Expand Down Expand Up @@ -1898,7 +1900,8 @@ class MLIRGenImpl
// interface or a bound method made over such a read (`const i: I = a`, `const m = a.get`):
// it holds the block as `b` would.
//
// Under rc, so does a const read out of a field (`const c = h.c`), an element (`const c =
// Under rc, so does a const read out of a field (`const c = h.c`, also through an interface,
// `const c = ix.c`, and a handle's value, `const c = s.value`), an element (`const c =
// a[0]`, `const [c] = a`) or a global (`const c = G`): folded, `h.c = d`, `a[0] = d` or
// `G = d` - here or in a call - freed what `c` still read. So does an interface or a bound
// method made over such a read (`const i: I = h.v`, `const m = a[0].get`), which holds the
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 @@ -405,6 +405,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-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")
tslang_add_test(NAME test-compile-00-unowned-local-store COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00unowned_local_store.ts")
Expand Down Expand Up @@ -988,6 +989,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-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")
tslang_add_test(NAME test-jit-00-unowned-local-store COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00unowned_local_store.ts")
Expand Down Expand Up @@ -1961,6 +1963,7 @@ set(TSLANG_CORPUS
00const_global_read.ts
00global_argument_reassigned.ts
00global_argument_view.ts
00const_interface_field_read.ts
00argument_place_overwritten.ts
00temporary_read_in_finally.ts
00unowned_local_store.ts
Expand Down
83 changes: 83 additions & 0 deletions tslang/test/tester/tests/00const_interface_field_read.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// A `const` read out of a field through an interface (`const v = ix.v`), or out of what a Shared
// handle holds (`const v = s.value`), keeps what it read alive when that field is given a new
// value. Folded, the const was the read itself and held no reference of its own: under rc the
// overwrite freed the block it still read (00const_field_read.ts is the class field case).
// Allocations of the same size reuse anything freed before each read.
class V {
tag = "";
constructor(public x: number) {
this.tag = "v" + x;
}
}

class N {
constructor(public v: V) {}
}

interface IX {
v: V;
}

function churn() {
let keep: V[] = [];
for (let i = 0; i < 1000; i++) keep.push(new V(999));
return keep.length;
}

let GS = new Shared<V>(new V(1));

function interfaceOfLiteral(i: number) {
const ix: IX = { v: new V(0) };
ix.v = new V(i);
const v = ix.v;
ix.v = new V(9);
churn();
assert(v.x == i && v.tag == "v" + i, "a const read through an interface over an object literal");
}

function interfaceOfClass(i: number) {
const ix: IX = new N(new V(0));
ix.v = new V(i);
const v = ix.v;
ix.v = new V(9);
churn();
assert(v.x == i && v.tag == "v" + i, "a const read through an interface over a class");
}

function interfaceInLoop() {
let ix: IX = { v: new V(1) };
for (let i = 0; i < 3; i++) {
const v = ix.v;
ix.v = new V(i + 10);
churn();
assert(v.x == (i == 0 ? 1 : i + 9), "a const read through an interface, in a loop");
}
}

function sharedValue(i: number) {
GS.value = new V(i);
const v = GS.value;
GS.value = new V(9);
churn();
assert(v.x == i && v.tag == "v" + i, "a const read of a handle's value");
}

function sharedHandleReplaced(i: number) {
GS = new Shared<V>(new V(i));
const v = GS.value;
GS = new Shared<V>(new V(9));
churn();
assert(v.x == i && v.tag == "v" + i, "a const read of a handle's value, the handle replaced");
}

function main() {
for (let i = 2; i < 5; i++) {
interfaceOfLiteral(i);
interfaceOfClass(i);
sharedValue(i);
sharedHandleReplaced(i);
}

interfaceInLoop();
print("done.");
}
Loading