From 286afbf36162db3f58ea5c0a47c1f35e7b50d5fa Mon Sep 17 00:00:00 2001 From: hanyuone Date: Wed, 12 Aug 2026 18:47:05 +1000 Subject: [PATCH 1/3] fix: remove incorrect copilot changes for equality, widening/narrowing/clone --- pybind/AE.cpp | 48 +++++++----------------------------------------- pysvf/pysvf.pyi | 4 ---- 2 files changed, 7 insertions(+), 45 deletions(-) diff --git a/pybind/AE.cpp b/pybind/AE.cpp index 34ebc02..dfd7bc3 100644 --- a/pybind/AE.cpp +++ b/pybind/AE.cpp @@ -113,27 +113,12 @@ void bind_abstract_state(py::module& m) { return new IntervalValue(to_bounded_int(lb), to_bounded_int(ub)); }), py::arg("lb"), py::arg("ub")) - - // Equality - // C++'s operator== / operator!= return an IntervalValue (abstract "boolean" domain: - // [1,1] definitely true, [0,0] definitely false, [0,1]/top/bottom ambiguous). Mirror - // that here so Python's `==`/`!=` behave the same as C++'s `==`/`!=`. `equals()` - // remains the separate, real bool-returning concrete-equality check. .def("__eq__", [](const IntervalValue &self, const IntervalValue &other) { - return self.operator==(other); + return self.equals(other); }) .def("__ne__", [](const IntervalValue &self, const IntervalValue &other) { - return self.operator!=(other); - }) - // Truthiness: only a definite [1,1] result is truthy, so common patterns like - // `if a == b:` continue to behave sensibly even though `==` now returns an - // IntervalValue rather than a bool. Ambiguous/top/bottom/other-numeral results - // are falsy; callers needing three-way logic should inspect the IntervalValue - // directly (e.g. via is_numeral()/getIntNumeral() or eq_interval()). - .def("__bool__", [](const IntervalValue &self) { - return self.is_numeral() && self.getIntNumeral() == 1; + return !self.equals(other); }) - .def("clone", [](const IntervalValue &self) { return std::make_unique(self); }, py::return_value_policy::move) @@ -362,23 +347,8 @@ void bind_abstract_state(py::module& m) { // Abstract operations .def("joinWith", &AbstractState::joinWith, py::arg("other")) .def("meetWith", &AbstractState::meetWith, py::arg("other")) - // `widening`/`narrowing` construct a brand-new AbstractState in C++, so a naive - // binding would silently downgrade any Python subclass (e.g. `class AEState - // (AbstractState): ...`) back to a plain AbstractState. Reconstruct an instance of - // the caller's actual runtime type (assumes a no-arg constructor, true for - // subclasses that don't override __init__) and copy the computed state into it. - .def("widening", [](py::object self, const AbstractState& other) -> py::object { - AbstractState result = py::cast(self).widening(other); - py::object new_obj = self.attr("__class__")(); - py::cast(new_obj) = result; - return new_obj; - }, py::arg("other")) - .def("narrowing", [](py::object self, const AbstractState& other) -> py::object { - AbstractState result = py::cast(self).narrowing(other); - py::object new_obj = self.attr("__class__")(); - py::cast(new_obj) = result; - return new_obj; - }, py::arg("other")) + .def("widening", &AbstractState::widening, py::arg("other")) + .def("narrowing", &AbstractState::narrowing, py::arg("other")) .def("getIDFromAddr", &AbstractState::getIDFromAddr, py::arg("addr")) // Static utilities for address handling @@ -419,13 +389,9 @@ void bind_abstract_state(py::module& m) { .def("getVarToVal", &AbstractState::getVarToVal, py::return_value_policy::reference) .def("getLocToVal", &AbstractState::getLocToVal, py::return_value_policy::reference) .def("printAbstractState", &AbstractState::printAbstractState) - .def("clone", [](py::object self) -> py::object { - // See widening/narrowing above: preserve the caller's actual Python - // (sub)class rather than always returning a plain AbstractState. - py::object new_obj = self.attr("__class__")(); - py::cast(new_obj) = py::cast(self); - return new_obj; - }) + .def("clone", [](const AbstractState &self) { + return std::make_unique(self); // clone + }, py::return_value_policy::move) .def("bottom", &AbstractState::bottom) .def("top", &AbstractState::top) .def("inVarToValTable", &AbstractState::inVarToValTable, py::arg("var_id")) diff --git a/pysvf/pysvf.pyi b/pysvf/pysvf.pyi index d429991..f4c965a 100644 --- a/pysvf/pysvf.pyi +++ b/pysvf/pysvf.pyi @@ -1973,11 +1973,7 @@ class AbstractState: def joinWith(self, other: 'AbstractState') -> None: ... def meetWith(self, other: 'AbstractState') -> None: ... def widening(self, other: 'AbstractState') -> 'AbstractState': ... - """Returns an instance of type(self) (subclasses are preserved), not necessarily - a plain AbstractState.""" def narrowing(self, other: 'AbstractState') -> 'AbstractState': ... - """Returns an instance of type(self) (subclasses are preserved), not necessarily - a plain AbstractState.""" def bottom(self) -> None: ... def getIDFromAddr(self, addr: int) -> int: ... def top(self) -> None: ... From 9eab11b79d2eb49c8ab5c32f9ad5a98276075279 Mon Sep 17 00:00:00 2001 From: hanyuone Date: Wed, 12 Aug 2026 18:48:28 +1000 Subject: [PATCH 2/3] --amend --- pysvf/pysvf.pyi | 2 -- 1 file changed, 2 deletions(-) diff --git a/pysvf/pysvf.pyi b/pysvf/pysvf.pyi index f4c965a..9f4f939 100644 --- a/pysvf/pysvf.pyi +++ b/pysvf/pysvf.pyi @@ -2006,8 +2006,6 @@ class AbstractState: def clear(self) -> None: ... def clone(self) -> 'AbstractState': ... - """Returns an instance of type(self) (subclasses are preserved), not necessarily - a plain AbstractState.""" def getLocToVal(self) -> dict: ... def getVarToVal(self) -> dict: ... def printAbstractState(self) -> None: ... From eb0806effff275b5a232e9d3d50cfd63177f43f4 Mon Sep 17 00:00:00 2001 From: hanyuone Date: Wed, 12 Aug 2026 18:49:36 +1000 Subject: [PATCH 3/3] fix: remove incorrect copilot changes for isCmpBranchFeasible, isSwitchBranchFeasible --- pysvf/pysvf.pyi | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pysvf/pysvf.pyi b/pysvf/pysvf.pyi index 9f4f939..ed1d036 100644 --- a/pysvf/pysvf.pyi +++ b/pysvf/pysvf.pyi @@ -1981,16 +1981,6 @@ class AbstractState: def isVirtualMemAddress(val: int) -> bool: ... @staticmethod def getVirtualMemAddress(idx: int) -> int: ... - @staticmethod - def isCmpBranchFeasible(pag: 'SVFIR', cmpStmt: 'CmpStmt', succ: int, as_: 'AbstractState') -> bool: ... - """Not a real SVF C++ AbstractState/AbstractInterpretation API: this is a pysvf-only - static helper that reimplements AbstractInterpretation::isCmpBranchFeasible's logic - in the Python bindings, since that method is private in the C++ library.""" - @staticmethod - def isSwitchBranchFeasible(svfir: 'SVFIR', var: SVFVar, succ: int, as_: 'AbstractState') -> bool: ... - """Not a real SVF C++ AbstractState/AbstractInterpretation API: this is a pysvf-only - static helper that reimplements AbstractInterpretation::isSwitchBranchFeasible's logic - in the Python bindings, since that method is private in the C++ library.""" def inVarToValTable(self, var_id: int) -> bool: ... def inVarToAddrsTable(self, var_id: int) -> bool: ... def inAddrToAddrsTable(self, id: int) -> bool: ...