fix(verilog): link a module instantiation to the local definition, not a phantom - #3675
rajatnagda45 wants to merge 1 commit into
Conversation
…t a phantom
A module instantiation minted its target id with a bare, unscoped
_make_id(name), while a module definition uses _make_id(stem, name). The
two never matched, so instantiating a module defined in the same file:
module counter(...); endmodule
module top;
counter u1(...);
endmodule
split `counter` into a real definition node and a second, sourced phantom
that carried the `instantiates` edge. `top`'s dependency on the module you
actually defined was invisible, and because the phantom was sourced it also
baked this file's path into the id and blocked the corpus-level rewire that
would otherwise collapse it (the Graphify-Labs#1402 duplicate shape the other
extractors guard against).
Pre-scan module names so a same-file instantiation (Verilog allows
forward/any-order instantiation) resolves to the definition id. A module
defined in another file becomes a SOURCELESS stub instead, so the
corpus-level rewire collapses it onto the real definition rather than a
per-file duplicate — mirroring the Go extractor's ensure_named_node.
Adds regression coverage for both the local-definition link and the
cross-file sourceless stub.
|
Spotted a phantom-duplicate module node here: instantiations minted a bare-id target while definitions are stem-scoped, so a module got split into a real def + an empty instantiation target (and the sourced stub blocked the rewire, the #1402 shape). Handled it the way the Go extractor does cross-file stubs. @safishamsi curious whether the sourceless-stub approach is what you'd want for the cross-file case. |
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Fixes Verilog module instantiations that never linked to their target definition: extract_verilog now pre-scans all module_declaration names so an instantiation of a same-file module resolves to that module's scoped definition id (_make_id(stem, name)) instead of minting a bare-id phantom duplicate. Cross-file targets emit a sourceless stub (empty source_file) so the corpus-level rewire can later collapse it onto the real definition, rather than baking this file's path into the id and blocking the rewire. Adds two tests covering the local-resolution and sourceless-stub paths.
No blocking issues surfaced. 2 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 585 functions depend on the 585 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract_verilog()— 12 callers, 7 callees - new:
_augment_systemverilog_semantics()— 1 callers, 7 callees
Verification — 585 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 585 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
1 of 286 test file(s) selected (0%) via static blast radius.
tests/test_languages.py— impact, changed-test
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Could not verify: Could not verify extract\_verilog.
The verifier did not have enough to check extract\_verilog, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: no capturable inputs from the test suite; property tier: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 2 more finding(s) on lines outside this diff (see the check run).
Problem
A Verilog/SystemVerilog module instantiation minted its target id with a bare, unscoped
_make_id(name), while a module definition uses_make_id(stem, name). The two ids never matched, so instantiating a module defined in the same file:split
counterinto two nodes:t_counter— the real definition (with a body, ports, everything), andcounter— a bare-id phantom that carried theinstantiatesedge.So
top's dependency on the module you actually defined pointed at an empty duplicate, and the real module looked like nothing ever instantiated it — exactly the wrong picture for a hardware design, where the module hierarchy is the architecture.Worse, the phantom was sourced (it got this file's path + a line number), which bakes the file path into the id and blocks the corpus-level rewire that normally collapses cross-file reference stubs onto their real definitions — the same #1402 phantom-duplicate shape the other extractors deliberately avoid.
Fix
_make_id(stem, name)). Verilog allows instantiating a module declared later in the file / in any order across the compilation unit, so a forward scan is required rather than relying on declaration order.source_file/source_location), so the corpus-level rewire collapses it onto the real definition instead of leaving one duplicate per instantiating file. This mirrors the Go extractor'sensure_named_node.Test
Two new cases in
tests/test_languages.py, both failing before and passing after:test_systemverilog_instantiation_resolves_to_local_module—top→leafinsample.svlinks to the single, sourcedleafdefinition, no duplicate;test_systemverilog_cross_file_instantiation_is_sourceless_stub— an instantiation of a module defined elsewhere emits a sourceless stub target.All 407 language-extractor tests pass.