Skip to content

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

Description

@monkopedia-coder

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:

compilation.cinterops.create("kplusplus") { interop ->
    interop.definitionFile.set(krappedDef)
}

The generated .def names the compiled wrapper archive (:212-213):

staticLibraries = lib<module>.a
libraryPaths = <krappedDir>

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

  1. Declare the real inputslib<module>.a and the generated <module>.h — on the cinterop task,
    or hash them into the .def so its content changes when they do.
  2. 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.
  3. Consider whether nativeTest reporting UP-TO-DATE with zero tests executed deserves its own
    assertion. 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 errors term, not just the number (#243) #244 catch "reported but not executed" in the
    workflows; 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 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.

Refs #248, Refs #224, Refs #231, Refs #16.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-workableClear, scoped, no user-judgment needed; triage dispatches work_on_issuebugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions