Skip to content

Commit b951387

Browse files
committed
Add support for function pointers to goto-harness
Also add support for having multiple constructors for the same type with different behaviours and different signatures. We are doing this because we need some types to be sometimes nullable and sometimes not, and for example, for arrays we sometimes need to pass a size parameter and sometimes not.
1 parent 9357614 commit b951387

File tree

8 files changed

+182
-8
lines changed

8 files changed

+182
-8
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <assert.h>
2+
3+
typedef int (*fptr)(int a, int b);
4+
5+
int max_normal(int first, int second)
6+
{
7+
return first >= second ? first : second;
8+
}
9+
10+
int max_crazy(int first, int second)
11+
{
12+
return first >= second ? second : first;
13+
}
14+
15+
void use_fptr(fptr max, int first, int second)
16+
{
17+
int m = max(first, second);
18+
assert(m == first || m == second);
19+
assert(m >= first && m >= second);
20+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
test.c
3+
--harness-type call-function --harness-function-name harness --function use_fptr
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
\[use_fptr.assertion.1\] line \d+ assertion m == first \|\| m == second: SUCCESS
7+
\[use_fptr.assertion.2\] line \d+ assertion m >= first && m >= second: FAILURE
8+
--
9+
^warning: ignoring
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
typedef int (*fptr_t)(void);
5+
6+
void entry_point(fptr_t f, fptr_t f_but_may_be_null)
7+
{
8+
assert(f != (void *)0); // expected to succeed
9+
assert(f == (void *)0); // expected to fail
10+
assert(f_but_may_be_null != (void *)0); // expected to fail
11+
assert(f_but_may_be_null == (void *)0); // expected to fail
12+
assert(f_but_may_be_null == (void *)0 || f() == f_but_may_be_null());
13+
}
14+
15+
int f(void)
16+
{
17+
return 42;
18+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
test.c
3+
--harness-type call-function --function entry_point --function-pointer-can-be-null __goto_harness::f_but_may_be_null
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
\[entry_point.assertion.1\] line \d+ assertion f != \(void \*\)0: SUCCESS
7+
\[entry_point.assertion.2\] line \d+ assertion f == \(void \*\)0: FAILURE
8+
\[entry_point.assertion.3\] line \d+ assertion f_but_may_be_null != \(void \*\)0: FAILURE
9+
\[entry_point.assertion.4\] line \d+ assertion f_but_may_be_null == \(void \*\)0: FAILURE
10+
\[entry_point.assertion.5\] line \d+ assertion f_but_may_be_null == \(void \*\)0 || f\(\) == f_but_may_be_null\(\): SUCCESS
11+
--
12+
^warning: ignoring

src/goto-harness/common_harness_generator_options.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@ Author: Diffblue Ltd.
1414
"max-nondet-tree-depth"
1515
#define COMMON_HARNESS_GENERATOR_MIN_ARRAY_SIZE_OPT "min-array-size"
1616
#define COMMON_HARNESS_GENERATOR_MAX_ARRAY_SIZE_OPT "max-array-size"
17+
#define COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT \
18+
"function-pointer-can-be-null"
1719

1820
// clang-format off
1921
#define COMMON_HARNESS_GENERATOR_OPTIONS \
2022
"(" COMMON_HARNESS_GENERATOR_MIN_NULL_TREE_DEPTH_OPT "):" \
2123
"(" COMMON_HARNESS_GENERATOR_MAX_NONDET_TREE_DEPTH_OPT "):" \
2224
"(" COMMON_HARNESS_GENERATOR_MIN_ARRAY_SIZE_OPT "):" \
2325
"(" COMMON_HARNESS_GENERATOR_MAX_ARRAY_SIZE_OPT "):" \
26+
"(" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT "):" \
2427
// COMMON_HARNESS_GENERATOR_OPTIONS
2528

2629
// clang-format on

src/goto-harness/function_call_harness_generator.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Author: Diffblue Ltd.
1212
#include <util/arith_tools.h>
1313
#include <util/c_types.h>
1414
#include <util/exception_utils.h>
15+
#include <util/prefix.h>
1516
#include <util/std_code.h>
1617
#include <util/std_expr.h>
1718
#include <util/string2int.h>
@@ -164,6 +165,18 @@ void function_call_harness_generatort::handle_option(
164165
p_impl->function_parameters_to_treat_as_cstrings.insert(
165166
values.begin(), values.end());
166167
}
168+
else if(option == COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT)
169+
{
170+
std::transform(
171+
values.begin(),
172+
values.end(),
173+
std::inserter(
174+
p_impl->recursive_initialization_config
175+
.potential_null_function_pointers,
176+
p_impl->recursive_initialization_config.potential_null_function_pointers
177+
.end()),
178+
[](const std::string &opt) -> irep_idt { return irep_idt{opt}; });
179+
}
167180
else
168181
{
169182
throw invalid_command_line_argument_exceptiont{

src/goto-harness/recursive_initialization.cpp

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ code_blockt recursive_initializationt::build_constructor_body(
159159
const exprt &depth_symbol,
160160
const symbol_exprt &result_symbol,
161161
const optionalt<exprt> &size_symbol,
162-
const optionalt<irep_idt> &lhs_name)
162+
const optionalt<irep_idt> &lhs_name,
163+
const bool is_nullable)
163164
{
164165
PRECONDITION(result_symbol.type().id() == ID_pointer);
165166
const typet &type = result_symbol.type().subtype();
@@ -169,6 +170,10 @@ code_blockt recursive_initializationt::build_constructor_body(
169170
}
170171
else if(type.id() == ID_pointer)
171172
{
173+
if(type.subtype().id() == ID_code)
174+
{
175+
return build_function_pointer_constructor(result_symbol, is_nullable);
176+
}
172177
if(lhs_name.has_value())
173178
{
174179
if(should_be_treated_as_cstring(*lhs_name) && type == char_type())
@@ -203,17 +208,23 @@ irep_idt recursive_initializationt::build_constructor(const exprt &expr)
203208
// void type_constructor_T(int depth_T, T *result_T, int *size);
204209
optionalt<irep_idt> size_var;
205210
optionalt<irep_idt> expr_name;
211+
bool is_nullable = false;
212+
bool has_size_param = false;
206213
if(expr.id() == ID_symbol)
207214
{
208215
expr_name = to_symbol_expr(expr).get_identifier();
216+
is_nullable = initialization_config.potential_null_function_pointers.count(
217+
expr_name.value());
209218
if(should_be_treated_as_array(*expr_name))
210219
{
211220
size_var = get_associated_size_variable(*expr_name);
221+
has_size_param = true;
212222
}
213223
}
214224
const typet &type = expr.type();
215-
if(type_constructor_names.find(type) != type_constructor_names.end())
216-
return type_constructor_names[type];
225+
const constructor_key key{type, is_nullable, has_size_param};
226+
if(type_constructor_names.find(key) != type_constructor_names.end())
227+
return type_constructor_names[key];
217228

218229
const std::string &pretty_type = type2id(type);
219230
symbolt &depth_symbol =
@@ -254,7 +265,7 @@ irep_idt recursive_initializationt::build_constructor(const exprt &expr)
254265
}
255266
const symbolt &function_symbol = get_fresh_fun_symbol(
256267
"type_constructor_" + pretty_type, code_typet{fun_params, empty_typet{}});
257-
type_constructor_names[type] = function_symbol.name;
268+
type_constructor_names[key] = function_symbol.name;
258269
symbolt *mutable_symbol = symbol_table.get_writeable(function_symbol.name);
259270

260271
// the body is specific for each type of expression
@@ -264,11 +275,12 @@ irep_idt recursive_initializationt::build_constructor(const exprt &expr)
264275
size_symbol_expr,
265276
// the expression name may be needed to decide if expr should be treated as
266277
// a string
267-
expr_name);
278+
expr_name,
279+
is_nullable);
268280

269281
goto_model.goto_functions.function_map[function_symbol.name].type =
270282
to_code_type(function_symbol.type);
271-
return type_constructor_names[type];
283+
return type_constructor_names.at(key);
272284
}
273285

274286
symbol_exprt recursive_initializationt::get_malloc_function()
@@ -799,3 +811,57 @@ bool recursive_initializationt::needs_freeing(const exprt &expr) const
799811
}
800812
return true;
801813
}
814+
815+
code_blockt recursive_initializationt::build_function_pointer_constructor(
816+
const symbol_exprt &result,
817+
bool is_nullable)
818+
{
819+
PRECONDITION(can_cast_type<pointer_typet>(result.type()));
820+
const auto &result_type = to_pointer_type(result.type());
821+
PRECONDITION(can_cast_type<pointer_typet>(result_type.subtype()));
822+
const auto &function_pointer_type = to_pointer_type(result_type.subtype());
823+
PRECONDITION(can_cast_type<code_typet>(function_pointer_type.subtype()));
824+
const auto &function_type = to_code_type(function_pointer_type.subtype());
825+
826+
std::vector<exprt> targets;
827+
828+
for(const auto &sym : goto_model.get_symbol_table())
829+
{
830+
if(sym.second.type == function_type)
831+
{
832+
targets.push_back(address_of_exprt{sym.second.symbol_expr()});
833+
}
834+
}
835+
836+
if(is_nullable)
837+
targets.push_back(null_pointer_exprt{function_pointer_type});
838+
839+
code_blockt body{};
840+
841+
const auto function_pointer_selector =
842+
get_fresh_local_symexpr("function_pointer_selector");
843+
body.add(
844+
code_assignt{function_pointer_selector,
845+
side_effect_expr_nondett{function_pointer_selector.type()}});
846+
auto function_pointer_index = std::size_t{0};
847+
848+
for(const auto &target : targets)
849+
{
850+
auto const assign = code_assignt{dereference_exprt{result}, target};
851+
if(function_pointer_index != targets.size() - 1)
852+
{
853+
auto const condition = equal_exprt{
854+
function_pointer_selector,
855+
from_integer(function_pointer_index, function_pointer_selector.type())};
856+
auto const then = code_blockt{{assign, code_returnt{}}};
857+
body.add(code_ifthenelset{condition, then});
858+
}
859+
else
860+
{
861+
body.add(assign);
862+
}
863+
++function_pointer_index;
864+
}
865+
866+
return body;
867+
}

