Skip to content

Record derived source expressions in counterexamples - #859

Merged
shaobo-he merged 2 commits into
developfrom
feature/source-expression-cex-recording
Aug 28, 2026
Merged

Record derived source expressions in counterexamples#859
shaobo-he merged 2 commits into
developfrom
feature/source-expression-cex-recording

Conversation

@shaobo-he

Copy link
Copy Markdown
Contributor

Summary

  • Track source expressions from LLVM debug metadata through loads, GEPs, and pointer casts.
  • Emit boogie_si recorder calls for derived scalar lvalues such as x->p and nested member access.
  • Add a Corral regression covering the generated Boogie and counterexample output.

Testing

  • cmake --build build -j2
  • source_expr_pointer_fail.c with Corral: expected error and x->p = 42 in the counterexample
  • nested_struct2.c with Corral: verified
  • git diff --cached --check

shaobo-he and others added 2 commits August 27, 2026 22:10
The n-th LLVM struct element is not the n-th DW_TAG_member, so naming a
field by its ordinal position reported the wrong member whenever the two
orderings diverged: bitfields share a storage unit, clang inserts padding,
and a C++ base class occupies a field without being a member. A store to
'b->after' was recorded as 'b->f2', and 'p->d0' as 'p->d1' -- a wrong name
in a counterexample is worse than no name at all.

Look the member up by the byte offset of the field being indexed instead,
and decline to name a bitfield, a static or artificial member, an ambiguous
match, or a union, where a field index cannot pick a member out.

Fix three more ways a name could describe the wrong object:

  - A pointer value owes a dereference that only the member branch spent,
    so '(*pa)[2]' came out as 'pa[2]', a different address. Spend it in the
    subscript branch too.
  - Subscripting the address of a non-array named 'x[1]' for a scalar x.
    Require the debug type to really be an array.
  - A cast that changes the type no longer describes the same object, so
    neither it nor the stripPointerCastsAndAliases fallback should carry a
    source expression across one.

Consume one DWARF array dimension per subscript, since DWARF folds every
dimension into a single node; multi-dimensional element expressions were
dropped entirely before and now resolve.

Restrict the recorded value to scalars -- vectors reached the recorder
through the vector load and store paths -- and drop the parameter name
shadowing llvm::Value.

Move the test into its own folder pinned to Corral: source expressions are
only rendered into a counterexample by /printDataValues, so @checkout could
never match under Boogie and the c/data job went red. Add regressions for
the bitfield, cast, and array cases; the absence assertions use awk rather
than grep -v, which exits 0 whenever any line differs and so asserts
nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shaobo-he
shaobo-he merged commit d9ceada into develop Aug 28, 2026
60 checks passed
@shaobo-he
shaobo-he deleted the feature/source-expression-cex-recording branch August 28, 2026 19:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant