-
Notifications
You must be signed in to change notification settings - Fork 274
Allow function pointer arguments to be equal #5171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#include <assert.h> | ||
#include <stdlib.h> | ||
|
||
typedef struct st1 | ||
{ | ||
struct st *next; | ||
int data; | ||
} st1_t; | ||
|
||
typedef struct st2 | ||
{ | ||
struct st *next; | ||
int data; | ||
} st2_t; | ||
|
||
void func(st1_t *p, st1_t *q, st2_t *r, st2_t *s, st2_t *t) | ||
{ | ||
assert(p != NULL); | ||
assert(p->next != NULL); | ||
|
||
assert(p == q); | ||
|
||
assert((void *)p != (void *)r); | ||
|
||
assert(r == s); | ||
assert(r == t); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
CORE | ||
main.c | ||
--function func --min-null-tree-depth 10 --max-nondet-tree-depth 3 --harness-type call-function --treat-pointers-equal 'p,q;r,s,t' --treat-pointers-equal-maybe | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
^\[func.assertion.\d+\] line \d+ assertion p == q: FAILURE$ | ||
^\[func.assertion.\d+\] line \d+ assertion \(void \*\)p != \(void \*\)r: SUCCESS$ | ||
^\[func.assertion.\d+\] line \d+ assertion r == s: FAILURE$ | ||
^\[func.assertion.\d+\] line \d+ assertion r == t: FAILURE$ | ||
VERIFICATION FAILED | ||
-- | ||
^warning: ignoring |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#include <assert.h> | ||
#include <stdlib.h> | ||
|
||
typedef struct st1 | ||
{ | ||
struct st *next; | ||
int data; | ||
} st1_t; | ||
|
||
typedef struct st2 | ||
{ | ||
struct st *next; | ||
int data; | ||
} st2_t; | ||
|
||
void func(st1_t *p, st1_t *q, st2_t *r, st2_t *s, st2_t *t) | ||
{ | ||
assert(p != NULL); | ||
assert(p->next != NULL); | ||
|
||
assert(p == q); | ||
|
||
assert((void *)p != (void *)r); | ||
|
||
assert(r == s); | ||
assert(r == t); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
CORE | ||
main.c | ||
--function func --min-null-tree-depth 10 --max-nondet-tree-depth 3 --harness-type call-function --treat-pointers-equal 'p,q;r,s,t' | ||
^EXIT=0$ | ||
^SIGNAL=0$ | ||
VERIFICATION SUCCESSFUL | ||
-- | ||
^warning: ignoring |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -93,13 +93,54 @@ recursive_initializationt::recursive_initializationt( | |
initialization_config.min_null_tree_depth, | ||
signed_int_type()))) | ||
{ | ||
common_arguments_origins.resize( | ||
this->initialization_config.pointers_to_treat_equal.size()); | ||
} | ||
|
||
void recursive_initializationt::initialize( | ||
const exprt &lhs, | ||
const exprt &depth, | ||
code_blockt &body) | ||
{ | ||
// special handling for the case that pointer arguments should be treated | ||
// equal: if the equality is enforced (rather than the pointers may be equal), | ||
// then we don't even build the constructor functions | ||
if(lhs.id() == ID_symbol) | ||
{ | ||
const auto maybe_cluster_index = | ||
find_equal_cluster(to_symbol_expr(lhs).get_identifier()); | ||
if(maybe_cluster_index.has_value()) | ||
{ | ||
if(common_arguments_origins[*maybe_cluster_index].has_value()) | ||
{ | ||
const auto set_equal_case = | ||
code_assignt{lhs, *common_arguments_origins[*maybe_cluster_index]}; | ||
if(initialization_config.arguments_may_be_equal) | ||
{ | ||
const irep_idt &fun_name = build_constructor(lhs); | ||
const symbolt &fun_symbol = | ||
goto_model.symbol_table.lookup_ref(fun_name); | ||
const auto proper_init_case = code_function_callt{ | ||
fun_symbol.symbol_expr(), {depth, address_of_exprt{lhs}}}; | ||
|
||
body.add(code_ifthenelset{ | ||
side_effect_expr_nondett{bool_typet{}, source_locationt{}}, | ||
set_equal_case, | ||
proper_init_case}); | ||
} | ||
else | ||
{ | ||
body.add(set_equal_case); | ||
} | ||
return; | ||
} | ||
else | ||
{ | ||
common_arguments_origins[*maybe_cluster_index] = lhs; | ||
} | ||
} | ||
} | ||
|
||
const irep_idt &fun_name = build_constructor(lhs); | ||
const symbolt &fun_symbol = goto_model.symbol_table.lookup_ref(fun_name); | ||
|
||
|
@@ -280,6 +321,19 @@ bool recursive_initializationt::should_be_treated_as_array( | |
initialization_config.pointers_to_treat_as_arrays.end(); | ||
} | ||
|
||
optionalt<recursive_initializationt::equal_cluster_idt> | ||
recursive_initializationt::find_equal_cluster(const irep_idt &name) const | ||
{ | ||
for(equal_cluster_idt index = 0; | ||
index != initialization_config.pointers_to_treat_equal.size(); | ||
++index) | ||
{ | ||
if(initialization_config.pointers_to_treat_equal[index].count(name) != 0) | ||
return index; | ||
} | ||
return {}; | ||
} | ||
|
||
bool recursive_initializationt::is_array_size_parameter( | ||
const irep_idt &cmdline_arg) const | ||
{ | ||
|
@@ -763,3 +817,49 @@ code_blockt recursive_initializationt::build_dynamic_array_constructor( | |
|
||
return body; | ||
} | ||
|
||
bool recursive_initializationt::needs_freeing(const exprt &expr) const | ||
{ | ||
return expr.type().id() == ID_pointer && expr.type().id() != ID_code; | ||
} | ||
|
||
void recursive_initializationt::free_if_possible( | ||
const exprt &expr, | ||
code_blockt &body) | ||
{ | ||
PRECONDITION(expr.id() == ID_symbol); | ||
const auto expr_id = to_symbol_expr(expr).get_identifier(); | ||
const auto maybe_cluster_index = find_equal_cluster(expr_id); | ||
const auto call_free = code_function_callt{get_free_function(), {expr}}; | ||
if(!maybe_cluster_index.has_value()) | ||
{ | ||
// not in any equality cluster -> just free | ||
body.add(call_free); | ||
return; | ||
} | ||
|
||
if( | ||
to_symbol_expr(*common_arguments_origins[*maybe_cluster_index]) | ||
.get_identifier() != expr_id && | ||
initialization_config.arguments_may_be_equal) | ||
{ | ||
// in equality cluster but not common origin -> free if not equal to origin | ||
const auto condition = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don’t quite understand the logic, shouldn’t it be the other way round (i.e. don’t free if not common origin, unless your value is different than origin)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Off by negation: good catch. Fixed. |
||
notequal_exprt{expr, *common_arguments_origins[*maybe_cluster_index]}; | ||
body.add(code_ifthenelset{condition, call_free}); | ||
} | ||
else | ||
{ | ||
// expr is common origin, leave freeing until the rest of the cluster is | ||
// freed | ||
return; | ||
} | ||
} | ||
|
||
void recursive_initializationt::free_cluster_origins(code_blockt &body) | ||
{ | ||
for(auto const &origin : common_arguments_origins) | ||
{ | ||
body.add(code_function_callt{get_free_function(), {*origin}}); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super minor nitpick that
;
might not necessarily be the best separator to use here because on most shells it’s also a terminator, but tbh I don’t think it’s big enough of a problem to worry about here.