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
12 changes: 12 additions & 0 deletions tslang/lib/TypeScript/MLIRGenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,18 @@ namespace mlirgen

return castTupleToTuple(location, unboxedTuple, srcTupleType, fields, genContext);
}

// an object literal is boxed for its methods and accessors, which a class instance cannot
// have of its own: it takes them from its class's vtable. The cast was a reinterpretation
// of the pointer, so the class read its fields and vtable out of the literal's layout -
// garbage, and a crash on a method call (#494).
if (auto classType = dyn_cast<mlir_ts::ClassType>(type))
{
emitError(location, "an object literal with methods or accessors cannot be assigned to class '")
<< to_print(classType)
<< "': a class instance takes its methods from its class; use 'new', or an interface type";
return mlir::failure();
}
}

return std::nullopt;
Expand Down
13 changes: 13 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3165,6 +3165,19 @@ set_tests_properties(test-compile-class-cast-unrelated-error
PROPERTIES PASS_REGULAR_EXPRESSION "type A is not assignable to type B: 'v' is number, not string"
FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed")

# An object literal with methods or accessors assigned to a class is an error: a class instance
# takes its methods from its class. The cast reinterpreted the pointer, so the class read garbage
# fields and crashed on a method call (#494).
foreach(object_literal_to_class_name method accessor parameter return)
add_test(NAME test-compile-object-literal-to-class-${object_literal_to_class_name}
COMMAND $<TARGET_FILE:tslang> --emit=obj --no-default-lib -mm=none
"${PROJECT_SOURCE_DIR}/test/tester/object-literal-to-class/${object_literal_to_class_name}.ts"
-o "${CMAKE_CURRENT_BINARY_DIR}/object-literal-to-class-${object_literal_to_class_name}.obj")
set_tests_properties(test-compile-object-literal-to-class-${object_literal_to_class_name}
PROPERTIES PASS_REGULAR_EXPRESSION "an object literal with methods or accessors cannot be assigned to class 'C'"
FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed")
endforeach()

# The shared-component tier under the other two models. A shared library records the model
# it was built under, so both halves of a pair are built with the same flag - which is what
# these run. The file pairs are the default model's, verbatim.
Expand Down
9 changes: 9 additions & 0 deletions tslang/test/tester/object-literal-to-class/accessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// An object literal with an accessor assigned to a class (#494).
class C {
x: number;
}

function main() {
const c: C = { x: 3, get y() { return 2; } };
print(c.x);
}
11 changes: 11 additions & 0 deletions tslang/test/tester/object-literal-to-class/method.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// An object literal with a method assigned to a class (#494): the cast reinterpreted the pointer,
// so c.x read garbage and c.f() crashed.
class C {
x: number;
f() { return 1; }
}

function main() {
const c: C = { x: 3, f() { return 2; } };
print(c.x, c.f());
}
13 changes: 13 additions & 0 deletions tslang/test/tester/object-literal-to-class/parameter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// An object literal with a method passed to a class parameter (#494).
class C {
x: number;
f() { return 1; }
}

function take(c: C) {
return c.x;
}

function main() {
print(take({ x: 3, f() { return 2; } }));
}
13 changes: 13 additions & 0 deletions tslang/test/tester/object-literal-to-class/return.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// An object literal with a method returned as a class (#494).
class C {
x: number;
f() { return 1; }
}

function make(): C {
return { x: 3, f() { return 2; } };
}

function main() {
print(make().x);
}
Loading