Skip to content

Commit 862a20c

Browse files
committed
Add support for uninterpreted functions in incremental SMT2 solver
Added handling for `ID_function_application` expressions in `convert_expr_to_smt()` and the `ID_mathematical_function` case to `convert_type_to_smt_sort()`. Fixes: #8068
1 parent 4fe3ade commit 862a20c

File tree

4 files changed

+170
-14
lines changed

4 files changed

+170
-14
lines changed

regression/cbmc/uninterpreted_function/uf1.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE no-new-smt
1+
CORE
22
uf1.c
33

44
^EXIT=10$

regression/cbmc/uninterpreted_function/uf2.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE no-new-smt
1+
CORE
22
uf2.c
33

44
^EXIT=0$

src/solvers/smt2_incremental/convert_expr_to_smt.cpp

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,27 @@ smt_sortt convert_type_to_smt_sort(const typet &type)
110110
{
111111
return convert_type_to_smt_sort(*array_type);
112112
}
113+
if(can_cast_type<mathematical_function_typet>(type))
114+
{
115+
// Mathematical function types are not directly converted to sorts.
116+
// They are used as function symbols in SMT and their applications
117+
// are handled via function_application_exprt.
118+
INVARIANT(
119+
false, "mathematical_function type should not be converted to a sort");
120+
}
113121
UNIMPLEMENTED_FEATURE("Generation of SMT formula for type: " + type.pretty());
114122
}
115123

116124
static smt_termt convert_expr_to_smt(const symbol_exprt &symbol_expr)
117125
{
126+
// Symbols with mathematical_function_typet represent uninterpreted functions.
127+
// They should not be converted to SMT terms directly - only their applications
128+
// (function_application_exprt) should be converted.
129+
INVARIANT(
130+
!can_cast_type<mathematical_function_typet>(symbol_expr.type()),
131+
"Symbols with mathematical_function_typet should not be converted to SMT "
132+
"terms - only function applications should be converted");
133+
118134
return smt_identifier_termt{symbol_expr.get_identifier(),
119135
convert_type_to_smt_sort(symbol_expr.type())};
120136
}
@@ -1405,6 +1421,67 @@ static smt_termt convert_expr_to_smt(
14051421
"Generation of SMT formula for vector expression: " + vector.pretty());
14061422
}
14071423

