From ebf7f5368367893ba27540fcbaf6be6c8abb3051 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Sat, 17 Oct 2026 14:53:31 +0100 Subject: [PATCH] A switch case of another kind of primitive is an error `switch (5) { case "a": ... }` compiled with no diagnostic: the switch's value was cast to the case's type (the number became a string) and compared. TypeScript rejects a case whose type is not comparable to the switch's; tslang now rejects a case that is a number, string, boolean or bigint when the switch is another of those, at the case expression, with TypeScript's message. Unions, any, enums of mixed members and other types are left as they were. Co-Authored-By: Claude Opus 5.5 --- tslang/lib/TypeScript/MLIRGenImpl.h | 20 +++++++++++++++++++ tslang/test/tester/CMakeLists.txt | 10 ++++++++++ .../diagnostic-location/switch_case_type.ts | 10 ++++++++++ 3 files changed, 40 insertions(+) create mode 100644 tslang/test/tester/diagnostic-location/switch_case_type.ts diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index b62a7b92e..d1100f5e8 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -4615,6 +4615,26 @@ class MLIRGenImpl EXIT_IF_FAILED_OR_NO_VALUE(result) caseValue = V(result); + // a case of another kind of primitive than the switch's (`switch (5) { case "a": }`) + // can never match: the cast made one out of the other instead + auto primitiveKind = [&](mlir::Type type) { + auto baseType = mth.getBaseType(type); + if (mth.isNumericType(baseType)) return 1; + if (isa(baseType)) return 2; + if (isa(baseType)) return 3; + if (isa(baseType)) return 4; + return 0; + }; + + auto switchKind = primitiveKind(switchValue.getType()); + auto caseKind = primitiveKind(caseValue.getType()); + if (switchKind && caseKind && switchKind != caseKind) + { + emitError(loc(caseExpr)) << "Type '" << to_print(caseValue.getType()) << "' is not comparable to type '" + << to_print(switchValue.getType()) << "'"; + return mlir::failure(); + } + auto switchValueEffective = switchValue; auto actualCaseType = mth.stripLiteralType(caseValue.getType()); if (switchValue.getType() != actualCaseType) diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index e92db2eb9..c8ad23e39 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -2719,6 +2719,16 @@ add_test(NAME test-compile-integer-literal-truncation-quiet set_tests_properties(test-compile-integer-literal-truncation-quiet PROPERTIES FAIL_REGULAR_EXPRESSION "warning:|error:|Stack dump|Assertion failed") +# A switch's case of another kind of primitive (a string for a number) is an error, as it can never +# match: the switch's value was cast to the case's type instead. +add_test(NAME test-compile-diagnostic-location-switch-case-type + COMMAND $ --emit=obj --no-default-lib -mm=none + "${PROJECT_SOURCE_DIR}/test/tester/diagnostic-location/switch_case_type.ts" + -o "${CMAKE_CURRENT_BINARY_DIR}/diagnostic-location-switch-case-type.obj") +set_tests_properties(test-compile-diagnostic-location-switch-case-type + PROPERTIES PASS_REGULAR_EXPRESSION "switch_case_type\\.ts:8:14: error: Type '\"a\"' is not comparable to type '5'" + FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed|switch_case_type\\.ts:7:") + # A store to an indexed Ref (`bytes[i] = x`) names the form that stores, `Deref(bytes[i]) = x`, # instead of "saving to constant object". add_test(NAME test-compile-ref-index-store diff --git a/tslang/test/tester/diagnostic-location/switch_case_type.ts b/tslang/test/tester/diagnostic-location/switch_case_type.ts new file mode 100644 index 000000000..70ec7dfc3 --- /dev/null +++ b/tslang/test/tester/diagnostic-location/switch_case_type.ts @@ -0,0 +1,10 @@ +// A case of another kind of primitive than the switch's could never match: the number was made +// a string to compare, with no error. + +function main() { + const n = 5; + switch (n) { + case 5: print("five"); break; + case "a": print("a"); break; + } +}