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
26 changes: 19 additions & 7 deletions tslang/include/TypeScript/MLIRLogic/MLIRTypeHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -2894,24 +2894,36 @@ class MLIRTypeHelper

if (auto genericClass = getGenericClassInfoByFullName(srcClassInfo->originClassType.getName().getValue()))
{
// every type argument has to extend, not only the first:
// Pair<number, number> is not a Pair<number, string>
auto result = ExtendsResult::True;
for (auto &typeParam : genericClass->typeParams)
{
auto name = typeParam->getName();
auto srcFound = srcClassInfo->typeParamsWithArgs.find(name);
auto extFound = extClassInfo->typeParamsWithArgs.find(name);
if (srcFound != srcClassInfo->typeParamsWithArgs.end() &&
extFound != extClassInfo->typeParamsWithArgs.end())
if (srcFound == srcClassInfo->typeParamsWithArgs.end() ||
extFound == extClassInfo->typeParamsWithArgs.end())
{
auto srcType = srcFound->getValue().second;
auto extType = extFound->getValue().second;
return ExtendsResult::False;
}

return extendsType(location, srcType, extType, typeParamsWithArgs);
auto srcType = srcFound->getValue().second;
auto extType = extFound->getValue().second;

auto argResult = extendsType(location, srcType, extType, typeParamsWithArgs);
if (!isTrue(argResult))
{
return argResult;
}
else

if (argResult == ExtendsResult::Any)
{
return ExtendsResult::False;
result = argResult;
}
}

return result;
}

// default behavior - false, because something is different
Expand Down
24 changes: 24 additions & 0 deletions tslang/lib/TypeScript/MLIRGenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1515,6 +1515,30 @@ namespace mlirgen
}
}

// Two specializations of one generic class keep fields of different types in the same
// place: `const b: Box<string> = new Box<number>(1)` read a number as a string and crashed.
// One's type arguments have to extend the other's - Box<Derived> as Box<Base>, or back as
// a downcast - which is TypeScript's rule for a type assertion as well.
if (auto classType = dyn_cast<mlir_ts::ClassType>(type))
{
if (auto valueClassType = dyn_cast<mlir_ts::ClassType>(valueType))
{
auto classInfo = getClassInfoByFullName(classType.getName().getValue());
auto valueClassInfo = getClassInfoByFullName(valueClassType.getName().getValue());
if (classInfo && valueClassInfo && classInfo->originClassType
&& classInfo->originClassType == valueClassInfo->originClassType)
{
llvm::StringMap<std::pair<ts::TypeParameterDOM::TypePtr,mlir::Type>> typeParamsWithArgs;
if (!isTrue(mth.extendsType(location, valueType, type, typeParamsWithArgs))
&& !isTrue(mth.extendsType(location, type, valueType, typeParamsWithArgs)))
{
emitError(location, "type ") << to_print(valueType) << " is not assignable to type " << to_print(type);
return mlir::failure();
}
}
}
}

