From ac8914f84abcba18cb10984e30373c9766568516 Mon Sep 17 00:00:00 2001 From: jun0 Date: Fri, 18 Sep 2026 05:28:27 +0900 Subject: [PATCH] =?UTF-8?q?[rustjava-adopt-javac-fixture-provenance-verifi?= =?UTF-8?q?ed-p0]=20test(fixtures):=20=EB=8B=A4=EC=84=AF=20=ED=94=BD?= =?UTF-8?q?=EC=8A=A4=EC=B2=98=EA=B0=80=20=EC=9E=AC=EB=B9=8C=EB=93=9C?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=9D=B4=EC=9C=A0?= =?UTF-8?q?=EB=8A=94=20`-g`=20=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 채택 제안 2026-09-17-javac-fixture-provenance-verified#p0 의 처분. 커밋본은 디버그 정보를 달고 컴파일됐고 스크립트는 그것 없이 재빌드하고 있었다 — javac 기본은 `-g:lines,source` 라 LocalVariableTable 이 안 나온다. `-g` 를 주면 다섯 전건 바이트 동일. ★제안이 댄 두 설명은 둘 다 틀렸다: · 다른 컴파일러 아니다 — 같은 다섯이 26.0.1·26.0.2.1 에서 똑같이 다르고 둘 다 -g 면 똑같이 동일하다. · 소스 발산 아니다 — -g 만 주면 지금 소스가 커밋 바이트를 정확히 낸다. ⇒ 제안 tradeoff 의 「재컴파일이 동작 변경이 될 수 있다」는 성립하지 않는다(재컴파일 불요 · 커밋된 .class 바이트 0 변경). 고친 한 자리: 스크립트가 --release 를 픽스처에서 읽듯 -g 도 픽스처에서 읽는다(상수풀의 LocalVariableTable 유무). 배선 전 판별력 실측 — 보유 5 · 미보유 107 · 5/5 · 오탐 0. 양방향: 정상 109 rebuilt / 109 reproduced / 0 differed ↔ 개악(-g 파생 한 줄 no-op) 104 / 5 differed (✗ 목록이 원래 다섯과 동일) · 복원 0. 잃는 것: 스크립트는 여전히 rc=1(재빌드 불가 3건 = 제안이 미해결로 적은 별 축 · 넓히지 않았다). cargo test --all 579/0/1(불변 — Rust 무접촉) · check-dod-ci-parity OK. --- REPORT.md | 11 +++ STATE.md | 7 ++ ...09-18-five-fixtures-were-built-with-g.json | 22 ++++++ ...6-09-18-five-fixtures-were-built-with-g.md | 68 +++++++++++++++++++ test-data/src/verify-javac-fixtures.sh | 24 +++++-- 5 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 docs/worklog/2026-09-18-five-fixtures-were-built-with-g.json create mode 100644 docs/worklog/2026-09-18-five-fixtures-were-built-with-g.md diff --git a/REPORT.md b/REPORT.md index 164f5301..84f45654 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,4 +1,15 @@ # REPORT +## [2026-09-18] 루트 픽스처 다섯이 재빌드되지 않는 이유 — ★**`-g` 다. 제안이 댄 두 설명은 «둘 다» 틀렸다** (rustjava-adopt-javac-fixture-provenance-verified-p0) +- 무엇을: 채택 제안 `2026-09-17-javac-fixture-provenance-verified#p0`(worklog json `adoptedProposals` 기록). ★**제품 Rust 0줄 · 커밋된 `.class` 바이트 «0 변경»** — 고친 것은 검증 스크립트 한 자리다. +- ★**답**: 커밋본은 **디버그 정보를 달고**(`-g`) 컴파일됐고 스크립트는 **그것 없이** 재빌드했다. javac 기본은 `-g:lines,source` 라 `LocalVariableTable` 이 안 나온다. 단서는 `javap -v -p` 대조(커밋본 902B 에만 `LocalVariableTable` 과 `this`·`args`·`oe`…). +- ★★**제안의 두 설명을 측정으로 반증했다**: ⒜**다른 컴파일러 아니다** — 같은 다섯이 **26.0.1 과 26.0.2.1 에서 똑같이** 다르고 둘 다 `-g` 면 **똑같이 동일**하다(두 판본 각각 직접 실행 · 26.0.1 keg 잔존) ⒝**소스 발산 아니다** — `-g` 만 주면 **지금 소스가 커밋 바이트를 정확히 낸다**. + ⇒ ★★**제안이 가장 걱정한 대가가 사라진다** — `tradeoff` 의 「재컴파일이 동작 변경이 될 수 있다」는 ★**재컴파일 자체가 불요**라 성립하지 않는다. +- ★**고친 한 자리**: 스크립트가 이미 `--release` 를 픽스처에서 읽으므로 **`-g` 도 같은 자리에서** 읽게 했다(상수풀의 `LocalVariableTable` 유무). ★**배선 전에 판별력을 쟀다** — 보유 **5** · 미보유 **107** · ★**5/5 · 오탐 0**. +- ★**양방향**: 정상 **109 rebuilt / 109 reproduced / 0 differed** ↔ ★개악(`-g` 파생 한 줄 no-op) **104 / 5 differed**(✗ 목록이 원래 다섯과 동일) · 복원 0. +- ★**잃는 것**: ⒜스크립트는 **여전히 `rc=1`** — 재빌드 불가 **3건**(형제 참조 소스 · `-sourcepath` 미사용)은 제안이 미해결로 적은 **별 축**이라 넓히지 않았다 ⒝판정이 **한 속성의 유무**에 걸린다(`-g:none` 재생성은 조용히 드리프트로 읽힌다). +- 검증: `cargo test --all` **579 passed / 0 failed / 1 ignored**(불변 — Rust 무접촉) · `check-dod-ci-parity` → **「OK 두 축 모두 대칭차 0 — 명령 6개 · toolchain 2개로 «둘 다 일치»」**. +- ★후속 추천: 재빌드 불가 3건의 컴파일 방법 결정(M) — 상세 = `docs/worklog/2026-09-18-five-fixtures-were-built-with-g.md`. + ## [2026-09-17] 「전건 single-defect」는 «측정»이 아니었다 — 판정식을 `given` 에서 파생시킨다 (rustjava-adopt-class-format-mutation-audit-p0-fix) - 무엇을: 게이트② **request-changes** 승계(PR #63 · 핀 `377d58b1`). ★**검수자 지적이 옳았다** — 고친 것은 감사 스크립트 **1파일**이고 제품 Rust 는 **0줄**이다. - ★★**급소는 한 줄이다**: `repaired` 를 **정본 인자로 다시 짓고** 있었다 ⇒ 픽스처에 둘째 결함이 무엇이 들어오든 `repaired` 와 `canonical` 이 **똑같이 버려서** 항상 같았다. diff --git a/STATE.md b/STATE.md index f5ccd586..ffd8b06b 100644 --- a/STATE.md +++ b/STATE.md @@ -7,6 +7,13 @@ (둘 다 이것보다 오래됐고 MERGEABLE/CONFLICTING 처분이 이미 걸려 있다). 겹침은 전부 **append 형 합집합**이라 해소는 기계적이다) ## 완료 +- [rustjava-adopt-javac-fixture-provenance-verified-p0] ★★**루트 픽스처 다섯이 재빌드되지 않는 이유 = `-g`.** 채택 제안 `2026-09-17-javac-fixture-provenance-verified#p0`(worklog json 기록). ★**제품 Rust 0줄 · 커밋 `.class` 바이트 0 변경.** + ★**제안이 댄 두 설명이 둘 다 틀렸다** — 컴파일러 아니다(**26.0.1·26.0.2.1 에서 같은 다섯이 같게 행동**) · 소스 발산 아니다(`-g` 면 **지금 소스가 커밋 바이트를 낸다**). + ⇒ ★**제안 `tradeoff` 의 「재컴파일이 동작 변경이 될 수 있다」가 «성립하지 않는다»** — 재컴파일이 불요다. + ★고친 한 자리: 스크립트가 `--release` 를 픽스처에서 읽듯 **`-g` 도 픽스처에서**(상수풀 `LocalVariableTable`). ★배선 전 판별력 실측 **5/5 · 오탐 0**(보유 5 · 미보유 107). + ★양방향: 정상 **109/109/0 differed** ↔ 개악(파생 한 줄 no-op) **104/5 differed**(원래 다섯과 동일). + ★**잃는 것**: 스크립트는 **여전히 rc=1**(재빌드 불가 3건 = 제안이 미해결로 적은 별 축 · 넓히지 않았다) · 판정이 한 속성 유무에 걸린다. + ★`--all` **579/0/1**(불변) · `check-dod-ci-parity` OK. - [rustjava-adopt-class-format-mutation-audit-p0-fix] ★★**판정식을 `given` 에서 파생시킨다 — 게이트② 반려 승계(PR #63).** ★**급소 한 줄**: `repaired` 를 정본 인자로 **다시 짓고** 있어 둘째 결함을 버렸다 ⇒ 「single-defect 인가」를 묻는데 **입력이 이미 single-defect** 였다(순환 · 18중 **14건**). ★처방은 발명이 아니라 **옮겨오기** — 이미 옳던 `indy` 근접실패 형태를 `add()` 한 곳으로 모아 **4족 전건**이 지나게 했고, diff --git a/docs/worklog/2026-09-18-five-fixtures-were-built-with-g.json b/docs/worklog/2026-09-18-five-fixtures-were-built-with-g.json new file mode 100644 index 00000000..3916f33d --- /dev/null +++ b/docs/worklog/2026-09-18-five-fixtures-were-built-with-g.json @@ -0,0 +1,22 @@ +{ + "date": "2026-09-18", + "taskId": "rustjava-adopt-javac-fixture-provenance-verified-p0", + "summary": "The five root fixtures that would not rebuild to their committed bytes were compiled with -g and rebuilt without it. Both explanations the proposal offered — a different compiler, or a source edited after the fixture was built — are refuted by measurement. No fixture was recompiled and no committed byte changed; verify-javac-fixtures.sh now derives -g from the fixture the same way it already derived --release, and all 109 rebuildable fixtures reproduce exactly.", + "changes": [ + "test-data/src/verify-javac-fixtures.sh: per-fixture `debug=-g` derived from the presence of LocalVariableTable in the committed class, passed to the rebuild and echoed in the per-fixture line; header comment records the measurement" + ], + "verification": [ + "before: `sh test-data/src/verify-javac-fixtures.sh test-data` reported 109 rebuilt: 104 reproduced, 5 differed — the same counts the proposal reported, and under javac 26.0.2.1 rather than the 26.0.1 it was measured on", + "after: 109 rebuilt: 109 reproduced, 0 differed", + "MUTATION, kill the one line that derives -g (`debug=-g` becomes a no-op) in the script's own call path: 109 rebuilt: 104 reproduced, 5 differed — and the five named are exactly the original five. Restored: back to 0 differed", + "the discriminator was measured before it was wired: over all of test-data, exactly 5 fixtures carry LocalVariableTable and 107 do not, and those 5 are precisely the ones that differed — 5/5 with zero false positives", + "compiler axis ruled out by hand: rebuilding the five under javac 26.0.1 (the version the proposal measured) gives differ-without-g and byte-identical-with-g, the same as 26.0.2.1", + "cargo test --all: 579 passed / 0 failed / 1 ignored, unchanged — this round touches no Rust", + "no committed .class byte changed: the only file modified under test-data/ is the script" + ], + "issues": [ + "The script still exits 1, because 3 fixtures (ConstantsReader, IncompatibleStatic, VirtualDispatchSemantics) cannot be rebuilt alone — they reference siblings, and the script deliberately omits -sourcepath so that test-data/src/Exception.java does not shadow java.lang.Exception. That is a separate axis the proposal named as unresolved, and this round did not widen into it.", + "The -g axis is now derived, so a fixture rebuilt by hand without -g and committed would be reported as drift on the next run — which is the intended reading, but it means the script's answer depends on an attribute that a future `-g:none` build would remove silently." + ], + "adoptedProposals": ["2026-09-17-javac-fixture-provenance-verified#p0"] +} diff --git a/docs/worklog/2026-09-18-five-fixtures-were-built-with-g.md b/docs/worklog/2026-09-18-five-fixtures-were-built-with-g.md new file mode 100644 index 00000000..166d769e --- /dev/null +++ b/docs/worklog/2026-09-18-five-fixtures-were-built-with-g.md @@ -0,0 +1,68 @@ +# 2026-09-18 — 다섯 픽스처는 `-g` 로 만들어졌다 (rustjava-adopt-javac-fixture-provenance-verified-p0) + +채택 제안 `2026-09-17-javac-fixture-provenance-verified#p0` — +「루트 픽스처 다섯이 왜 커밋된 바이트로 재빌드되지 않는지 알아내라」의 처분이다. +★**제품 Rust 0줄 · 커밋된 `.class` 바이트 0 변경.** + +## 답 — `-g` 다 + +커밋본은 **디버그 정보를 달고** 컴파일됐고, 검증 스크립트는 **그것 없이** 재빌드하고 있었다. +javac 의 기본은 `-g:lines,source`(`LineNumberTable`+`SourceFile`)라 `LocalVariableTable` 은 **안 나온다**. + +| 픽스처 | `-g` 없이 | `-g` | +|---|---|---| +| `MonitorSemantics` · `$CounterWorker` · `$FailingWorker` (`--release 8`) | 다름 | ★**바이트 동일** | +| `NativeMethod` (`--release 8`) | 다름 | ★**바이트 동일** | +| `OddEven` (`--release 21`) | 다름 | ★**바이트 동일** | + +처음 단서는 `javap -v -p` 대조였다 — 커밋본(902B)에만 `LocalVariableTable` 과 그 이름들 +(`this`·`args`·`oe`·`arg`·`i`)이 있고 재빌드본(709B)에는 없다. + +## ★제안의 «두 설명»은 둘 다 틀렸다 + +제안 `why` 는 「**다른 컴파일러**와 **픽스처를 만든 뒤 편집된 소스**가 둘 다 이것을 설명한다」고 적었다. +측정은 **둘 다 반증한다**: + +- **컴파일러 아니다** — 같은 다섯이 **26.0.1 과 26.0.2.1 에서 똑같이** 다르고, 둘 다 `-g` 를 주면 + **똑같이 바이트 동일**하다(두 판본을 각각 직접 돌려 확인 · 26.0.1 keg 가 남아 있어 가능했다). + ※제안은 26.0.1 에서 측정됐고 이 회차는 26.0.2.1 이 기본인 맥에서 쟀는데 **수가 같았다** — 그 자체가 첫 반증이다. +- **소스 발산 아니다** — `-g` 만 주면 **지금 소스가 커밋된 바이트를 정확히 낸다**. 소스와 픽스처는 **어긋나 있지 않다.** + +★★**그래서 제안이 가장 걱정한 대가가 «사라진다»**: 제안 `tradeoff` 는 「두 번째 경우의 처방은 재컴파일이고, +여러 시험이 픽스처 출력을 커밋된 `.txt` 와 비교하므로 재컴파일이 **동작 변경**이 될 수 있다」였다. +⇒ ★**재컴파일이 필요 없다.** 이 회차는 `.class` 를 **한 바이트도** 건드리지 않았다. + +## 고친 «한 자리» + +스크립트는 이미 **`--release` 를 픽스처 자신에서** 읽는다(「nothing external has to be consulted or kept in sync」). +★**`-g` 도 같은 자리에서 같은 방식으로** 읽게 했다 — 커밋된 클래스의 상수풀에 `LocalVariableTable` 이 있으면 `-g`. + +★**배선 «전»에 판별력을 쟀다**: `test-data` 전체에서 `LocalVariableTable` 보유는 **정확히 5개**, +미보유 **107개**이고 그 5개가 **바로 그 다섯**이다 — ★**5/5 · 오탐 0**. + +## 검증 — 양방향 + +| | 결과 | +|---|---| +| 고친 뒤(정상) | ★**109 rebuilt: 109 reproduced, 0 differed** | +| ★개악 — `-g` 파생 **한 줄**을 no-op 으로 | ★**104 reproduced, 5 differed** · ✗ 목록이 **원래 다섯과 동일** | +| 복원 | 0 differed | + +`cargo test --all` **579 passed / 0 failed / 1 ignored**(불변 — Rust 무접촉). + +## ★잃는 것 — 그리고 «안 하면 무엇이 나쁜가» + +- ★**스크립트는 여전히 `rc=1` 이다.** 재빌드 불가 **3건**(`ConstantsReader`·`IncompatibleStatic`· + `VirtualDispatchSemantics`)이 남아 있고, 그것은 제안이 **따로 미해결로 적어 둔 축**이다(형제가 필요한 소스 · + `-sourcepath` 를 일부러 안 쓴다 — `test-data/src/Exception.java` 가 `java.lang.Exception` 을 가리기 때문). + ⇒ ★**드리프트 질문은 답했고, 종료 코드는 아직 0 이 아니다.** 넓히지 않았다. +- ★**파생 축이 하나 늘었다** — 누군가 `-g:none` 으로 다시 만들어 커밋하면 그 속성이 **조용히 사라지고** + 스크립트는 그것을 「드리프트」로 읽는다. 의도한 읽기이지만, **판정이 한 속성의 유무에 걸린다**는 뜻이기도 하다. +- ★**안 하면**: 스크립트가 **영구히 거짓 드리프트 5건**을 보고한다. 그러면 ⑴「재컴파일해서 맞추자」는 + 제안의 `tradeoff` 대로 **정말로 동작 변경 위험**을 감수하게 되거나 ⑵다음 사람이 **같은 조사를 처음부터 다시** 한다. + ★그리고 이 스크립트는 「픽스처를 재생성했을 때 사람이 돌리는 검사」라 ★**거짓 경보가 그 유일한 용도를 무디게 한다.** + +## 후속 + +- ★**재빌드 불가 3건** — 형제를 참조하는 소스를 어떻게 컴파일할 것인가(`-sourcepath` 없이). + ★제안이 「이걸 정하지 않으면 조사가 완결되지 않는다」고 적은 그 축이고, **여기서 넓히지 않았다.** diff --git a/test-data/src/verify-javac-fixtures.sh b/test-data/src/verify-javac-fixtures.sh index 42dbd6ea..9a02e3b5 100755 --- a/test-data/src/verify-javac-fixtures.sh +++ b/test-data/src/verify-javac-fixtures.sh @@ -11,6 +11,14 @@ # `javac 26.0.1 --release 21`, and five sampled root fixtures reproduce under the --release their # recorded major version implies (65->21, 66->22, 70->26). # +# "flags" in that sentence is not decoration. Measured 2026-09-18 over all of test-data with javac +# 26.0.2.1: five fixtures rebuilt to different bytes -- MonitorSemantics and its two named inner +# classes, NativeMethod, OddEven -- and all five reproduce exactly once -g is passed. They were +# built with debug info; the rebuild was not. Neither a different compiler nor a source edited +# after the fact was involved, which were the two explanations on offer: the same five differ under +# 26.0.1 and 26.0.2.1 alike, and the sources are in step with the committed bytes. So the -g is +# derived per fixture below, from the fixture, exactly as --release already was. +# # It is *not* wired into CI, and that is deliberate rather than an oversight: there is no JDK in # .github/workflows/rust.yml and none on PATH here, so a test that needed one would either fail # everywhere or skip everywhere. This is the manual check you run when you regenerate a fixture. @@ -63,23 +71,31 @@ for class in "$DIR"/*.class; do major=$(od -An -tu1 -j6 -N2 "$class" | tr -s ' \n' ' ' | awk '{ print $1 * 256 + $2 }') release=$((major - 44)) + # ...and its own debug attributes say which -g it was built with, read the same way and for the + # same reason: nothing external to keep in sync. javac's default is -g:lines,source, which emits + # LineNumberTable but not LocalVariableTable, so the presence of that attribute name in the pool + # is what separates a `-g` build from a default one. Measured 2026-09-18 over test-data: exactly + # the five fixtures that did not reproduce carry it, and none of the other 107 do. + debug= + if LC_ALL=C grep -aq LocalVariableTable "$class"; then debug=-g; fi + # Deliberately no -sourcepath: test-data/src holds Exception.java, Array.java, Method.java and # friends, so putting it on the source path makes javac resolve `Exception` to the fixture # rather than java.lang.Exception. Measured — it turns clean rebuilds into type errors. The # cost is that a source needing a sibling cannot be rebuilt alone, which is reported as # "could not be rebuilt" rather than as drift. rm -rf "$work/out" && mkdir -p "$work/out" - if ! "$JAVAC" --release "$release" -d "$work/out" "$source" 2>"$work/err"; then - echo " ? $base: could not rebuild at --release $release: $(grep -m1 error "$work/err" || head -1 "$work/err")" >&2 + if ! "$JAVAC" ${debug:+"$debug"} --release "$release" -d "$work/out" "$source" 2>"$work/err"; then + echo " ? $base: could not rebuild at --release $release${debug:+ $debug}: $(grep -m1 error "$work/err" || head -1 "$work/err")" >&2 unbuildable=$((unbuildable + 1)) continue fi checked=$((checked + 1)) if cmp -s "$work/out/$base.class" "$class"; then - echo " ✓ $base (--release $release)" + echo " ✓ $base (--release $release${debug:+ $debug})" else - echo " ✗ $base (--release $release): rebuilt bytes differ from the committed fixture" >&2 + echo " ✗ $base (--release $release${debug:+ $debug}): rebuilt bytes differ from the committed fixture" >&2 differed=$((differed + 1)) fi done