diff --git a/src/solvers/smt2/smt2_conv.cpp b/src/solvers/smt2/smt2_conv.cpp index d9da852ab62..2b38389dcfa 100644 --- a/src/solvers/smt2/smt2_conv.cpp +++ b/src/solvers/smt2/smt2_conv.cpp @@ -1285,6 +1285,10 @@ void smt2_convt::convert_expr(const exprt &expr) { convert_mod(to_mod_expr(expr)); } + else if(expr.id() == ID_euclidean_mod) + { + convert_euclidean_mod(to_euclidean_mod_expr(expr)); + } else if(expr.id()==ID_mult) { convert_mult(to_mult_expr(expr)); @@ -2988,6 +2992,21 @@ void smt2_convt::convert_constant(const constant_exprt &expr) UNEXPECTEDCASE("unknown constant: "+expr_type.id_string()); } +void smt2_convt::convert_euclidean_mod(const euclidean_mod_exprt &expr) +{ + if(expr.type().id() == ID_integer) + { + out << "(mod "; + convert_expr(expr.op0()); + out << ' '; + convert_expr(expr.op1()); + out << ')'; + } + else + UNEXPECTEDCASE( + "unsupported type for euclidean_mod: " + expr.type().id_string()); +} + void smt2_convt::convert_mod(const mod_exprt &expr) { if(expr.type().id()==ID_unsignedbv || diff --git a/src/solvers/smt2/smt2_conv.h b/src/solvers/smt2/smt2_conv.h index 96b302b314b..3c812d5c341 100644 --- a/src/solvers/smt2/smt2_conv.h +++ b/src/solvers/smt2/smt2_conv.h @@ -120,6 +120,7 @@ class smt2_convt : public stack_decision_proceduret void convert_floatbv_div(const ieee_float_op_exprt &expr); void convert_floatbv_mult(const ieee_float_op_exprt &expr); void convert_mod(const mod_exprt &expr); + void convert_euclidean_mod(const euclidean_mod_exprt &expr); void convert_index(const index_exprt &expr); void convert_member(const member_exprt &expr); diff --git a/src/solvers/smt2/smt2_parser.cpp b/src/solvers/smt2/smt2_parser.cpp index cee001d1388..d760aabf967 100644 --- a/src/solvers/smt2/smt2_parser.cpp +++ b/src/solvers/smt2/smt2_parser.cpp @@ -1067,7 +1067,12 @@ void smt2_parsert::setup_expressions() expressions["bvurem"] = [this] { return binary(ID_mod, operands()); }; - expressions["%"] = [this] { return binary(ID_mod, operands()); }; + expressions["mod"] = [this] { + // SMT-LIB2 uses Boute's Euclidean definition for mod, + // which is never negative and undefined when the second + // argument is zero. + return binary(ID_euclidean_mod, operands()); + }; expressions["concat"] = [this] { auto op = operands(); diff --git a/src/util/irep_ids.def b/src/util/irep_ids.def index c5f1b9c2008..61028ce0fcd 100644 --- a/src/util/irep_ids.def +++ b/src/util/irep_ids.def @@ -235,6 +235,7 @@ IREP_ID_ONE(array_equal) IREP_ID_ONE(array_set) IREP_ID_ONE(array_copy) IREP_ID_ONE(array_list) +IREP_ID_ONE(euclidean_mod) IREP_ID_ONE(mod) IREP_ID_ONE(rem) IREP_ID_ONE(shr) diff --git a/src/util/std_expr.h b/src/util/std_expr.h index 3af2bd5fa55..5bca50aea0e 100644 --- a/src/util/std_expr.h +++ b/src/util/std_expr.h @@ -1043,8 +1043,10 @@ inline div_exprt &to_div_expr(exprt &expr) return ret; } - -/// \brief Modulo +/// \brief Modulo defined as lhs-(rhs * truncate(lhs/rhs)). +/// The sign follows the lhs or dividend. This matches C99 and +/// SMT-LIB's bvsrem but differs from the Euclidian definition +/// and from SMT-LIB's bvsmod. class mod_exprt:public binary_exprt { public: @@ -1088,6 +1090,50 @@ inline mod_exprt &to_mod_expr(exprt &expr) return ret; } +/// \brief Boute's Euclidean definition of Modulo -- to match SMT-LIB2 +class euclidean_mod_exprt : public binary_exprt +{ +public: + euclidean_mod_exprt(exprt _lhs, exprt _rhs) + : binary_exprt(std::move(_lhs), ID_euclidean_mod, std::move(_rhs)) + { + } +}; + +template <> +inline bool can_cast_expr(const exprt &base) +{ + return base.id() == ID_euclidean_mod; +} + +inline void validate_expr(const euclidean_mod_exprt &value) +{ + validate_operands(value, 2, "Modulo must have two operands"); +} + +/// \brief Cast an exprt to a \ref euclidean_mod_exprt +/// +/// \a expr must be known to be \ref euclidean_mod_exprt. +/// +/// \param expr: Source expression +/// \return Object of type \ref euclidean_mod_exprt +inline const euclidean_mod_exprt &to_euclidean_mod_expr(const exprt &expr) +{ + PRECONDITION(expr.id() == ID_euclidean_mod); + const euclidean_mod_exprt &ret = + static_cast(expr); + validate_expr(ret); + return ret; +} + +/// \copydoc to_euclidean_mod_expr(const exprt &) +inline euclidean_mod_exprt &to_euclidean_mod_expr(exprt &expr) +{ + PRECONDITION(expr.id() == ID_euclidean_mod); + euclidean_mod_exprt &ret = static_cast(expr); + validate_expr(ret); + return ret; +} /// \brief Remainder of division class rem_exprt:public binary_exprt