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
7 changes: 4 additions & 3 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1888,8 +1888,8 @@ class MLIRGenImpl
//
// Under rc, so does a const read out of a field (`const c = h.c`) or an element (`const c =
// a[0]`, `const [c] = a`): folded, `h.c = d` or `a[0] = d` freed what `c` still read. So
// does an interface or a bound method made over a field read (`const i: I = h.v`,
// `const m = h.v.get`), which holds the block as `c` would. Under own the folded read is a
// 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 block as `c` would. Under own the folded read is a
// borrow, and a use after the overwrite is an error there.
//
// And a const record whose fields own blocks (`const o = { s: p.child }`): the literal
Expand All @@ -1910,7 +1910,8 @@ class MLIRGenImpl
|| ((isa<mlir_ts::ArrayType>(type) || MLIRTypeHelper::isSharedHandleType(type)
|| ((readsLocalSlot(initial) || viewsLocalSlot(initial)) && mth.ownsHeapMemory(location, type))
|| (compileOptions.isRefCounted()
&& (readsField(initial) || readsElement(initial) || readsField(viewedValue(initial)))
&& (readsField(initial) || readsElement(initial) || readsField(viewedValue(initial))
|| readsElement(viewedValue(initial)))
&& mth.ownsHeapMemory(location, type))
|| (readsLiteralSlot(mth, location, initial) && isa<mlir_ts::TupleType>(type)
&& mth.ownsHeapMemory(location, type))
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 @@ -392,6 +392,7 @@ tslang_add_test(NAME test-compile-00-shared-global-field-argument COMMAND test-r
tslang_add_test(NAME test-compile-00-narrowed-assign-other-member COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00narrowed_assign_other_member.ts")
tslang_add_test(NAME test-compile-00-bound-method-count COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00bound_method_count.ts")
tslang_add_test(NAME test-compile-00-const-view-of-field COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_view_of_field.ts")
tslang_add_test(NAME test-compile-00-const-view-of-element COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_view_of_element.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-generator-temporaries COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_temporaries.ts")
tslang_add_test(NAME test-compile-00-array-length-valid COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_length_valid.ts")
Expand Down Expand Up @@ -967,6 +968,7 @@ tslang_add_test(NAME test-jit-00-shared-global-field-argument COMMAND test-runne
tslang_add_test(NAME test-jit-00-narrowed-assign-other-member COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00narrowed_assign_other_member.ts")
tslang_add_test(NAME test-jit-00-bound-method-count COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00bound_method_count.ts")
tslang_add_test(NAME test-jit-00-const-view-of-field COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_view_of_field.ts")
tslang_add_test(NAME test-jit-00-const-view-of-element COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_view_of_element.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-generator-temporaries COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_temporaries.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")
Expand Down Expand Up @@ -1932,6 +1934,7 @@ set(TSLANG_CORPUS
00narrowed_assign_other_member.ts
00bound_method_count.ts
00const_view_of_field.ts
00const_view_of_element.ts
00const_record_owned_fields.ts
00generator_temporaries.ts
00array_length_valid.ts
Expand Down
57 changes: 57 additions & 0 deletions tslang/test/tester/tests/00const_view_of_element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// A `const` interface or bound method made over an element read keeps the object alive when the
// element is given a new value, as a `let` does. Folded, the const was the view itself and held no
// reference of its own: under rc `arr[0] = mkv(..)` freed the object `m()` and `i.get()` then ran
// on. Each call follows allocations of the same size, so a freed object is reused.
interface I {
get(): number;
}

class W {
constructor(public x: number) {}
}

class V {
items: number[] = [1, 2, 3];
constructor(public w: W) {}

get() {
return this.w.x;
}
}

function mkv(n: number) {
return new V(new W(n));
}

function churn(): number {
let s: number = 0;
const a: V[] = [];
for (let i = 0; i < 64; i++) a.push(mkv(1000 + i));
for (const e of a) s += e.w.x;
return s;
}

function boundMethod(n: number) {
const arr: V[] = [mkv(n), mkv(0)];
const m = arr[0].get;
arr[0] = mkv(n + 1);
churn();
return m() == n && arr[0].w.x == n + 1;
}

function interfaceView(n: number) {
const arr: V[] = [mkv(0), mkv(n)];
const i: I = arr[1];
arr[1] = mkv(n + 1);
churn();
return i.get() == n && arr[1].w.x == n + 1;
}

function main() {
for (let n = 0; n < 20; n++) {
assert(boundMethod(n), "a const bound method over an element");
assert(interfaceView(n), "a const interface over an element");
}

print("done.");
}
Loading