Skip to content

Fix IntervalValue equality/bitshift stubs, expose CopyKind, preserve AbstractState subclass identity - #71

Open
yuleisui wants to merge 9 commits into
mainfrom
fix/ae-python-binding-mismatches
Open

Fix IntervalValue equality/bitshift stubs, expose CopyKind, preserve AbstractState subclass identity#71
yuleisui wants to merge 9 commits into
mainfrom
fix/ae-python-binding-mismatches

Conversation

@yuleisui

Copy link
Copy Markdown
Collaborator

Summary

Fixes several mismatches between the Python bindings/stubs and the actual C++ implementation/behavior in pysvf:

  1. IntervalValue.__eq__/__ne__ returned bool, not IntervalValue. The binding called equals() (bool), while the stub already documented (and C++'s operator==/operator!= actually implement) an abstract-domain result ([1,1]/[0,0]/[0,1]). Rewired the bindings to call operator==/operator!=, and added __bool__ (true only for a definite [1,1]) so common if a == b: patterns keep behaving correctly. Also fixed equals()'s stub, which was mistakenly typed as returning IntervalValue instead of bool.

  2. CopyKind enum was missing from the bindings, so callers had to guess raw integer values to detect zext/trunc/etc. copy kinds. Added a CopyKind(IntEnum) to pysvf/enums.py mirroring CopyStmt::CopyKind, following the same convention already used for Predicate/OpCode.

  3. AbstractState.clone()/widening()/narrowing() always constructed a plain AbstractState in C++, silently discarding any Python subclass (e.g. a class AEState(AbstractState): ... pattern, as used in the SVF course material). Reconstruct an instance of the caller's actual runtime type instead.

  4. isCmpBranchFeasible/isSwitchBranchFeasible don't exist in the C++ library (the real AbstractInterpretation methods are private) and the stub had an incorrect argument count (missing the leading SVFIR/pag argument) and wasn't marked @staticmethod, despite the binding being a def_static. Documented these as pysvf-only helpers in the stub and fixed the signatures to match the real 4-arg binding.

  5. IntervalValue.__lshift__/__rshift__ stubs were typed as taking int for the RHS, but the binding accepts another IntervalValue (py::self << py::self).

Validation

  • Rebuilt the extension (against a matching svf-lib/LLVM 21 toolchain) and ran the SVF course's Assignment-3 material (Software-Security-Analysis / Software-Security-Analysis-Sol) against it.
  • Full 120-case .ll regression corpus passes identically before/after these changes (120/120, 69/69 assertions verified, 0 regressions vs. a pre-fix build using the same SVF/LLVM version).
  • Directly exercised the new equality/__bool__, CopyKind, and subclass-preservation behavior against real compiled IR and confirmed correctness.
  • stubtest passes with the existing stubtest_allowlist.txt.

No behavioral changes were made to isCmpBranchFeasible/isSwitchBranchFeasible's underlying logic — only their stub signatures were corrected.

…AbstractState subclass identity

Fixes several mismatches between the Python bindings/stubs and the
actual C++ implementation/behavior:

- IntervalValue.__eq__/__ne__ were wired to equals() (bool), while the
  stub documented (and C++'s operator==/!= actually implement) an
  abstract-domain IntervalValue result ([1,1]/[0,0]/[0,1]). Rewire the
  bindings to call operator==/operator!=, and add __bool__ (true only
  for a definite [1,1]) so common `if a == b:` patterns keep behaving
  correctly. Also fix equals()'s stub, which was mistakenly typed as
  returning IntervalValue instead of bool.

- Add a CopyKind IntEnum (pysvf/enums.py) mirroring
  CopyStmt::CopyKind, following the same convention already used for
  Predicate/OpCode, so zext/trunc/etc. copy kinds don't need to be
  guessed as magic numbers.

- AbstractState.clone()/widening()/narrowing() always constructed a
  plain AbstractState in C++, silently discarding any Python subclass
  (e.g. a `class AEState(AbstractState): ...` pattern). Reconstruct an
  instance of the caller's actual runtime type instead.

- isCmpBranchFeasible/isSwitchBranchFeasible are pysvf-only static
  helpers (the real AbstractInterpretation methods are private in
  SVF); document this in their stubs, mark them @staticmethod, and fix
  their argument lists to match the real 4-arg (svfir, stmt, succ,
  abstract_state) binding.

- Fix IntervalValue.__lshift__/__rshift__ stubs: the binding accepts
  another IntervalValue as the RHS (py::self << py::self), not int.

Validated by rebuilding the extension and running it against the
Assignment-3 course material (Software-Security-Analysis[-Sol]): the
full 120-case regression corpus passes identically before/after these
changes (120/120, 69/69 assertions verified, 0 regressions), and the
new equality/CopyKind/branch-feasibility behavior was independently
exercised against real compiled IR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 12, 2026 05:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aligns pysvf’s Python bindings and type stubs with SVF’s intended abstract-domain semantics and the actual pybind-exposed APIs, especially around IntervalValue equality/bitshifts, AbstractState subclass preservation, and feasibility helper signatures. It also exposes CopyStmt copy kinds to Python callers via a new enum.

Changes:

  • Rewire IntervalValue equality to return an abstract-domain IntervalValue result and add __bool__ to make common if a == b: patterns work sensibly.
  • Add CopyKind(IntEnum) mirroring SVF::CopyStmt::CopyKind and export it from pysvf.__init__.
  • Preserve Python subclass identity for AbstractState.clone()/widening()/narrowing() and correct stub signatures for feasibility helpers and shift operators.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
pysvf/pysvf.pyi Updates stubs for IntervalValue shifts/equality helpers and corrects AbstractState static helper signatures + documentation.
pysvf/enums.py Adds CopyKind enum for CopyStmt copy-kind inspection.
pysvf/init.py Re-exports CopyKind from the package top-level.
pybind/AE.cpp Adjusts IntervalValue equality/truthiness semantics and preserves AbstractState Python subclass identity in clone/widen/narrow.
Suppressed comments (1)

pybind/AE.cpp:135

  • __bool__ currently gates on is_numeral() but then calls getIntNumeral(). If is_numeral() can also be true for real numerals (suggested by the presence of is_real()/getRealNumeral()), this can throw or assert for non-integer numerals.

Using is_int() avoids calling getIntNumeral() on non-integer values while preserving the intended [1,1] truthiness semantics.

        .def("__bool__", [](const IntervalValue &self) {
            return self.is_numeral() && self.getIntNumeral() == 1;
        })

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pybind/AE.cpp Outdated
Comment on lines +122 to +126
.def("__eq__", [](const IntervalValue &self, const IntervalValue &other) {
return self.equals(other);
return self.operator==(other);
})
.def("__ne__", [](const IntervalValue &self, const IntervalValue &other) {
return !self.equals(other);
return self.operator!=(other);
Comment thread pysvf/pysvf.pyi Outdated
def isCmpBranchFeasible(self, cmp: 'CmpStmt', succ: int, abstract_state: AbstractState) -> bool: ...
def isSwitchBranchFeasible(self, switch_var: SVFVar, succ: int, abstract_state: AbstractState) -> bool: ...
@staticmethod
def isCmpBranchFeasible(pag: 'SVFIR', cmpStmt: 'CmpStmt', succ: int, as_: 'AbstractState') -> bool: ...
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.

3 participants