From c25d2a7db7f7d60899470f67cfd934929661dd6b Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Sat, 17 Oct 2026 14:53:26 +0100 Subject: [PATCH] `in` with a number, a boolean or a bigint on the right is an error `"a" in 5` compiled with no diagnostic and was false: the field lookup on a number found nothing. TypeScript rejects a primitive on the right of `in`; so does tslang now, at the right operand. A string on the right stays an extension (`"length" in s`, tests/00in_method_names.ts). Co-Authored-By: Claude Opus 5.5 --- tslang/lib/TypeScript/MLIRGenImpl.h | 11 +++++++++++ tslang/test/tester/CMakeLists.txt | 10 ++++++++++ .../test/tester/diagnostic-location/in_primitive.ts | 8 ++++++++ 3 files changed, 29 insertions(+) create mode 100644 tslang/test/tester/diagnostic-location/in_primitive.ts diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index b62a7b92e..d0a4bf792 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -5303,6 +5303,17 @@ class MLIRGenImpl EXIT_IF_FAILED_OR_NO_VALUE(resultRight) auto rightExpressionValue = V(resultRight); + // a number, a boolean or a bigint has no fields to look up (a string's `length` is an + // extension: tests/00in_method_names.ts) + auto rightBaseType = mth.getBaseType(rightExpressionValue.getType()); + if (mth.isNumericType(rightBaseType) || isa(rightBaseType) + || isa(rightBaseType)) + { + emitError(loc(binaryExpressionAST->right), "The right-hand side of an 'in' expression must not be a primitive: '") + << to_print(rightExpressionValue.getType()) << "'"; + return mlir::failure(); + } + if (isa(rightExpressionValue.getType())) { emitError(loc(binaryExpressionAST->right), "not supported"); diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index e92db2eb9..34ac45fa8 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -2729,6 +2729,16 @@ set_tests_properties(test-compile-ref-index-store PROPERTIES PASS_REGULAR_EXPRESSION "store\\.ts:8:5: error: indexing a 'Ref' gives a reference to the element; store through it with 'Deref\\(p\\[i\\]\\) = value'" FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed") +# `in` with a number, a boolean or a bigint on the right is an error: it was quietly false. A +# string on the right stays an extension (its `length`). +add_test(NAME test-compile-diagnostic-location-in-primitive + COMMAND $ --emit=obj --no-default-lib -mm=none + "${PROJECT_SOURCE_DIR}/test/tester/diagnostic-location/in_primitive.ts" + -o "${CMAKE_CURRENT_BINARY_DIR}/diagnostic-location-in-primitive.obj") +set_tests_properties(test-compile-diagnostic-location-in-primitive + PROPERTIES PASS_REGULAR_EXPRESSION "in_primitive\\.ts:7:18: error: The right-hand side of an 'in' expression must not be a primitive: '5'" + FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed|in_primitive\\.ts:6:") + # A number, string, boolean or array passed where an interface with members is expected is an # error; it compiled to a cast lowering could not make, and the callee read garbage. An interface # without members still takes any value. diff --git a/tslang/test/tester/diagnostic-location/in_primitive.ts b/tslang/test/tester/diagnostic-location/in_primitive.ts new file mode 100644 index 000000000..73f15e857 --- /dev/null +++ b/tslang/test/tester/diagnostic-location/in_primitive.ts @@ -0,0 +1,8 @@ +// The right-hand side of `in` that is a number had no fields to look in, and the test was +// quietly false. + +function main() { + const s = "hello"; + print("length" in s); + print("a" in 5); +}