include/PowersetAbstractDomain.h (142 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <cstddef>
#include <initializer_list>
#include <ostream>
#include <sstream>
#include <type_traits>
#include "AbstractDomain.h"
namespace sparta {
/*
* The definition of an abstract value belonging to a powerset abstract domain.
* The `Snapshot` parameter describes the type of the container returned by the
* elements() method. It can be a reference to the actual underlying structure
* or a completely different type.
*
* Note: the default constructor is expected to generate the empty set.
*/
template <typename Element, typename Snapshot, typename Derived>
class PowersetImplementation : public AbstractValue<Derived> {
public:
virtual Snapshot elements() const = 0;
virtual size_t size() const = 0;
virtual bool contains(const Element& e) const = 0;
virtual void add(const Element& e) = 0;
virtual void remove(const Element& e) = 0;
// We only consider finite powerset domains. Hence, we don't need to define a
// widening or narrowing operator.
AbstractValueKind widen_with(const Derived& other) override {
return this->join_with(other);
}
AbstractValueKind narrow_with(const Derived& other) override {
return this->meet_with(other);
}
virtual AbstractValueKind difference_with(const Derived& other) = 0;
};
/*
* A powerset abstract domain is the complete lattice made of all subsets of a
* base set of elements. Note that in this abstract domain, Bottom is different
* from the empty set. Bottom represents an unreachable program configuration,
* whereas the empty set may have a perfectly valid semantics (like in liveness
* analysis or pointer analysis, for example). Since in practice the base set of
* elements is usually very large or infinite, it is implicitly represented by
* Top. We use the AbstractDomainScaffolding template to build the domain. The
* choice of the underlying implementation for sets is left as a parameter of
* the abstract domain.
*/
template <typename Element,
typename Powerset,
typename Snapshot,
typename Derived>
class PowersetAbstractDomain
: public AbstractDomainScaffolding<Powerset, Derived> {
public:
virtual ~PowersetAbstractDomain() {
// The destructor is the only method that is guaranteed to be created when a
// class template is instantiated. This is a good place to perform all the
// sanity checks on the template parameters.
static_assert(
std::is_base_of<PowersetImplementation<Element, Snapshot, Powerset>,
Powerset>::value,
"Powerset doesn't inherit from PowersetImplementation");
static_assert(
std::is_base_of<
PowersetAbstractDomain<Element, Powerset, Snapshot, Derived>,
Derived>::value,
"Derived doesn't inherit from PowersetAbstractDomain");
}
/*
* This constructor produces the empty set, which is distinct from Bottom.
*/
PowersetAbstractDomain() : AbstractDomainScaffolding<Powerset, Derived>() {}
explicit PowersetAbstractDomain(AbstractValueKind kind)
: AbstractDomainScaffolding<Powerset, Derived>(kind) {}
Snapshot elements() const {
RUNTIME_CHECK(this->kind() == AbstractValueKind::Value,
invalid_abstract_value()
<< expected_kind(AbstractValueKind::Value)
<< actual_kind(this->kind()));
return this->get_value()->elements();
}
size_t size() const {
RUNTIME_CHECK(this->kind() == AbstractValueKind::Value,
invalid_abstract_value()
<< expected_kind(AbstractValueKind::Value)
<< actual_kind(this->kind()));
return this->get_value()->size();
}
void add(const Element& e) {
if (this->kind() == AbstractValueKind::Value) {
this->get_value()->add(e);
}
}
void add(std::initializer_list<Element> l) { add(l.begin(), l.end()); }
template <typename InputIterator>
void add(InputIterator first, InputIterator last) {
if (this->kind() == AbstractValueKind::Value) {
Powerset* powerset = this->get_value();
for (InputIterator it = first; it != last; ++it) {
powerset->add(*it);
}
}
}
void remove(const Element& e) {
if (this->kind() == AbstractValueKind::Value) {
this->get_value()->remove(e);
}
}
void remove(std::initializer_list<Element> l) {
if (this->kind() == AbstractValueKind::Value) {
Powerset* powerset = this->get_value();
for (const Element& e : l) {
powerset->remove(e);
}
}
}
template <typename InputIterator>
void remove(InputIterator first, InputIterator last) {
if (this->kind() == AbstractValueKind::Value) {
Powerset* powerset = this->get_value();
for (InputIterator it = first; it != last; ++it) {
powerset->remove(*it);
}
}
}
void difference_with(const Derived& other) {
if (this->is_bottom() || other.is_top()) {
this->set_to_bottom();
} else if (this->is_top() || other.is_bottom()) {
// Note that the difference of top with anything except top is top.
return;
} else {
auto kind = this->get_value()->difference_with(*other.get_value());
if (kind == AbstractValueKind::Bottom) {
this->set_to_bottom();
} else if (kind == AbstractValueKind::Top) {
this->set_to_top();
}
}
}
bool contains(const Element& e) const {
switch (this->kind()) {
case AbstractValueKind::Bottom: {
return false;
}
case AbstractValueKind::Top: {
return true;
}
case AbstractValueKind::Value: {
return this->get_value()->contains(e);
}
}
}
friend std::ostream& operator<<(std::ostream& o, const Derived& s) {
switch (s.kind()) {
case AbstractValueKind::Bottom: {
o << "_|_";
break;
}
case AbstractValueKind::Top: {
o << "T";
break;
}
case AbstractValueKind::Value: {
o << *s.get_value();
break;
}
}
return o;
}
};
} // namespace sparta