From 014ba42ab4d2fb586c53c27255878fc0927cd5e3 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Tue, 27 Oct 2026 18:04:32 +0000 Subject: [PATCH 1/4] -mm=own: reverse and sort move string elements by copy reverse reads the element it swaps into a `let`, as sort's `temp` did: a `const` borrows the slot overwritten next. sort no longer assigns its optional comparator a default - a callback parameter that is assigned is one -mm=own knows nothing about, so a call of it with two elements was a use of them after a call that may release them; it gives a selectionSort helper the comparator or the default. Needs the compiler's copy of a string read out of a parameter the result borrows. Co-Authored-By: Claude Opus 5.5 --- src/generics/lib.generics.ts | 43 ++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/generics/lib.generics.ts b/src/generics/lib.generics.ts index 9656081..a7b2bf1 100644 --- a/src/generics/lib.generics.ts +++ b/src/generics/lib.generics.ts @@ -321,8 +321,10 @@ namespace __Array { const shift = this.length - 1; const size = this.length >> 1; for (let i = 0; i < size; i++) { - const v = this[i]; - this[i] = this[shift - i] + // a `let`, as sort's `temp`: under -mm=own a string element is copied out, where a + // `const` would borrow the slot overwritten next + let v = this[i]; + this[i] = this[shift - i]; this[shift - i] = v; } @@ -362,30 +364,37 @@ namespace __Array { return false; } + // The comparison is a parameter the sort only calls, never one it assigns: under -mm=own what a + // callback may release is known from the functions given for it, and a parameter assigned a + // default is one nothing knows - so a call of it with two elements would be a use of them after + // it may release them. + function selectionSort(a: T[], compare: (value1: T, value2: T) => int) { + const len = a.length; + for (let i = 0; i < len - 1; ++i) { + for (let j = i + 1; j < len; ++j) { + if (compare(a[i], a[j]) > 0) { + let temp: T = a[i]; + a[i] = a[j]; + a[j] = temp; + } + } + } + } + function sort(this: T[], callbackfn?: (value1: T, value2: T) => int): T[] { if (this.length <= 0) { return this; } - if (!callbackfn) { - callbackfn = (a, b) => { + if (callbackfn) { + selectionSort(this, callbackfn); + } else { + selectionSort(this, (a: T, b: T) => { const sa = a; const sb = b; if (sa == sb) return 0; return sa > sb ? 1 : -1; - }; - } - - const len = this.length; - // simple selection sort. - for (let i = 0; i < len - 1; ++i) { - for (let j = i + 1; j < len; ++j) { - if (callbackfn(this[i], this[j]) > 0) { - let temp: T = this[i]; - this[i] = this[j]; - this[j] = temp; - } - } + }); } return this; From 304cdd3fd95131a53279891c388a7779198412f7 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Tue, 27 Oct 2026 18:40:34 +0000 Subject: [PATCH 2/4] -mm=own: reverse and sort exchange elements with `[a[i], a[j]] = [a[j], a[i]]` The compiler's exchange of two places (spec 42) moves nothing in any model, where a swap through a local gave an object element a second owner under -mm=own. reverse over class instances now passes under own (array_reverse_class). sort over class instances (array_sort_class) is `// not own`: its comparison is handed on to selectionSort through a parameter, which has no callback facts yet. Needs the compiler's `ts.Swap`. Co-Authored-By: Claude Opus 5.5 --- src/generics/lib.generics.ts | 13 +++++-------- tests/array_reverse_class.ts | 19 +++++++++++++++++++ tests/array_sort_class.ts | 23 +++++++++++++++++++++++ 3 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 tests/array_reverse_class.ts create mode 100644 tests/array_sort_class.ts diff --git a/src/generics/lib.generics.ts b/src/generics/lib.generics.ts index a7b2bf1..853884d 100644 --- a/src/generics/lib.generics.ts +++ b/src/generics/lib.generics.ts @@ -321,11 +321,9 @@ namespace __Array { const shift = this.length - 1; const size = this.length >> 1; for (let i = 0; i < size; i++) { - // a `let`, as sort's `temp`: under -mm=own a string element is copied out, where a - // `const` would borrow the slot overwritten next - let v = this[i]; - this[i] = this[shift - i]; - this[shift - i] = v; + // an exchange of the two elements, which moves nothing: through a local, under -mm=own + // an object element would have two owners + [this[i], this[shift - i]] = [this[shift - i], this[i]]; } return this; @@ -373,9 +371,8 @@ namespace __Array { for (let i = 0; i < len - 1; ++i) { for (let j = i + 1; j < len; ++j) { if (compare(a[i], a[j]) > 0) { - let temp: T = a[i]; - a[i] = a[j]; - a[j] = temp; + // an exchange, as in reverse + [a[i], a[j]] = [a[j], a[i]]; } } } diff --git a/tests/array_reverse_class.ts b/tests/array_reverse_class.ts new file mode 100644 index 0000000..e5c8bd4 --- /dev/null +++ b/tests/array_reverse_class.ts @@ -0,0 +1,19 @@ +// reverse over class instances, in place: under -mm=own the elements are exchanged, which moves +// nothing +class Item { + constructor(public key: number, public name: string) {} +} + +function main() { + const items = [new Item(1, "a"), new Item(2, "b"), new Item(3, "c"), new Item(4, "d"), new Item(5, "e")]; + + const reversed = items.reverse(); + console.log(reversed[0].name, reversed[2].name, reversed[4].name); + // Expected output: "e c a" + assert(items[0].key == 5 && items[1].key == 4 && items[2].key == 3 && items[4].key == 1, "reversed in place"); + + items.reverse(); + assert(items[0].name == "a" && items[4].name == "e", "reversed back"); + + console.log("ALL DONE"); +} diff --git a/tests/array_sort_class.ts b/tests/array_sort_class.ts new file mode 100644 index 0000000..f0d7d54 --- /dev/null +++ b/tests/array_sort_class.ts @@ -0,0 +1,23 @@ +// not own: the comparison is forwarded through sort's optional parameter to its helper, and a callback +// given on through a parameter has no facts under -mm=own yet, so the call with two elements is a use +// of them after a call that may release them +// sort over class instances, in place. sort's default comparison (compiled even when a comparison is +// given) converts an element to a string, which needs a class to say how. +class Item { + constructor(public key: number, public name: string) {} + + toString() { + return this.name; + } +} + +function main() { + const items = [new Item(3, "c"), new Item(1, "a"), new Item(4, "d"), new Item(2, "b")]; + + const sorted = items.sort((p, q) => p.key - q.key); + console.log(sorted[0].name, sorted[1].name, sorted[2].name, sorted[3].name); + // Expected output: "a b c d" + assert(items[0].key == 1 && items[3].key == 4, "sorted in place"); + + console.log("ALL DONE"); +} From e7b1fb61e912c594c412aba087b17641dcdf9d91 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Tue, 27 Oct 2026 19:31:06 +0000 Subject: [PATCH 3/4] -mm=own: sort over class instances with a comparison compiles sort gives its optional comparison on to selectionSort, and a callback given on through a parameter now keeps the facts of the functions passed for it (compiler spec 43), so array_sort_class is no longer `// not own`. Co-Authored-By: Claude Opus 5.5 --- tests/array_sort_class.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/array_sort_class.ts b/tests/array_sort_class.ts index f0d7d54..e2308d8 100644 --- a/tests/array_sort_class.ts +++ b/tests/array_sort_class.ts @@ -1,6 +1,3 @@ -// not own: the comparison is forwarded through sort's optional parameter to its helper, and a callback -// given on through a parameter has no facts under -mm=own yet, so the call with two elements is a use -// of them after a call that may release them // sort over class instances, in place. sort's default comparison (compiled even when a comparison is // given) converts an element to a string, which needs a class to say how. class Item { From 7343d61834edac5fa9949a849cdea21a6b542dd3 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Tue, 27 Oct 2026 20:24:52 +0000 Subject: [PATCH 4/4] -mm=own: array entries, and the Array class holds shared elements - entries() reads the element into the state before the yield. Own does not follow a tuple made from a borrow inside the yield itself yet. - class Array is @sharedElements, as Map and Set are: concat, fill and slice copy elements, and an object has one owner, so an object goes in as Shared. Its iterators say @borrows("this"), as T[]'s do. filter2 hands back T[]'s generator; as a generator itself, it returned that generator as its done value. concat no longer spreads other into a rest argument, which under own would move the arrays. - array_class_splice (an Array) is marked "not own". Needs the compiler's own-defaultlib-batch for new Array() (a constructor parameter with a default) and the other own fixes. Co-Authored-By: Claude Opus 5.5 --- src/generics/lib.generics.ts | 24 +++++++++++++++++++++--- tests/array_class_splice.ts | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/generics/lib.generics.ts b/src/generics/lib.generics.ts index 853884d..03f5817 100644 --- a/src/generics/lib.generics.ts +++ b/src/generics/lib.generics.ts @@ -105,7 +105,10 @@ namespace __Array { function* entries(this: T[]) { for (let i = 0; i in this; i++) { - yield [i, this[i]]; + // under -mm=own the element is read into the state before the yield: a tuple made from + // a borrow of it in the yield itself is not one own can follow yet + const v = this[i]; + yield [i, v]; } } @@ -438,6 +441,9 @@ namespace __Array { } } +// -mm=own: elements are values, strings or Shared handles, as a Map's are: concat, fill and slice +// copy them, and an object has one owner; an object goes in as Shared +@sharedElements class Array { constructor(private data: T[] = []) { @@ -527,10 +533,18 @@ class Array { return this.data.copyWithin(target, start, end); } + // not `this.data.concat(...other)`: under -mm=own the spread would move other's arrays into a new one public concat(...other: T[][]) { - return this.data.concat(...other); + let result = this.data.slice(); + for (const item of other) { + for (const v of item) result.push(v); + } + + return result; } + // the iterators borrow the array, as T[]'s do: under -mm=own they may not outlive it + @borrows("this") public entries() { return this.data.entries(); } @@ -551,7 +565,8 @@ class Array { return this.data.filter(func); } - public *filter2(func: (v: T) => boolean) { + @borrows("this", "func") + public filter2(func: (v: T) => boolean) { return this.data.filter2(func); } @@ -587,6 +602,7 @@ class Array { return this.data.join(separator); } + @borrows("this") public keys() { return this.data.keys(); } @@ -623,6 +639,7 @@ class Array { return this.data.sort(callbackfn); } + @borrows("this") public [Symbol.iterator]() { return this.data.values(); } @@ -635,6 +652,7 @@ class Array { return this.data.toSorted(callbackfn); } + @borrows("this") public values() { return this.data.values(); } diff --git a/tests/array_class_splice.ts b/tests/array_class_splice.ts index 836a978..8803ac8 100644 --- a/tests/array_class_splice.ts +++ b/tests/array_class_splice.ts @@ -1,3 +1,4 @@ +// not own: an Array of objects, which -mm=own refuses (an object goes into an Array as Shared) // splice, shift and unshift on an Array (the class, not T[]): they were missing, so // `childNodes: Array` could not remove a child (TypeScriptCompiler #231).