Skip to content

Commit

Permalink
Extend numeric_cast for constant expressions
Browse files Browse the repository at this point in the history
This requires moving some declarations to arith_tools, because overload
needs to be declared in the same struct.

This also adds a numeric_cast_v function which returns a value instead
of an optional but an invariant can fail.
  • Loading branch information
romainbrenguier committed Dec 8, 2017
1 parent 20b5366 commit 946b6e2
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 67 deletions.
125 changes: 125 additions & 0 deletions src/util/arith_tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,146 @@ Author: Daniel Kroening, [email protected]
#define CPROVER_UTIL_ARITH_TOOLS_H

#include "mp_arith.h"
#include "optional.h"
#include "invariant.h"

class exprt;
class constant_exprt;
class typet;

// this one will go away
// returns 'true' on error
/// \deprecated: use the constant_exprt version instead
bool to_integer(const exprt &expr, mp_integer &int_value);

// returns 'true' on error
/// \deprecated: use numeric_cast<mp_integer> instead
bool to_integer(const constant_exprt &expr, mp_integer &int_value);

// returns 'true' on error
bool to_unsigned_integer(const constant_exprt &expr, unsigned &uint_value);

/// Numerical cast provides a unified way of converting from one numerical type
/// to another.
/// Generic case doesn't exist, this has to be specialized for different types.
template <typename Target, typename = void>
struct numeric_castt final
{
};

/// Convert expression to mp_integer
template <>
struct numeric_castt<mp_integer> final
{
optionalt<mp_integer> operator()(const exprt &expr) const
{
mp_integer out;
if(to_integer(expr, out))
return {};
return out;
}
};

/// Convert mp_integer or expr to any integral type
template <typename T>
struct numeric_castt<T,
typename std::enable_if<std::is_integral<T>::value>::type>
{
private:
// Unchecked conversion from mp_integer when T is signed
template <typename U = T,
typename std::enable_if<std::is_signed<U>::value, int>::type = 0>
static auto get_val(const mp_integer &mpi) -> decltype(mpi.to_long())
{
return mpi.to_long();
}

// Unchecked conversion from mp_integer when T is unsigned
template <typename U = T,
typename std::enable_if<!std::is_signed<U>::value, int>::type = 0>
static auto get_val(const mp_integer &mpi) -> decltype(mpi.to_ulong())
{
return mpi.to_ulong();
}

public:
// Conversion from mp_integer to integral type T
optionalt<T> operator()(const mp_integer &mpi) const
{
#if !defined(_MSC_VER) || _MSC_VER >= 1900
static_assert(
std::numeric_limits<T>::max() <=
std::numeric_limits<decltype(get_val(mpi))>::max() &&
std::numeric_limits<T>::min() >=
std::numeric_limits<decltype(get_val(mpi))>::min(),
"Numeric cast only works for types smaller than long long");
#else
// std::numeric_limits<> methods are not declared constexpr in old versions
// of VS
PRECONDITION(
std::numeric_limits<T>::max() <=
std::numeric_limits<decltype(get_val(mpi))>::max() &&
std::numeric_limits<T>::min() >=
std::numeric_limits<decltype(get_val(mpi))>::min());
#endif
if(
mpi <= std::numeric_limits<T>::max() &&
mpi >= std::numeric_limits<T>::min())
// to_long converts to long long which is the largest signed numeric type
return static_cast<T>(get_val(mpi));
else
return {};
}

// Conversion from expression
optionalt<T> operator()(const exprt &expr) const
{
if(auto mpi_opt = numeric_castt<mp_integer>{}(expr))
return numeric_castt<T>{}(*mpi_opt);
else
return {};
}
};

/// Converts an expression to any integral type
/// \tparam Target: type to convert to
/// \param arg: expression to convert
/// \return optional integer of type Target if conversion is possible,
/// empty optional otherwise.
template <typename Target>
optionalt<Target> numeric_cast(const exprt &arg)
{
return numeric_castt<Target>{}(arg);
}

/// Convert an mp_integer to integral type Target
/// An invariant with fail with message "Bad conversion" if conversion
/// is not possible.
/// \tparam Target: type to convert to
/// \param arg: mp_integer
/// \return value of type Target
template <typename Target>
Target numeric_cast_v(const mp_integer &arg)
{
const auto maybe = numeric_castt<Target>{}(arg);
INVARIANT(maybe, "Bad conversion");
return *maybe;
}

/// Convert an expression to integral type Target
/// An invariant with fail with message "Bad conversion" if conversion
/// is not possible.
/// \tparam Target: type to convert to
/// \param arg: constant expression
/// \return value of type Target
template <typename Target>
Target numeric_cast_v(const exprt &arg)
{
const auto maybe = numeric_castt<Target>{}(arg);
INVARIANT(maybe, "Bad conversion");
return *maybe;
}

// PRECONDITION(false) in case of unsupported type
constant_exprt from_integer(const mp_integer &int_value, const typet &type);

Expand Down
67 changes: 0 additions & 67 deletions src/util/mp_arith.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,71 +62,4 @@ unsigned integer2unsigned(const mp_integer &);

const mp_integer mp_zero=string2integer("0");

/// Numerical cast provides a unified way of converting from one numerical type
/// to another.
/// Generic case doesn't exist, this has to be specialized for different types.
template <typename T, typename = void>
struct numeric_castt final
{
};

/// Convert mp_integer to any signed type
/// \tparam T: type to convert to
/// \param mpi: mp_integer to convert
/// \return optional integer of type T if conversion is possible,
/// empty optional otherwise.
template <typename T>
struct numeric_castt<T,
typename std::enable_if<std::is_integral<T>::value &&
std::is_signed<T>::value>::type>
{
static optionalt<T> numeric_cast(const mp_integer &mpi)
{
static_assert(
std::numeric_limits<T>::max() <=
std::numeric_limits<decltype(mpi.to_long())>::max() &&
std::numeric_limits<T>::min() >=
std::numeric_limits<decltype(mpi.to_long())>::min(),
"Numeric cast only works for types smaller than long long");
if(
mpi <= std::numeric_limits<T>::max() &&
mpi >= std::numeric_limits<T>::min())
// to_long converts to long long which is the largest signed numeric type
return {static_cast<T>(mpi.to_long())};
else
return {};
}
};

/// Convert mp_integer to any unsigned type
/// \tparam T: type to convert to
/// \param mpi: mp_integer to convert
/// \return optional integer of type T if conversion is possible,
/// empty optional otherwise.
template <typename T>
struct numeric_castt<T,
typename std::enable_if<std::is_integral<T>::value &&
!std::is_signed<T>::value>::type>
{
static optionalt<T> numeric_cast(const mp_integer &mpi)
{
static_assert(
std::numeric_limits<T>::max() <=
std::numeric_limits<decltype(mpi.to_ulong())>::max() &&
std::numeric_limits<T>::min() >=
std::numeric_limits<decltype(mpi.to_ulong())>::min(),
"Numeric cast only works for types smaller than unsigned long long");
if(mpi <= std::numeric_limits<T>::max() && mpi >= 0)
return {static_cast<T>(mpi.to_ulong())};
else
return {};
}
};

template <typename T>
optionalt<T> numeric_cast(const mp_integer &mpi)
{
return numeric_castt<T>::numeric_cast(mpi);
}

#endif // CPROVER_UTIL_MP_ARITH_H

0 comments on commit 946b6e2

Please sign in to comment.