You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A C++-bridge change is validated against a stale archive: cinterop declares only the .def as an input, and nativeTest goes UP-TO-DATE having run zero tests #249
A C++-bridge change is validated against a stale archive, and nativeTest reports UP-TO-DATE having run zero tests
Category: bug — a control that reports success while measuring nothing (Refs #231's family), in the build wiring Found by: the #248 author, when a mutation came back GREEN that should have been RED. Confirmed and deepened by the #248 reviewer.
The defect
compiler/gradle/.../KPlusPlusCompilerGradlePlugin.kt:828-830 declares exactly one cinterop input:
But neither lib<module>.a nor the generated <module>.h is a declared input, and the .def's
content is deterministic from the module name — so it never changes. The klib packages the archive
into itself, so once built it is frozen.
Consequence: an edit to krapper/include/clang_slice.h never reaches the test binary. Measured
during #248:
edit clang_slice.h, build WITHOUT purging:
kplusplusSync re-executed
cinteropKplusplusNative UP-TO-DATE
nativeTest UP-TO-DATE <-- ZERO TESTS RAN
BUILD SUCCESSFUL
timestamps: regenerated lib*.a 01:53:21 vs the klib's embedded copy 01:44:42
After purging krapper/build/classes/kotlin/native/main/cinterop, the same mutation turns exactly the
three real-clang tests RED.
It is symmetric, which is the dangerous part: a restore does not land either. So a developer can
break the bridge, see green, revert, and see the same green — with neither state actually exercised.
Why this went unnoticed, and how far back it reaches
Every clang_slice.h change in this repo's history may have been validated locally against code that
was not running. CI is unaffected — it is a cold checkout with no cache to be stale — which is exactly
why nothing surfaced. Local green and CI green looked the same, and only CI was telling the truth.
Note the near-miss in the code itself: the comment at :831-833 says the task was wired to regenerate
via kplusplusSync"so it can't process a stale def (issue #16)."The stale-.def hazard was
found and fixed; the stale-.a hazard the .def points at was not. A fix that addressed the pointer
and not the pointee.
Proposed
Declare the real inputs — lib<module>.a and the generated <module>.h — on the cinterop task,
or hash them into the .def so its content changes when they do.
Add the M4 mutation as a regression guard: edit clang_slice.h, build without purging, and
require the change to reach the test binary. Watch it go RED against today's wiring first — that
demonstration is the deliverable, not the fix.
Related, and worth folding in or splitting deliberately
The #248 reviewer noted that #224's own proposal (3) was never implemented: a test that catches an unread KrapperConfig field. That is the generalisation of the defect Refs #224 fixed — --strict-diagnostics sat declared-and-plumbed-but-read-by-nothing, and nothing would have said so.
Same shape as this issue one layer up: a declaration with no consumer, invisible to every green signal.
Acceptance bar
The same one this repo has been holding all week: watch it go RED before accepting the fix. A build
that cannot notice a changed C++ bridge, and a build that can, are indistinguishable while everything
is green — which is precisely how this survived.
A C++-bridge change is validated against a stale archive, and
nativeTestreports UP-TO-DATE having run zero testsCategory: bug — a control that reports success while measuring nothing (Refs #231's family), in the build wiring
Found by: the #248 author, when a mutation came back GREEN that should have been RED. Confirmed and deepened by the #248 reviewer.
The defect
compiler/gradle/.../KPlusPlusCompilerGradlePlugin.kt:828-830declares exactly one cinterop input:The generated
.defnames the compiled wrapper archive (:212-213):But neither
lib<module>.anor the generated<module>.his a declared input, and the.def'scontent is deterministic from the module name — so it never changes. The klib packages the archive
into itself, so once built it is frozen.
Consequence: an edit to
krapper/include/clang_slice.hnever reaches the test binary. Measuredduring #248:
After purging
krapper/build/classes/kotlin/native/main/cinterop, the same mutation turns exactly thethree real-clang tests RED.
It is symmetric, which is the dangerous part: a restore does not land either. So a developer can
break the bridge, see green, revert, and see the same green — with neither state actually exercised.
Why this went unnoticed, and how far back it reaches
Every
clang_slice.hchange in this repo's history may have been validated locally against code thatwas not running. CI is unaffected — it is a cold checkout with no cache to be stale — which is exactly
why nothing surfaced. Local green and CI green looked the same, and only CI was telling the truth.
Note the near-miss in the code itself: the comment at
:831-833says the task was wired to regeneratevia
kplusplusSync"so it can't process a stale def (issue #16)." The stale-.defhazard wasfound and fixed; the stale-
.ahazard the.defpoints at was not. A fix that addressed the pointerand not the pointee.
Proposed
lib<module>.aand the generated<module>.h— on the cinterop task,or hash them into the
.defso its content changes when they do.clang_slice.h, build without purging, andrequire the change to reach the test binary. Watch it go RED against today's wiring first — that
demonstration is the deliverable, not the fix.
nativeTestreporting UP-TO-DATE with zero tests executed deserves its ownassertion. The CI gates added by Refs ci: gate on tests EXECUTED, not tests reported, in all five test-evidence gates #239/fix(ci): run each gate's whole arithmetic, not just its subtraction (#240) #242/fix(ci): assert the gate's VERDICT and its
errorsterm, not just the number (#243) #244 catch "reported but not executed" in theworkflows; this is the same failure at the Gradle level, where nothing checks it.
Related, and worth folding in or splitting deliberately
The #248 reviewer noted that #224's own proposal (3) was never implemented: a test that catches an
unread
KrapperConfigfield. That is the generalisation of the defect Refs #224 fixed —--strict-diagnosticssat declared-and-plumbed-but-read-by-nothing, and nothing would have said so.Same shape as this issue one layer up: a declaration with no consumer, invisible to every green signal.
Acceptance bar
The same one this repo has been holding all week: watch it go RED before accepting the fix. A build
that cannot notice a changed C++ bridge, and a build that can, are indistinguishable while everything
is green — which is precisely how this survived.
Refs #248, Refs #224, Refs #231, Refs #16.