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
8 changes: 7 additions & 1 deletion tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1702,9 +1702,15 @@ class MLIRGenImpl
}

// The value an interface or a bound method is made over: `v` seen through `I` in
// `const i: I = v`, or `this` of `const m = v.get`.
// `const i: I = v`, or `this` of `const m = v.get`, through any casts: a declared type
// (`const m: () => number = v.get`) casts the bound method to a hybrid function.
static mlir::Value viewedValue(mlir::Value value)
{
while (auto castOp = value ? value.getDefiningOp<mlir_ts::CastOp>() : mlir_ts::CastOp())
{
value = castOp.getIn();
}

auto *definingOp = value ? value.getDefiningOp() : nullptr;
if (auto newInterfaceOp = dyn_cast_or_null<mlir_ts::NewInterfaceOp>(definingOp))
{
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 @@ -411,6 +411,7 @@ tslang_add_test(NAME test-compile-00-closure-const-read COMMAND test-runner "${P
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-const-bound-method-declared-type COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_bound_method_declared_type.ts")
tslang_add_test(NAME test-compile-00-argument-place-aliased COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00argument_place_aliased.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 @@ -1001,6 +1002,7 @@ tslang_add_test(NAME test-jit-00-closure-const-read COMMAND test-runner -jit "${
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-const-bound-method-declared-type COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_bound_method_declared_type.ts")
tslang_add_test(NAME test-jit-00-argument-place-aliased COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00argument_place_aliased.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 @@ -1981,6 +1983,7 @@ set(TSLANG_CORPUS
00element_store_after_value.ts
00const_interface_field_read.ts
00argument_place_overwritten.ts
00const_bound_method_declared_type.ts
00argument_place_aliased.ts
00temporary_read_in_finally.ts
00unowned_local_store.ts
Expand Down
115 changes: 115 additions & 0 deletions tslang/test/tester/tests/00const_bound_method_declared_type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// A const bound method with a declared type (`const c: () => number = G.items[0].get`) keeps its
// object alive, as one without does. Under rc a const made over a read of a field, an element or a
// global holds a count of its own; the declared type casts the bound method to a hybrid function,
// and that cast hid the read, so the const was folded with no count, and `G = mk()` freed the
// object `c()` still ran on. Each read follows allocations of the same size, so a freed block is
// reused.
class W {
constructor(public x: number) {}
}

class V {
x: number;
w: W;
a: number[];

constructor(x: number) {
this.x = x;
this.w = new W(x);
this.a = [x];
}

get(): number {
return this.x;
}
}

class N {
v: V;
items: V[];

constructor(x: number) {
this.v = new V(x);
this.items = [new V(x)];
}
}

@noinline function mk(i: number): N {
return new N(i);
}

function churn() {
const junk: V[] = [];
for (let i = 0; i < 64; i++) {
junk.push(new V(777));
}
}

let G: N = mk(2);

interface IN {
v: V;
items: V[];
}

@noinline function mv(x: number): V {
return new V(x);
}

function take(n: N, i: IN) {
return i.items[0].x;
}

function fieldOfGlobal(x: number) {
G = mk(x);
const c: () => number = G.v.get;
G = mk(0);
churn();
return c();
}

function fieldOfLocal(x: number) {
let n = mk(x);
const c: () => number = n.v.get;
n = mk(0);
churn();
return c();
}

function local(x: number) {
let v = new V(x);
const c: () => number = v.get;
v = new V(0);
churn();
return c();
}

function inLoop(x: number) {
G = mk(x);
const c: () => number = G.items[0].get;
for (let i = 0; i < 2; i++) {
G = mk(i);
churn();
}

return c();
}

function main() {
// an element of a global, the fuzzer's shape
const c: () => number = G.items[0].get;
let s: number = 0;
for (let i = 0; i < 1; i++) {
G = mk(4);
s += take(mk(5), { v: mv(i + 5), items: [mv(2)] });
}

assert(c() == 2, "an element of a global");
assert(s == 2, "the call's own element");

assert(fieldOfGlobal(4) == 4, "a field of a global");
assert(fieldOfLocal(5) == 5, "a field of a local");
assert(local(6) == 6, "a local");
assert(inLoop(7) == 7, "the global reassigned in a loop");
print("done.");
}
Loading