From 2c68ae817f6ac6a1908be8fa05be56a1a5c32958 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Tue, 13 Oct 2026 17:37:36 +0100 Subject: [PATCH] A store to an indexed Ref names the form that stores `bytes[i]` of a `Ref` is the element's reference, as a read gives it (`Deref(bytes[i])`), so `bytes[i] = 65` has nothing to store to and reported "saving to constant object". It now says to store through the reference: `Deref(bytes[i]) = value`, which works. Found writing a std::string's characters into a tuple's bytes (TSLANG_ISSUES.md 17). Co-Authored-By: Claude Opus 5.5 --- tslang/lib/TypeScript/MLIRGenImpl.h | 7 +++++++ tslang/test/tester/CMakeLists.txt | 10 ++++++++++ tslang/test/tester/ref-index-store/store.ts | 9 +++++++++ 3 files changed, 26 insertions(+) create mode 100644 tslang/test/tester/ref-index-store/store.ts diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index c4b942158..fd6f64934 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -6192,6 +6192,13 @@ class MLIRGenImpl else { LLVM_DEBUG(dbgs() << "\n!! left expr.: " << leftExpressionValueBeforeCast << " ...\n";); + // `p[i]` of a `Ref` is the reference to the element (as a read gives it), not the element + if (leftExpressionValueBeforeCast.getDefiningOp()) + { + emitError(location, "indexing a 'Ref' gives a reference to the element; store through it with 'Deref(p[i]) = value'"); + return mlir::failure(); + } + emitError(location, "saving to constant object"); return mlir::failure(); } diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 891d8b56e..9f77f13a2 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -2699,6 +2699,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 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 + COMMAND $ --emit=obj --no-default-lib -mm=none + "${PROJECT_SOURCE_DIR}/test/tester/ref-index-store/store.ts" + -o "${CMAKE_CURRENT_BINARY_DIR}/ref-index-store.obj") +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") + # 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/ref-index-store/store.ts b/tslang/test/tester/ref-index-store/store.ts new file mode 100644 index 000000000..0dd2fa3ae --- /dev/null +++ b/tslang/test/tester/ref-index-store/store.ts @@ -0,0 +1,9 @@ +// Indexing a Ref gives the element's reference, as a read does (`Deref(bytes[i])`), so a store +// to it is an error that names the form that stores: `Deref(bytes[i]) = value`. +type Str = [buf: [_0: u64, _1: u64], size: index]; + +function main() { + let value: Str = [[0, 0], 0]; + let bytes = >Ref(value); + bytes[0] = 65; +}