diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index 8e6b9693f..693573256 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -10010,7 +10010,16 @@ class MLIRGenImpl if (type != receiverElementType) { value = builder.getUnitAttr(); - itemValue = cast(location, receiverElementType, itemValue, genContext); + // a field that cannot be the receiver's (`kind: "ci"` for a `kind: "sq"`) is an error: its + // failed cast was a null value, the literal came out with no value at all, and an assignment + // of it stored nothing - with the cast's error postponed, and so never shown (#513) + auto castResult = cast(location, receiverElementType, itemValue, genContext); + if (castResult.failed_or_no_value()) + { + return mlir::failure(); + } + + itemValue = V(castResult); isConstValue = false; } diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index d0f0a113b..84dd2b892 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -2608,6 +2608,21 @@ endforeach() tslang_add_test(NAME test-jit-type-alias-not-circular COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/type-alias-circular/positive.ts") tslang_add_test(NAME test-compile-type-alias-not-circular COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/type-alias-circular/positive.ts") +# An object literal with a field the receiver's type cannot hold (`kind: "ci"` for `kind: "sq"`) is an +# error (issue #513). The field's failed cast was dropped: the literal had no value, an assignment of it +# stored nothing, and the cast's error, postponed like every message, was never shown. +foreach(literal_field_mismatch_name assign declare argument return) + add_test(NAME test-compile-literal-field-mismatch-${literal_field_mismatch_name} + COMMAND $ --emit=obj --no-default-lib -mm=none + "${PROJECT_SOURCE_DIR}/test/tester/literal-field-mismatch/${literal_field_mismatch_name}.ts" + -o "${CMAKE_CURRENT_BINARY_DIR}/literal-field-mismatch-${literal_field_mismatch_name}.obj") + set_tests_properties(test-compile-literal-field-mismatch-${literal_field_mismatch_name} + PROPERTIES PASS_REGULAR_EXPRESSION "can't cast from literal type: '\"ci\"' to '\"sq\"'" + FAIL_REGULAR_EXPRESSION "Expected 1 arguments|Stack dump|Assertion failed") +endforeach() +tslang_add_test(NAME test-jit-literal-field-match COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/literal-field-mismatch/positive.ts") +tslang_add_test(NAME test-compile-literal-field-match COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/literal-field-mismatch/positive.ts") + # An error reported by a pass that still returns success - a lowering pattern that cannot build a # value, says so and carries on - fails the compile. It used to be printed and then ignored: the object # file was written, and under --emit=jit the program ran and crashed. Two tests because one diff --git a/tslang/test/tester/literal-field-mismatch/argument.ts b/tslang/test/tester/literal-field-mismatch/argument.ts new file mode 100644 index 000000000..1cfb57b30 --- /dev/null +++ b/tslang/test/tester/literal-field-mismatch/argument.ts @@ -0,0 +1,10 @@ +// reported "Expected 1 arguments, but got 0" +type Sq = { kind: "sq"; side: number }; + +function f(s: Sq) { + return s.kind; +} + +function main() { + print(f({ kind: "ci", r: 1 })); +} diff --git a/tslang/test/tester/literal-field-mismatch/assign.ts b/tslang/test/tester/literal-field-mismatch/assign.ts new file mode 100644 index 000000000..59a844034 --- /dev/null +++ b/tslang/test/tester/literal-field-mismatch/assign.ts @@ -0,0 +1,8 @@ +// #513: compiled with no error, and `s` kept "sq" +type Sq = { kind: "sq"; side: number }; + +function main() { + let s: Sq = { kind: "sq", side: 4 }; + s = { kind: "ci", r: 1 }; + print(s.kind, s.side); +} diff --git a/tslang/test/tester/literal-field-mismatch/declare.ts b/tslang/test/tester/literal-field-mismatch/declare.ts new file mode 100644 index 000000000..239dc4abd --- /dev/null +++ b/tslang/test/tester/literal-field-mismatch/declare.ts @@ -0,0 +1,7 @@ +// compiled with no error, and `t` held garbage +type Sq = { kind: "sq"; side: number }; + +function main() { + let t: Sq = { kind: "ci", r: 1 }; + print(t.kind); +} diff --git a/tslang/test/tester/literal-field-mismatch/positive.ts b/tslang/test/tester/literal-field-mismatch/positive.ts new file mode 100644 index 000000000..20a36bff6 --- /dev/null +++ b/tslang/test/tester/literal-field-mismatch/positive.ts @@ -0,0 +1,26 @@ +// literals that are the receiver's type still assign, declare, pass and return +type Sq = { kind: "sq"; side: number }; +type Ci = { kind: "ci"; r: number }; + +function f(s: Sq) { + return s.side; +} + +function g(): Sq { + return { kind: "sq", side: 3 }; +} + +function main() { + let u: Sq | Ci = { kind: "ci", r: 1 }; + assert(u.kind == "ci", "union, declared"); + u = { kind: "sq", side: 2 }; + assert(u.kind == "sq", "union, assigned"); + + let s: Sq = { kind: "sq", side: 4 }; + s = { kind: "sq", side: 6 }; + assert(s.side == 6, "assigned"); + assert(f({ kind: "sq", side: 5 }) == 5, "argument"); + assert(g().side == 3, "returned"); + + print("done."); +} diff --git a/tslang/test/tester/literal-field-mismatch/return.ts b/tslang/test/tester/literal-field-mismatch/return.ts new file mode 100644 index 000000000..390c9d31e --- /dev/null +++ b/tslang/test/tester/literal-field-mismatch/return.ts @@ -0,0 +1,10 @@ +// a returned literal of the other member +type Sq = { kind: "sq"; side: number }; + +function g(): Sq { + return { kind: "ci", r: 1 }; +} + +function main() { + print(g().kind); +} diff --git a/tslang/test/tester/tests/export_object_literal_with_class_types.ts b/tslang/test/tester/tests/export_object_literal_with_class_types.ts index 1f7cd60db..e4d768564 100644 --- a/tslang/test/tester/tests/export_object_literal_with_class_types.ts +++ b/tslang/test/tester/tests/export_object_literal_with_class_types.ts @@ -1,3 +1,5 @@ +// @strict-null false +// `left: null` for a Point: null is not a Point under strict null checks namespace A { class Point {