Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelog.d/9971-segview-build-cache-input.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
### Fixed

- Register the segment-view lowering switch as a build-cache input so changing
`PERRY_SEGVIEW` cannot reuse a binary emitted under the opposite setting.
`PERRY_SEGVIEW_DIAG` remains diagnostic-only and is explicitly excluded from
the cache key.
19 changes: 6 additions & 13 deletions crates/perry/src/commands/compile/build_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ const BUILD_CACHE_ENV_VARS: &[&str] = &[
"PERRY_PTR_NUMARRAY_LOCALS",
"PERRY_PTR_SHAPE_LOCALS",
"PERRY_PTR_SHAPE_THIS",
// #9893: selects the segment-view lowering, which rewrites qualifying
// for-of loops to call the `js_segments_view_*` runtime entry points.
"PERRY_SEGVIEW",
"PERRY_SPECIALIZED_ABI",
"PERRY_SPECIALIZED_ABI_MAX",
"PERRY_SPEC_PRESERVE_NONE",
Expand Down Expand Up @@ -232,6 +235,9 @@ const BUILD_CACHE_ENV_EXCLUSIONS: &[&str] = &[
"PERRY_PACKED_LOOP_TRACE",
// Entry outlining report output is observational only.
"PERRY_OUTLINE_ENTRY_REPORT",
// Segment-view diagnostics only scan the final HIR and print counters;
// their checks inside the rewrite guard `eprintln!` calls only.
"PERRY_SEGVIEW_DIAG",
// Only read on an already-fatal dialect-construction failure (a unit that
// never parses); it writes a diagnostic IR dump to `<dir>/<name>.ll` for
// triage and cannot affect the bytes of any build that actually succeeds.
Expand Down Expand Up @@ -866,19 +872,6 @@ fn eligibility(args: &CompileArgs, project_root: &Path) -> Result<(), String> {
if std::env::var("PERRY_SEGVIEW_DIAG").is_ok() {
return Err("segview-diag".to_string());
}
// #9843: `PERRY_SEGVIEW` is NOT a diagnostic — it changes the emitted
// code. It is not part of the build-cache fingerprint or any object-cache
// key, so without this a cached build can hand back a binary compiled with
// the OTHER setting: compile a file with the tier on, compile it again
// with the tier off, and the second can be served from the first. The
// A/B rig's whole shape is "one compiler binary, two compiles of one
// source differing only in this variable", which is exactly the collision.
// Excluded rather than keyed because the tier is experimental and default
// OFF; a cache key is the right fix when it ships on, and then a stale
// entry cannot silently become the measurement.
if std::env::var("PERRY_SEGVIEW").is_ok() {
return Err("segview-lowering".to_string());
}
if args.verify_native_regions || args.emit_attest || args.emit_sandbox {
return Err("sidecar-or-verify".to_string());
}
Expand Down
41 changes: 41 additions & 0 deletions crates/perry/tests/native_link_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,44 @@ fn native_compile_skips_link_on_identical_second_build() {
assert_codegen_cache(&missing_output, 2, 0, 2, 0, 0);
assert!(output.exists());
}

#[test]
fn segment_view_switch_misses_build_and_object_caches() {
let dir = tempfile::tempdir().expect("tempdir");
let project = dir.path();
let output = project.join("app");
let entry = project.join("main.ts");
fs::write(
project.join("package.json"),
"{\"name\":\"segview-cache-test\"}\n",
)
.unwrap();
fs::write(
&entry,
r#"const segmenter = new Intl.Segmenter("en");
for (const { segment } of segmenter.segment("ab")) {
console.log(segment);
}
"#,
)
.unwrap();

let disabled = compile_json_with_env(project, &entry, &output, &[("PERRY_SEGVIEW", "0")]);
assert_linked(&disabled);
assert_build_cache_miss(&disabled, "manifest-missing");
assert_codegen_cache(&disabled, 0, 1, 0, 1, 0);
assert_eq!(run_binary(&output), "a\nb\n");

// This must reach codegen and produce a distinct object. Sabotage:
// removing PERRY_SEGVIEW from BUILD_CACHE_ENV_VARS makes it an erroneous
// whole-build cache hit, so the assertions below fail before codegen.
let enabled = compile_json_with_env(project, &entry, &output, &[("PERRY_SEGVIEW", "1")]);
assert_linked(&enabled);
assert_build_cache_miss(&enabled, "env");
assert_codegen_cache(&enabled, 0, 1, 0, 1, 0);
assert_eq!(run_binary(&output), "a\nb\n");

let enabled_warm = compile_json_with_env(project, &entry, &output, &[("PERRY_SEGVIEW", "1")]);
assert_skipped(&enabled_warm);
assert_build_cache_hit(&enabled_warm);
}
Loading