1424+
static smt_termt convert_expr_to_smt(
1425+
const function_application_exprt &function_application,
1426+
const sub_expression_mapt &converted)
1427+
{
1428+
// Get the function identifier - it should be a symbol with mathematical_function_typet
1429+
if(
1430+
const auto func_symbol =
1431+
expr_try_dynamic_cast<symbol_exprt>(function_application.function()))
1432+
{
1433+
INVARIANT(
1434+
can_cast_type<mathematical_function_typet>(func_symbol->type()),
1435+
"Function in function_application_exprt should have "
1436+
"mathematical_function_typet");
1437+
1438+
const auto &math_func_type =
1439+
to_mathematical_function_type(func_symbol->type());
1440+
const smt_sortt return_sort =
1441+
convert_type_to_smt_sort(math_func_type.codomain());
1442+
1443+
// Convert function arguments
1444+
std::vector<smt_termt> argument_terms;
1445+
for(const auto &arg : function_application.arguments())
1446+
{
1447+
argument_terms.push_back(converted.at(arg));
1448+
}
1449+
1450+
// Create a simple factory for uninterpreted functions
1451+
struct uninterpreted_functiont
1452+
{
1453+
irep_idt name;
1454+
smt_sortt return_type;
1455+
1456+
const char *identifier() const
1457+
{
1458+
return id2string(name).c_str();
1459+
}
1460+
1461+
smt_sortt return_sort(const std::vector<smt_termt> &) const
1462+
{
1463+
return return_type;
1464+
}
1465+
1466+
void validate(const std::vector<smt_termt> &) const
1467+
{
1468+
// No validation needed for uninterpreted functions
1469+
}
1470+
};
1471+
1472+
const uninterpreted_functiont uninterpreted_function{
1473+
func_symbol->get_identifier(), return_sort};
1474+
1475+
return smt_function_application_termt::factoryt<uninterpreted_functiont>(
1476+
uninterpreted_function)(argument_terms);
1477+
}
1478+
1479+
UNIMPLEMENTED_FEATURE(
1480+
"Generation of SMT formula for function application with non-symbol "
1481+
"function: " +
1482+
function_application.pretty());
1483+
}
1484+
14081485
static smt_termt convert_expr_to_smt(
14091486
const object_size_exprt &object_size,
14101487
const sub_expression_mapt &converted,
@@ -1794,6 +1871,21 @@ static smt_termt dispatch_expr_to_smt_conversion(
17941871
{
17951872
return convert_expr_to_smt(*vector, converted);
17961873
}
1874+
if(expr.id() == ID_tuple)
1875+
{
1876+
// Tuple expressions are used internally by function_application_exprt to
1877+
// store arguments. They should not be converted directly to SMT.
1878+
INVARIANT(
1879+
false,
1880+
"tuple expression should not be directly converted to SMT - it is used "
1881+
"internally by function_application_exprt");
1882+
}
1883+
if(
1884+
const auto function_application =
1885+
expr_try_dynamic_cast<function_application_exprt>(expr))
1886+
{
1887+
return convert_expr_to_smt(*function_application, converted);
1888+
}
17971889
if(const auto object_size = expr_try_dynamic_cast<object_size_exprt>(expr))
17981890
{
17991891
return convert_expr_to_smt(*object_size, converted, call_object_size);
@@ -1956,11 +2048,27 @@ smt_termt convert_expr_to_smt(
19562048
// Avoiding the conversion side steps a need to convert arbitrary code to
19572049
// SMT terms.
19582050
const auto address_of = expr_try_dynamic_cast<address_of_exprt>(expr);
1959-
if(!address_of)
1960-
return true;
1961-
return !can_cast_type<code_typet>(address_of->object().type());
2051+
if(address_of && can_cast_type<code_typet>(address_of->object().type()))
2052+
return false;
2053+
2054+
// Tuple expressions are internal to function_application_exprt and
2055+
// should not be traversed or converted directly
2056+
if(expr.id() == ID_tuple)
2057+
return false;
2058+
2059+
return true;
19622060
},
19632061
[&](const exprt &expr) {
2062+
// Skip tuple expressions entirely - they're internal to function_application
2063+
if(expr.id() == ID_tuple)
2064+
return;
2065+
2066+
// Skip symbols with mathematical_function_typet - they're declarations, not terms
2067+
if(
2068+
can_cast_expr<symbol_exprt>(expr) &&
2069+
can_cast_type<mathematical_function_typet>(expr.type()))
2070+
return;
2071+
19642072
const auto find_result = sub_expression_map.find(expr);
19652073
if(find_result != sub_expression_map.cend())
19662074
return;

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include <util/bitvector_expr.h>
77
#include <util/byte_operators.h>
88
#include <util/c_types.h>
9+
#include <util/mathematical_expr.h>
10+
#include <util/mathematical_types.h>
911
#include <util/range.h>
1012
#include <util/simplify_expr.h>
1113
#include <util/std_expr.h>
@@ -68,8 +70,9 @@ get_problem_messages(const smt_responset &response)
6870
/// `convert_expr_to_smt`. This is because any sub expressions which
6971
/// `convert_expr_to_smt` translates into function applications, must also be
7072
/// returned by this`gather_dependent_expressions` function.
71-
/// \details `symbol_exprt`, `array_exprt` and `nondet_symbol_exprt` add
72-
/// dependant expressions.
73+
/// \details `symbol_exprt`, `array_exprt`, `nondet_symbol_exprt`, and
74+
/// `function_application_exprt` (for the function part) add dependant
75+
/// expressions.
7376
static std::vector<exprt> gather_dependent_expressions(const exprt &root_expr)
7477
{
7578
std::vector<exprt> dependent_expressions;
@@ -90,6 +93,24 @@ static std::vector<exprt> gather_dependent_expressions(const exprt &root_expr)
9093
{
9194
dependent_expressions.push_back(expr_node);
9295
}
96+
// For function applications, we need to gather the function symbol as a
97+
// dependency so it gets declared, and we need to traverse the arguments
98+
// explicitly to avoid traversing the tuple wrapper.
99+
if(
100+
const auto func_app =
101+
expr_try_dynamic_cast<function_application_exprt>(expr_node))
102+
{
103+
if(can_cast_expr<symbol_exprt>(func_app->function()))
104+
{
105+
dependent_expressions.push_back(func_app->function());
106+
}
107+
// Push arguments for traversal (not the tuple wrapper)
108+
for(const auto &arg : func_app->arguments())
109+
{
110+
stack.push(&arg);
111+
}
112+
continue; // Skip normal operand traversal
113+
}
93114
// The decision procedure does not depend on the values inside address of
94115
// code typed expressions. We can build the address without knowing the
95116
// value at that memory location. In this case the hypothetical compiled
@@ -170,13 +191,40 @@ void send_function_definition(
170191
&expression_identifiers,
171192
std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
172193
{
173-
const smt_declare_function_commandt function{
174-
smt_identifier_termt(
175-
symbol_identifier, convert_type_to_smt_sort(expr.type())),
176-
{}};
177-
expression_identifiers.emplace(expr, function.identifier());
178-
identifier_table.emplace(symbol_identifier, function.identifier());
179-
solver_process->send(function);
194+
// Handle mathematical (uninterpreted) functions
195+
if(
196+
const auto math_func_type =
197+
type_try_dynamic_cast<mathematical_function_typet>(expr.type()))
198+
{
199+
// Build parameter sorts from the function domain
200+
std::vector<smt_sortt> parameter_sorts;
201+
for(const auto &param_type : math_func_type->domain())
202+
{
203+
parameter_sorts.push_back(convert_type_to_smt_sort(param_type));
204+
}
205+
206+
// The return sort comes from the codomain
207+
const smt_sortt return_sort =
208+
convert_type_to_smt_sort(math_func_type->codomain());
209+
210+
const smt_declare_function_commandt function{
211+
smt_identifier_termt(symbol_identifier, return_sort), parameter_sorts};
212+
213+
expression_identifiers.emplace(expr, function.identifier());
214+
identifier_table.emplace(symbol_identifier, function.identifier());
215+
solver_process->send(function);
216+
}
217+
else
218+
{
219+
// Normal symbol (non-function) declaration
220+
const smt_declare_function_commandt function{
221+
smt_identifier_termt(
222+
symbol_identifier, convert_type_to_smt_sort(expr.type())),
223+
{}};
224+
expression_identifiers.emplace(expr, function.identifier());
225+
identifier_table.emplace(symbol_identifier, function.identifier());
226+
solver_process->send(function);
227+
}
180228
}
181229

182230
/// \brief Defines any functions which \p expr depends on, which have not yet

0 commit comments

Comments
 (0)