src/goto-harness/recursive_initialization.h

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct recursive_initialization_configt
2727
std::size_t min_null_tree_depth = 1;
2828
std::size_t max_nondet_tree_depth = 2;
2929
irep_idt mode;
30+
std::unordered_set<irep_idt> potential_null_function_pointers;
3031

3132
// array stuff
3233
std::size_t max_dynamic_array_size = 2;
@@ -57,7 +58,29 @@ class recursive_initializationt
5758
{
5859
public:
5960
using recursion_sett = std::set<irep_idt>;
60-
using type_constructor_namest = std::map<typet, irep_idt>;
61+
struct constructor_key
62+
{
63+
typet constructor_type;
64+
bool is_nullable;
65+
bool has_size_parameter;
66+
bool operator<(const constructor_key &other) const
67+
{
68+
return std::tie(constructor_type, is_nullable, has_size_parameter) <
69+
std::tie(
70+
other.constructor_type,
71+
other.is_nullable,
72+
other.has_size_parameter);
73+
};
74+
bool operator==(const constructor_key &other) const
75+
{
76+
return std::tie(constructor_type, is_nullable, has_size_parameter) ==
77+
std::tie(
78+
other.constructor_type,
79+
other.is_nullable,
80+
other.has_size_parameter);
81+
};
82+
};
83+
using type_constructor_namest = std::map<constructor_key, irep_idt>;
6184

6285
recursive_initializationt(
6386
recursive_initialization_configt initialization_config,
@@ -165,19 +188,29 @@ class recursive_initializationt
165188
/// \param result_symbol: the symbol for `result` parameter
166189
/// \param size_symbol: maybe the symbol for `size` parameter
167190
/// \param lhs_name: the name of the original symbol
191+
/// \param is_nullable: flag for setting a function pointer nullable
168192
/// \return the body of the constructor
169193
code_blockt build_constructor_body(
170194
const exprt &depth_symbol,
171195
const symbol_exprt &result_symbol,
172196
const optionalt<exprt> &size_symbol,
173-
const optionalt<irep_idt> &lhs_name);
197+
const optionalt<irep_idt> &lhs_name,
198+
const bool is_nullable);
174199

175200
/// Check if a constructor for the type of \p expr already exists and create
176201
/// it if not.
177202
/// \param expr: the expression to be constructed
178203
/// \return name of the constructor function
179204
irep_idt build_constructor(const exprt &expr);
180205

206+
/// Constructor for function pointers.
207+
/// \param result: symbol of the result parameter
208+
/// \param is_nullable: if the function pointer can be null
209+
/// \return the body of the constructor
210+
code_blockt build_function_pointer_constructor(
211+
const symbol_exprt &result,
212+
bool is_nullable);
213+
181214
/// Generic constructor for all pointers: only builds one pointee (not an
182215
/// array) but may recourse in case the pointee contains more pointers, e.g.
183216
/// a struct.

0 commit comments

Comments
 (0)