if (isa<mlir_ts::ClassType>(type) || isa<mlir_ts::InterfaceType>(type))
{
if (isa<mlir_ts::NumberType>(valueType)
Expand Down
21 changes: 21 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ tslang_add_test(NAME test-compile-01-sizeof COMMAND test-runner "${PROJECT_SOURC
tslang_add_test(NAME test-compile-02-sizeof COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/02sizeof.ts")
tslang_add_test(NAME test-compile-00-new-delete COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00new_delete.ts")
tslang_add_test(NAME test-compile-00-new-generic-from-receiver COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00new_generic_from_receiver.ts")
tslang_add_test(NAME test-compile-00-generic-specialization-cast COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00generic_specialization_cast.ts")
tslang_add_test(NAME test-compile-00-owned-locals COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00owned_locals.ts")
# The JIT variants are the ones that matter here: this shape was a JIT-only miscompile until
# the catchable-type size was fixed (see the file header). Kept in all three models, since the
Expand Down Expand Up @@ -838,6 +839,7 @@ tslang_add_test(NAME test-jit-01-sizeof COMMAND test-runner -jit "${PROJECT_SOUR
tslang_add_test(NAME test-jit-02-sizeof COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/02sizeof.ts")
tslang_add_test(NAME test-jit-00-new-delete COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00new_delete.ts")
tslang_add_test(NAME test-jit-00-new-generic-from-receiver COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00new_generic_from_receiver.ts")
tslang_add_test(NAME test-jit-00-generic-specialization-cast COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00generic_specialization_cast.ts")
tslang_add_test(NAME test-jit-00-owned-locals COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00owned_locals.ts")
tslang_add_test(NAME test-jit-00-throw-in-catch COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00throw_in_catch.ts")
tslang_add_test(NAME test-jit-00-throw-inlined COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00throw_inlined.ts")
Expand Down Expand Up @@ -1711,6 +1713,7 @@ set(TSLANG_CORPUS
00try_mismatch_in_catch_clause_using.ts
00new_delete.ts
00new_generic_from_receiver.ts
00generic_specialization_cast.ts
00ns_bug.ts
00ns.ts
00ns2.ts
Expand Down Expand Up @@ -2501,6 +2504,24 @@ set_tests_properties(test-compile-declare-overloads-error
PROPERTIES PASS_REGULAR_EXPRESSION "'abs' is declared again with another signature"
FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed|redefinition of symbol")

# A cast between two specializations of one generic class whose type arguments don't extend each
# other is an error - it compiled, and then read one field type as another.
add_test(NAME test-compile-specialization-cast-box-error
COMMAND $<TARGET_FILE:tslang> --emit=obj --no-default-lib -mm=none
"${PROJECT_SOURCE_DIR}/test/tester/specialization-cast/box.ts"
-o "${CMAKE_CURRENT_BINARY_DIR}/specialization-cast-box.obj")
set_tests_properties(test-compile-specialization-cast-box-error
PROPERTIES PASS_REGULAR_EXPRESSION "Box<!ts.number> is not assignable to type Box<!ts.string>"
FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed")

add_test(NAME test-compile-specialization-cast-pair-error
COMMAND $<TARGET_FILE:tslang> --emit=obj --no-default-lib -mm=none
"${PROJECT_SOURCE_DIR}/test/tester/specialization-cast/pair.ts"
-o "${CMAKE_CURRENT_BINARY_DIR}/specialization-cast-pair.obj")
set_tests_properties(test-compile-specialization-cast-pair-error
PROPERTIES PASS_REGULAR_EXPRESSION "is not assignable to type Pair<!ts.number,!ts.string>"
FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed")

# 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
10 changes: 10 additions & 0 deletions tslang/test/tester/specialization-cast/box.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Box<number> and Box<string> keep a different type in `v`; reading one as the other crashed.
class Box<T> {
constructor(public v: T) {}
}

function main() {
const a = new Box<number>(1);
const b: Box<string> = a;
print(b.v);
}
10 changes: 10 additions & 0 deletions tslang/test/tester/specialization-cast/pair.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Every type argument has to match, not only the first.
class Pair<A, B> {
constructor(public a: A, public b: B) {}
}

function main() {
const p = new Pair<number, number>(1, 2);
const q: Pair<number, string> = p;
print(q.b);
}
34 changes: 34 additions & 0 deletions tslang/test/tester/tests/00generic_specialization_cast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// A cast between two specializations of one generic class is allowed when one's type arguments
// extend the other's: up (Box<Dog> as Box<Animal>) and back down again.
class Animal {
name = "animal";
}

class Dog extends Animal {
bark() {
return 1;
}
}

class Box<T> {
constructor(public v: T) {}
}

class Pair<A, B> {
constructor(public a: A, public b: B) {}
}

function main() {
const d = new Box<Dog>(new Dog());
const up: Box<Animal> = d;
assert(up.v.name == "animal", "up");

const down = <Box<Dog>>up;
assert(down.v.bark() == 1, "down");

const p = new Pair<Dog, number>(new Dog(), 2);
const q: Pair<Animal, number> = p;
assert(q.b == 2, "pair");

print("done.");
}
Loading