diff --git a/CLAUDE.md b/CLAUDE.md index 6ad5dcd1c8..a40fd31d53 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Perry is a native TypeScript compiler written in Rust that compiles TypeScript source code directly to native executables. It uses SWC for TypeScript parsing and LLVM for code generation. -**Current Version:** 0.5.1516 +**Current Version:** 0.5.1517 ## TypeScript Parity Status diff --git a/Cargo.lock b/Cargo.lock index a71492aa2e..5f58538fb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5598,7 +5598,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perry" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "base64 0.22.1", @@ -5658,7 +5658,7 @@ dependencies = [ [[package]] name = "perry-api-manifest" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-dispatch", "serde", @@ -5666,7 +5666,7 @@ dependencies = [ [[package]] name = "perry-audio-miniaudio" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "cc", "libc", @@ -5675,7 +5675,7 @@ dependencies = [ [[package]] name = "perry-codegen" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "inkwell", @@ -5692,7 +5692,7 @@ dependencies = [ [[package]] name = "perry-codegen-arkts" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "perry-hir", @@ -5700,7 +5700,7 @@ dependencies = [ [[package]] name = "perry-codegen-glance" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "perry-hir", @@ -5708,7 +5708,7 @@ dependencies = [ [[package]] name = "perry-codegen-js" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "perry-dispatch", @@ -5717,7 +5717,7 @@ dependencies = [ [[package]] name = "perry-codegen-swiftui" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "perry-hir", @@ -5725,7 +5725,7 @@ dependencies = [ [[package]] name = "perry-codegen-wasm" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "base64 0.22.1", @@ -5737,7 +5737,7 @@ dependencies = [ [[package]] name = "perry-codegen-wear-tiles" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "perry-hir", @@ -5745,7 +5745,7 @@ dependencies = [ [[package]] name = "perry-container-compose" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "async-trait", @@ -5774,14 +5774,14 @@ dependencies = [ [[package]] name = "perry-container-e2e" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", ] [[package]] name = "perry-diagnostics" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "serde", "serde_json", @@ -5789,7 +5789,7 @@ dependencies = [ [[package]] name = "perry-dispatch" -version = "0.5.1516" +version = "0.5.1517" [[package]] name = "perry-doc-fixture-my-bindings" @@ -5800,7 +5800,7 @@ dependencies = [ [[package]] name = "perry-doc-tests" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "clap", @@ -5815,7 +5815,7 @@ dependencies = [ [[package]] name = "perry-ext-ads" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "block2", "objc2", @@ -5825,7 +5825,7 @@ dependencies = [ [[package]] name = "perry-ext-argon2" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "argon2", "perry-ffi", @@ -5834,7 +5834,7 @@ dependencies = [ [[package]] name = "perry-ext-axios" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "reqwest", @@ -5843,7 +5843,7 @@ dependencies = [ [[package]] name = "perry-ext-bcrypt" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "bcrypt", "perry-ffi", @@ -5851,7 +5851,7 @@ dependencies = [ [[package]] name = "perry-ext-better-sqlite3" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "rusqlite", @@ -5859,7 +5859,7 @@ dependencies = [ [[package]] name = "perry-ext-cheerio" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "scraper", @@ -5867,7 +5867,7 @@ dependencies = [ [[package]] name = "perry-ext-commander" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "perry-runtime", @@ -5875,7 +5875,7 @@ dependencies = [ [[package]] name = "perry-ext-cron" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "chrono", "cron", @@ -5885,7 +5885,7 @@ dependencies = [ [[package]] name = "perry-ext-dayjs" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "chrono", "perry-ffi", @@ -5893,7 +5893,7 @@ dependencies = [ [[package]] name = "perry-ext-decimal" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "rust_decimal", @@ -5901,7 +5901,7 @@ dependencies = [ [[package]] name = "perry-ext-dotenv" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "serde_json", @@ -5909,7 +5909,7 @@ dependencies = [ [[package]] name = "perry-ext-ethers" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "rand 0.10.1", @@ -5917,7 +5917,7 @@ dependencies = [ [[package]] name = "perry-ext-events" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "perry-runtime", @@ -5925,14 +5925,14 @@ dependencies = [ [[package]] name = "perry-ext-exponential-backoff" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-fastify" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "bytes", "http-body-util", @@ -5950,7 +5950,7 @@ dependencies = [ [[package]] name = "perry-ext-fetch" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "bytes", "lazy_static", @@ -5963,7 +5963,7 @@ dependencies = [ [[package]] name = "perry-ext-http" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "base64 0.22.1", "bytes", @@ -5988,7 +5988,7 @@ dependencies = [ [[package]] name = "perry-ext-ioredis" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "lazy_static", "perry-ffi", @@ -5998,7 +5998,7 @@ dependencies = [ [[package]] name = "perry-ext-jsonwebtoken" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "base64 0.22.1", "jsonwebtoken", @@ -6009,7 +6009,7 @@ dependencies = [ [[package]] name = "perry-ext-lru-cache" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "lru", "perry-ffi", @@ -6018,7 +6018,7 @@ dependencies = [ [[package]] name = "perry-ext-moment" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "chrono", "perry-ffi", @@ -6026,7 +6026,7 @@ dependencies = [ [[package]] name = "perry-ext-mongodb" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "bson", "futures-util", @@ -6038,7 +6038,7 @@ dependencies = [ [[package]] name = "perry-ext-mysql2" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "chrono", "perry-ffi", @@ -6048,7 +6048,7 @@ dependencies = [ [[package]] name = "perry-ext-nanoid" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "nanoid", "perry-ffi", @@ -6057,7 +6057,7 @@ dependencies = [ [[package]] name = "perry-ext-net" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "bytes", "perry-ffi", @@ -6070,7 +6070,7 @@ dependencies = [ [[package]] name = "perry-ext-node-forge" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "const-oid 0.9.6", "der 0.7.10", @@ -6089,7 +6089,7 @@ dependencies = [ [[package]] name = "perry-ext-nodemailer" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "lettre", "perry-ffi", @@ -6099,7 +6099,7 @@ dependencies = [ [[package]] name = "perry-ext-parcel-watcher" -version = "0.5.1514" +version = "0.5.1517" dependencies = [ "fancy-regex", "notify", @@ -6111,7 +6111,7 @@ dependencies = [ [[package]] name = "perry-ext-pdf" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "printpdf", @@ -6119,7 +6119,7 @@ dependencies = [ [[package]] name = "perry-ext-pg" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "sqlx", @@ -6128,7 +6128,7 @@ dependencies = [ [[package]] name = "perry-ext-ratelimit" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "governor", "perry-ffi", @@ -6136,7 +6136,7 @@ dependencies = [ [[package]] name = "perry-ext-sharp" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "fast_image_resize", "image", @@ -6146,14 +6146,14 @@ dependencies = [ [[package]] name = "perry-ext-slugify" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-streams" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "lazy_static", "perry-ffi", @@ -6162,7 +6162,7 @@ dependencies = [ [[package]] name = "perry-ext-typescript" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "serde", @@ -6178,7 +6178,7 @@ dependencies = [ [[package]] name = "perry-ext-undici" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "perry-runtime", @@ -6187,7 +6187,7 @@ dependencies = [ [[package]] name = "perry-ext-uuid" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "uuid", @@ -6195,7 +6195,7 @@ dependencies = [ [[package]] name = "perry-ext-validator" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ffi", "regex", @@ -6205,7 +6205,7 @@ dependencies = [ [[package]] name = "perry-ext-ws" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "futures-util", "lazy_static", @@ -6218,7 +6218,7 @@ dependencies = [ [[package]] name = "perry-ext-zlib" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "brotli", "flate2", @@ -6228,7 +6228,7 @@ dependencies = [ [[package]] name = "perry-ffi" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "dashmap", "once_cell", @@ -6237,7 +6237,7 @@ dependencies = [ [[package]] name = "perry-hir" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "perry-api-manifest", @@ -6255,7 +6255,7 @@ dependencies = [ [[package]] name = "perry-parser" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "perry-diagnostics", @@ -6267,7 +6267,7 @@ dependencies = [ [[package]] name = "perry-runtime" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "base64 0.22.1", @@ -6309,14 +6309,14 @@ dependencies = [ [[package]] name = "perry-runtime-static" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-runtime", ] [[package]] name = "perry-stdlib" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "aes 0.8.4", "aes 0.9.1", @@ -6411,14 +6411,14 @@ dependencies = [ [[package]] name = "perry-stdlib-static" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-stdlib", ] [[package]] name = "perry-transform" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "perry-hir", @@ -6427,14 +6427,14 @@ dependencies = [ [[package]] name = "perry-ui" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ui-model", ] [[package]] name = "perry-ui-android" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "base64 0.22.1", "itoa", @@ -6452,7 +6452,7 @@ dependencies = [ [[package]] name = "perry-ui-geisterhand" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "rand 0.10.1", "serde", @@ -6462,7 +6462,7 @@ dependencies = [ [[package]] name = "perry-ui-gtk4" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "base64 0.22.1", "cairo-rs 0.22.0", @@ -6485,7 +6485,7 @@ dependencies = [ [[package]] name = "perry-ui-ios" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "base64 0.22.1", "block2", @@ -6502,7 +6502,7 @@ dependencies = [ [[package]] name = "perry-ui-macos" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "base64 0.22.1", "block2", @@ -6518,7 +6518,7 @@ dependencies = [ [[package]] name = "perry-ui-model" -version = "0.5.1516" +version = "0.5.1517" [[package]] name = "perry-ui-test" @@ -6529,11 +6529,11 @@ dependencies = [ [[package]] name = "perry-ui-testkit" -version = "0.5.1516" +version = "0.5.1517" [[package]] name = "perry-ui-tvos" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "base64 0.22.1", "block2", @@ -6550,7 +6550,7 @@ dependencies = [ [[package]] name = "perry-ui-visionos" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "base64 0.22.1", "block2", @@ -6567,7 +6567,7 @@ dependencies = [ [[package]] name = "perry-ui-watchos" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "block2", "libc", @@ -6581,7 +6581,7 @@ dependencies = [ [[package]] name = "perry-ui-windows" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "base64 0.22.1", "libc", @@ -6600,14 +6600,14 @@ dependencies = [ [[package]] name = "perry-ui-windows-winui" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "perry-ui-windows", ] [[package]] name = "perry-updater" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "anyhow", "base64 0.22.1", @@ -6623,7 +6623,7 @@ dependencies = [ [[package]] name = "perry-wasm-host" -version = "0.5.1516" +version = "0.5.1517" dependencies = [ "wasmi", ] diff --git a/Cargo.toml b/Cargo.toml index 2536a60bdd..9c19fff8ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -317,7 +317,7 @@ codegen-units = 16 codegen-units = 16 [workspace.package] -version = "0.5.1516" +version = "0.5.1517" edition = "2021" license = "MIT" repository = "https://github.com/PerryTS/perry" diff --git a/changelog.d/8566-e2e-scoped-drift.md b/changelog.d/8566-e2e-scoped-drift.md new file mode 100644 index 0000000000..74f069d87c --- /dev/null +++ b/changelog.d/8566-e2e-scoped-drift.md @@ -0,0 +1,6 @@ +Fixed several `e2e-scoped` failures that were pre-existing on `main` — surfaced while landing [#8550](https://github.com/PerryTS/perry/pull/8550) and confirmed via `git bisect` and cross-checks against unrelated branches, so none trace back to that PR's Rust toolchain pin. `e2e-scoped` (a required `pr-gate` context) only runs when a diff touches `crates/*/tests/*.rs`, which is why these went unnoticed on `main` pushes that don't touch those paths: + +- `manifest_consistency`: [#8527](https://github.com/PerryTS/perry/pull/8527) added a `worker_threads::reload` dispatch row, and [#8537](https://github.com/PerryTS/perry/pull/8537) added `bun::Glob` / `bun::unsupported` rows, none with matching `API_MANIFEST` entries — added the three missing entries. +- `argless_builtin_extra_args`, `native_proof_regressions`: [#8383](https://github.com/PerryTS/perry/pull/8383) intentionally dropped the unconditional per-consumer-module `__perry_wrap_extern_*` value wrapper and made inline-cache globals module-prefixed to avoid cross-module symbol collisions — both are stale IR-shape assertions from before that change; updated them to match the new, correct shape. +- `loop_safepoint_purity`: the same PR tightened module-global-export detection to require a matching `Export::Named` entry instead of trusting `exported_objects` alone (closing a "globalize the wrong local" hazard) — the test fixture never populated `exports`, so its `sum` accumulator silently stopped becoming a module global and the assertion it depends on (a global-target loop keeps its back-edge poll) stopped proving anything. Updated the fixture to add the matching export. +- `temp_root_operand_temporaries`: [#8450](https://github.com/PerryTS/perry/pull/8450) replaced `String.prototype.concat`'s single accumulator-with-write-back rooting with per-operand rooted slots feeding a fused `js_string_concat_chain` call — rewrote the assertion to check the new shape (each operand's rooted, re-read value lands in the scratch array) instead of counting stores into a shared accumulator slot that no longer exists. diff --git a/crates/perry-api-manifest/src/entries/part_2.rs b/crates/perry-api-manifest/src/entries/part_2.rs index 4b4c4b5f67..9868491512 100644 --- a/crates/perry-api-manifest/src/entries/part_2.rs +++ b/crates/perry-api-manifest/src/entries/part_2.rs @@ -1232,6 +1232,10 @@ pub(crate) const API_MANIFEST_PART_2: &[ApiEntry] = &[ method("worker_threads", "once", true, Some("Worker")), method("worker_threads", "off", true, Some("Worker")), method("worker_threads", "terminate", true, Some("Worker")), + // #8527 — global Web Worker AOT compile added the dispatch row + // (NativeModSig in native_table/extras.rs) without a manifest + // counterpart; this closes that drift. + method("worker_threads", "reload", true, Some("Worker")), // #4917 — real: `ref()`/`unref()` flip `WorkerRecord.refed`, which // `js_worker_threads_has_pending` checks to keep the event loop alive // (a live refed worker holds the process; `unref()` releases it). diff --git a/crates/perry-api-manifest/src/entries/part_4.rs b/crates/perry-api-manifest/src/entries/part_4.rs index 83569ff407..c775b41ae9 100644 --- a/crates/perry-api-manifest/src/entries/part_4.rs +++ b/crates/perry-api-manifest/src/entries/part_4.rs @@ -1090,6 +1090,11 @@ pub(crate) const API_MANIFEST_PART_4: &[ApiEntry] = &[ method("bun", "write", false, None), method("bun", "pathToFileURL", false, None), method("bun", "fileURLToPath", false, None), + // #8537 — OpenCode compatibility coverage added these dispatch rows + // (NativeModSig in native_table/bun.rs) without manifest counterparts; + // this closes that drift. + method("bun", "Glob", false, None), + method("bun", "unsupported", false, None), property("bun", "stdin"), property("bun", "stdout"), property("bun", "stderr"), diff --git a/crates/perry-codegen/tests/argless_builtin_extra_args.rs b/crates/perry-codegen/tests/argless_builtin_extra_args.rs index 421aec6800..e4a09ad275 100644 --- a/crates/perry-codegen/tests/argless_builtin_extra_args.rs +++ b/crates/perry-codegen/tests/argless_builtin_extra_args.rs @@ -175,8 +175,11 @@ fn any_call_result_trim_emits_string_tag_dispatch() { assert_eq!( ir.matches("= call double @perry_fn_schema_ts__make(") .count(), - 2, - "the receiver must be evaluated once in the init body (plus the generated extern wrapper):\n{ir}" + 1, + "the receiver must be evaluated exactly once, in the init body — #8383 dropped the \ + unconditional per-consumer-module `__perry_wrap_extern_*` value wrapper (imported \ + function values now materialize lazily via js_closure_alloc_singleton), so there is \ + no second call site to account for:\n{ir}" ); } diff --git a/crates/perry-codegen/tests/loop_safepoint_purity.rs b/crates/perry-codegen/tests/loop_safepoint_purity.rs index 5a8424f645..5ad1983845 100644 --- a/crates/perry-codegen/tests/loop_safepoint_purity.rs +++ b/crates/perry-codegen/tests/loop_safepoint_purity.rs @@ -37,7 +37,7 @@ use perry_codegen::{compile_module, AppMetadata, CompileOptions}; use perry_hir::types::Type; -use perry_hir::{BinaryOp, CompareOp, Expr, Module, ModuleInitKind, Stmt, UpdateOp}; +use perry_hir::{BinaryOp, CompareOp, Export, Expr, Module, ModuleInitKind, Stmt, UpdateOp}; fn entry_opts() -> CompileOptions { CompileOptions { @@ -156,6 +156,17 @@ fn ir_for_with_exported_vars(name: &str, init: Vec, exported: &[&str]) -> enable_back_edge_polls(); let mut m = module_with_init(name, init); m.exported_objects = exported.iter().map(|s| s.to_string()).collect(); + // #8383: module-global promotion now requires a matching `Export::Named` + // entry (deriving it from `exported_objects` alone could globalize an + // unrelated local sharing the public name), so the fixture must declare + // one for each name to still exercise the module-global path. + m.exports = exported + .iter() + .map(|s| Export::Named { + local: s.to_string(), + exported: s.to_string(), + }) + .collect(); String::from_utf8(compile_module(&m, entry_opts()).unwrap()).expect("LLVM IR should be UTF-8") } diff --git a/crates/perry-codegen/tests/native_proof_regressions.rs b/crates/perry-codegen/tests/native_proof_regressions.rs index d18958165e..a3de43bbdf 100644 --- a/crates/perry-codegen/tests/native_proof_regressions.rs +++ b/crates/perry-codegen/tests/native_proof_regressions.rs @@ -14437,7 +14437,10 @@ fn static_put_value_uses_write_pic_for_call_free_rhs() { "the write PIC should retain four inline entries plus a bounded outlined tail" ); assert!( - ir.contains("@perry_ic_0_poly_tail = private global"), + // #8383: inline-cache globals are now source-module-prefixed + // (`inline_cache_global_name`) so separately compiled modules can't + // collide on the same `perry_ic_N` symbol at final link. + ir.contains("@perry_ic_static_put_value_write_pic__0_poly_tail = private global"), "the outlined ways must use a distinct zero-initialized cache:\n{ir}" ); } diff --git a/crates/perry-codegen/tests/temp_root_operand_temporaries.rs b/crates/perry-codegen/tests/temp_root_operand_temporaries.rs index d673774904..291d0c4626 100644 --- a/crates/perry-codegen/tests/temp_root_operand_temporaries.rs +++ b/crates/perry-codegen/tests/temp_root_operand_temporaries.rs @@ -219,9 +219,17 @@ fn map_set_with_a_non_allocating_value_emits_no_rooting_calls() { // ---------------------------------------------------------------- #6971 ---- -/// `s.concat(x)` threads a bare `StringHeader*` accumulator through an SSA -/// register across every argument, and each `js_string_concat` returns a NEW -/// address — so the slot has to be written back, not just re-read. +/// `s.concat(x)` used to thread a bare `StringHeader*` accumulator through an +/// SSA register across every argument, written back into its own slot after +/// each `js_string_concat` call because every call returns a NEW address. +/// +/// #8450 replaced that iterative accumulator with a fused +/// `js_string_concat_chain` call: the receiver and every argument are each +/// rooted in their OWN slot (not one shared, repeatedly-written slot), then +/// re-read into a scratch array right before the single chain call. This +/// pins the #8450-shaped invariant directly with `assert_rooted_across` +/// rather than counting stores into a shared accumulator that no longer +/// exists. #[test] fn concat_accumulator_is_rooted_and_written_back() { let ir = ir_for( @@ -243,36 +251,35 @@ fn concat_accumulator_is_rooted_and_written_back() { ); let f = init_ir(&ir); - let slots = temp_root_slots(f); - assert!( - !slots.is_empty(), - "the concat accumulator must be rooted across an allocating argument \ - (#6971):\n{f}" - ); - let traffic = slot_traffic(f); - let written_back = slots.iter().any(|slot| { - traffic[slot] - .iter() - .filter(|e| matches!(e, SlotEvent::Store { .. })) - .count() - >= 2 + let arg = first_call_result(f, "js_object_alloc").unwrap_or_else(|| { + panic!("the concat argument must allocate, or this proves nothing:\n{f}") }); - assert!( - written_back, - "each js_string_concat yields a NEW address, so the accumulator must be \ - written BACK into its slot — otherwise the next argument's lowering \ - keeps the INPUT alive and sweeps the string under construction. Slot \ - traffic: {traffic:#?}\n{f}" - ); - let re_read = slots.iter().any(|slot| { - traffic[slot] - .iter() - .any(|e| matches!(e, SlotEvent::Load { .. })) + let slot = slot_holding(f, &arg).unwrap_or_else(|| { + panic!( + "the concat argument must be rooted across evaluation of later \ + parts (#6971/#8450):\n{f}" + ) }); + let chain_line = f + .lines() + .position(|line| line.contains("@js_string_concat_chain(")) + .expect("this test's whole point is a js_string_concat_chain call"); + // #8450 fuses the receiver + args into a scratch array passed by pointer, + // so the call's own operands are just `(ptr, i32)` — the rooted re-read + // shows up one level removed, in the `store double %v, ptr %slot` that + // fills the array. Any of those stored values deriving from a load out of + // the rooted slot proves the fused call consumes the re-read, not the + // stale producer register. + let fed_from_reread = f + .lines() + .take(chain_line) + .filter_map(|line| line.trim().strip_prefix("store double ")?.split(',').next()) + .any(|reg| derives_from_slot_load(f, reg.trim(), 6)); assert!( - re_read, - "the accumulator must be re-read after the argument (and its ToString \ - coercion, which also allocates):\n{f}" + fed_from_reread, + "the concat argument (rooted in {slot}) must be re-read before it is \ + stored into the js_string_concat_chain scratch array — otherwise the \ + array holds the stale pre-collection register (#6971/#8450):\n{f}" ); }