From c3474cfa7ae6d0ef1e6620040aa5237ecce641f8 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Wed, 28 Oct 2026 22:30:01 -0500 Subject: [PATCH] Test that receivers are evaluated once and before effectful keys Adds the corpus program receiver-before-effectful-key.ts, with its corpus cost and order baseline: - a tuple read with a runtime index that rebinds the tuple or replaces the field holding it; - a keyed read through a finite union of field names whose key rebinds the record or replaces the field holding it; - an inherited-key `in` test on a computed null-prototype receiver, which runs the receiver once. Each case reads the receiver as evaluated before the key, as in Node. --- .../receiver-before-effectful-key.ts.json | 6 ++ tests/corpus/receiver-before-effectful-key.ts | 59 +++++++++++++++++++ .../receiver-before-effectful-key.ts.json | 1 + 3 files changed, 66 insertions(+) create mode 100644 packages/compiler/test/ts7/baselines/order-parity/tests/corpus/receiver-before-effectful-key.ts.json create mode 100644 tests/corpus/receiver-before-effectful-key.ts create mode 100644 tests/harness/corpus-costs/receiver-before-effectful-key.ts.json diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/receiver-before-effectful-key.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/receiver-before-effectful-key.ts.json new file mode 100644 index 000000000..b44d5a127 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/receiver-before-effectful-key.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/receiver-before-effectful-key.ts" + ], + "diags": [] +} diff --git a/tests/corpus/receiver-before-effectful-key.ts b/tests/corpus/receiver-before-effectful-key.ts new file mode 100644 index 000000000..496c9c4c3 --- /dev/null +++ b/tests/corpus/receiver-before-effectful-key.ts @@ -0,0 +1,59 @@ +// A receiver is evaluated once, before its key, even when the key's +// evaluation rebinds the receiver's binding or has other effects: a tuple +// read with a runtime index, a keyed read through a finite union of field +// names, and an inherited-key `in` test on a computed index-signature +// receiver. + +const log: string[] = []; + +// Tuple read with a runtime index that rebinds the tuple. +let pair: [string, number] = ["first", 1]; +function rebindPair(i: number): number { + log.push(`rebind pair ${i}`); + pair = ["second", 2]; + return i; +} +const viaIndex = pair[rebindPair(0)]; +console.log("tuple", viaIndex, pair[0]); + +// A field holding a tuple, replaced by the index. +const holder: { t: [number, number, number] } = { t: [10, 20, 30] }; +function replaceHeld(i: number): number { + holder.t = [-1, -2, -3]; + return i; +} +console.log("tuple field", holder.t[replaceHeld(2)], holder.t[2]); + +// Keyed read through a finite union of keys naming a subset of the fields, +// with a key that rebinds the local or replaces the field holding the record. +type Row = { width?: number; label: string; tags: string[] }; +function finiteKeys(): void { + let row: Row = { width: 640, label: "old", tags: [] }; + const pick = (): "width" | "label" => { + log.push("pick key"); + row = { width: 1, label: "new", tags: ["x"] }; + return "label"; + }; + const picked = row[pick()]; + console.log("keyed", picked, row.label); + const box: { row: Row } = { row: { label: "boxed", tags: [] } }; + const replace = (): "width" | "label" => { + box.row = { width: 7, label: "replaced", tags: [] }; + return "width"; + }; + const width = box.row[replace()]; + console.log("keyed field", width, box.row.width); +} +finiteKeys(); + +// `in` on an inherited key of a computed null-prototype record: the +// receiver call runs once. +let groups = 0; +function grouped(): Partial> { + groups++; + log.push("grouped"); + return Object.groupBy([1, 2, 3], (n) => (n % 2 === 0 ? "even" : "odd")); +} +console.log("in", "toString" in grouped(), "hasOwnProperty" in grouped(), groups); + +console.log(log.join(" | ")); diff --git a/tests/harness/corpus-costs/receiver-before-effectful-key.ts.json b/tests/harness/corpus-costs/receiver-before-effectful-key.ts.json new file mode 100644 index 000000000..bb380e241 --- /dev/null +++ b/tests/harness/corpus-costs/receiver-before-effectful-key.ts.json @@ -0,0 +1 @@ +{ "plain": 2500, "san": 3000 }