Skip to content

miscompile: an uncalled function containing new Proxy(arr, {}) makes an unrelated function's indexed read loop run zero iterations #7775

Description

@proggeramlug

Symptom

The mere PRESENCE of new Proxy(rawArray, {}) in a function that is never called makes an unrelated function's for (let i = 0; i < a.length; i++) read loop run zero iterations. Wrong answer, no diagnostic.

Minimal reproducer

class P { x: number; y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } }
function build(n: number): P[] {
  const a: P[] = [];
  for (let i = 0; i < n; i++) a.push(new P(i, i + 1));
  return a;
}
function readLoop(): number {
  const a = build(10);
  let s = 0;
  for (let i = 0; i < a.length; i++) { const r = a[i]; s += r.x + r.y; }
  return s;
}
function neverCalled(): number {
  const raw = build(10);
  const a: P[] = new Proxy(raw, {}) as any;
  let s = 0;
  for (let i = 0; i < a.length; i++) { const r = a[i]; s += r.x + r.y; }
  return s;
}
console.log(readLoop());

node 26.5.1: 100. Perry: 0.

What is established

  • Delete neverCalled (or just its new Proxy line's function) → Perry prints 100. Bisected from a 10-case battery: of delete a[i], a[i] = undefined, a different-class store, class X extends Array, a.length = 5, and the proxy — only the proxy variant poisons the module (the others are all byte-identical to node in the same file).
  • The poisoning is static: neverCalled is never invoked. readLoop's loop body itself runs zero times (a leak = r side effect in a sibling case never fires), so the loop condition — not the sum — is what breaks.
  • The proxy loop ITSELF is correct when it is the only such function in the module (prints 100, byte-identical to node).
  • Reproduces on pristine main @ 423bb44 (v0.5.1448) and is byte-identical on the repsel: a proven element fetch is still a runtime call — specialize a[i] inside the element-shape guarded clone #7771 branch (A/B'd both arms) — long pre-existing, not introduced by any recent element-shape work.
  • perry-dev profile, PERRY_NO_AUTO_OPTIMIZE=1, runtime pinned via PERRY_RUNTIME_DIR.

Found while building the #7771 criterion-3 hazard battery — the proxy hazard case had to be moved to its own compilation unit to keep the other nine cases meaningful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regression

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions