Skip to content
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

[TG-2608] Use string dependencies to reduce the number of constraints #1947

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
54a4d7d
Add class for builtin function with no eval
romainbrenguier Mar 15, 2018
c26b83d
Constructor for builtin function with no eval
romainbrenguier Mar 15, 2018
408adbe
never return nullptr in to_string_builtin_function
romainbrenguier Mar 15, 2018
2e7057a
Remove use of unknown_dependency
romainbrenguier Mar 15, 2018
82f552c
Add and add_constraint method to builtin functions
romainbrenguier Mar 15, 2018
b8df2b3
Initialize return_code for all builtin_functions
romainbrenguier Mar 15, 2018
8a7d194
add_constraints method in string_dependencies
romainbrenguier Mar 15, 2018
41c0294
Add constraints from the string dependencies
romainbrenguier Mar 15, 2018
97ee2d6
Store builtin function pointer inside builtin node
romainbrenguier Mar 16, 2018
6609247
Add a maybe_testing_function to builtin functions
romainbrenguier Mar 16, 2018
46d2507
Add move constructors to builtin_function_nodet
romainbrenguier Mar 20, 2018
c487c4b
Generic get_reachable function
romainbrenguier Mar 19, 2018
23f8cd4
Correct name of builtin function with no eval
romainbrenguier Mar 22, 2018
3c61cf2
Add a hash function for nodes
romainbrenguier Mar 19, 2018
4b342f7
Only add constraints on test function dependencies
romainbrenguier Mar 19, 2018
be5f194
Add double quotes to output_dot
romainbrenguier Mar 23, 2018
2655a9b
Rewrite axioms for insert
romainbrenguier Mar 22, 2018
e3da98c
Deactivating invariant
romainbrenguier Mar 23, 2018
a45c64f
Add tests for the use of string dependencies
romainbrenguier Mar 19, 2018
c3aed85
Define two helper functions for for_each_successor
romainbrenguier Mar 23, 2018
8a98246
Adapt unit tests for the new interface
romainbrenguier Mar 22, 2018
8684e6d
Helper functions for length constraints on strings
romainbrenguier Mar 23, 2018
61b3910
Add length_constraint method to builtin functions
romainbrenguier Mar 23, 2018
805f45f
Correct add_constraints in string_dependencies
romainbrenguier Mar 23, 2018
0bb2fad
Define a for_each_atomic_string helper function
romainbrenguier Mar 23, 2018
367ca13
Ensure all atomic strings in arguments have node
romainbrenguier Mar 23, 2018
50cfe34
Clear constraints at each call to dec_solve
romainbrenguier Mar 24, 2018
60bfbd0
Reduce string-max-length on String.equals test
romainbrenguier Mar 24, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file not shown.
22 changes: 22 additions & 0 deletions regression/jbmc-strings/StringDependencies/Test.java
@@ -0,0 +1,22 @@
class Test {
void test(String s) {
// Filter
if(s == null)
return;

// Act

// this matters for the final test
String t = s.concat("_foo");

// these should be ignored by the solver as they
// do not matter for the final test
String u = s.concat("_bar");
String v = s.concat("_baz");
String w = s.concat("_fiz");
String x = s.concat("_buz");

// Assert
assert t.endsWith("foo");
}
}
12 changes: 12 additions & 0 deletions regression/jbmc-strings/StringDependencies/test.desc
@@ -0,0 +1,12 @@
CORE
Test.class
--refine-strings --string-max-length 1000 --string-max-input-length 100 --verbosity 10 Test.class --function Test.test
^EXIT=0$
^SIGNAL=0$
assertion at file Test.java line 20 .*: SUCCESS
string_refinement::check_axioms: 3 universal axioms
--
--
We check there are exactly 3 universal formulas considered by the solver (2 for
`t = s.concat("_foo")` and 1 for `t.endsWith("foo")`).
The other concatenations should be ignored.
2 changes: 1 addition & 1 deletion regression/jbmc-strings/StringEquals/test.desc
@@ -1,6 +1,6 @@
CORE
Test.class
--refine-strings --string-max-length 1000 --function Test.check
--refine-strings --string-max-length 40 --function Test.check
^EXIT=10$
^SIGNAL=0$
assertion at file Test.java line 6 .* SUCCESS
Expand Down
36 changes: 36 additions & 0 deletions src/solvers/refinement/string_builtin_function.cpp
Expand Up @@ -18,8 +18,10 @@ static array_string_exprt make_string(

string_transformation_builtin_functiont::
string_transformation_builtin_functiont(
const exprt &return_code,
const std::vector<exprt> &fun_args,
array_poolt &array_pool)
: string_builtin_functiont(return_code)
{
PRECONDITION(fun_args.size() > 2);
const auto arg1 = expr_checked_cast<struct_exprt>(fun_args[2]);
Expand Down Expand Up @@ -55,8 +57,10 @@ optionalt<exprt> string_transformation_builtin_functiont::eval(
}

string_insertion_builtin_functiont::string_insertion_builtin_functiont(
const exprt &return_code,
const std::vector<exprt> &fun_args,
array_poolt &array_pool)
: string_builtin_functiont(return_code)
{
PRECONDITION(fun_args.size() > 4);
const auto arg1 = expr_checked_cast<struct_exprt>(fun_args[2]);
Expand All @@ -69,8 +73,10 @@ string_insertion_builtin_functiont::string_insertion_builtin_functiont(
}

string_concatenation_builtin_functiont::string_concatenation_builtin_functiont(
const exprt &return_code,
const std::vector<exprt> &fun_args,
array_poolt &array_pool)
: string_insertion_builtin_functiont(return_code)
{
PRECONDITION(fun_args.size() >= 4 && fun_args.size() <= 6);
const auto arg1 = expr_checked_cast<struct_exprt>(fun_args[2]);
Expand Down Expand Up @@ -207,3 +213,33 @@ optionalt<exprt> string_insertion_builtin_functiont::eval(
const array_typet type(result.type().subtype(), length);
return make_string(result_value, type);
}

string_builtin_function_with_no_evalt::string_builtin_function_with_no_evalt(
const exprt &return_code,
const function_application_exprt &f,
array_poolt &array_pool)
: string_builtin_functiont(return_code), function_application(f)
{
const std::vector<exprt> &fun_args = f.arguments();
std::size_t i = 0;
if(fun_args.size() >= 2 && fun_args[1].type().id() == ID_pointer)
{
string_res = array_pool.find(fun_args[1], fun_args[0]);
i = 2;
}

for(; i < fun_args.size(); ++i)
{
const auto arg = expr_try_dynamic_cast<struct_exprt>(fun_args[i]);
// TODO: use is_refined_string_type ?
if(
arg && arg->operands().size() == 2 &&
arg->op1().type().id() == ID_pointer)
{
INVARIANT(is_refined_string_type(arg->type()), "should be a string");
string_args.push_back(array_pool.find(arg->op1(), arg->op0()));
}
else
args.push_back(fun_args[i]);
}
}
153 changes: 148 additions & 5 deletions src/solvers/refinement/string_builtin_function.h
Expand Up @@ -7,6 +7,7 @@
#include <vector>
#include <util/optional.h>
#include <util/string_expr.h>
#include "string_constraint_generator.h"

class array_poolt;

Expand All @@ -32,8 +33,33 @@ class string_builtin_functiont

virtual std::string name() const = 0;

protected:
/// Add constraints ensuring that the value of result expression of the
/// builtin function corresponds to the value of the function call.
virtual exprt
add_constraints(string_constraint_generatort &constraint_generator) const = 0;

/// Constraint ensuring that the length of the strings are coherent with
/// the function call.
virtual exprt length_constraint() const = 0;

exprt return_code;

/// Tells whether the builtin function can be a testing function, that is a
/// function that does not return a string, for instance like `equals`,
/// `indexOf` or `compare`.
virtual bool maybe_testing_function() const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why maybe and not is?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the functions that don't have yet a dedicated builtin_function class we consider that could be testing function although we don't actually know. Ultimately this can be replaced by a is

{
return true;
}

private:
string_builtin_functiont() = default;

protected:
explicit string_builtin_functiont(const exprt &return_code)
: return_code(return_code)
{
}
};

/// String builtin_function transforming one string into another
Expand All @@ -43,14 +69,14 @@ class string_transformation_builtin_functiont : public string_builtin_functiont
array_string_exprt result;
array_string_exprt input;
std::vector<exprt> args;
exprt return_code;

/// Constructor from arguments of a function application.
/// The arguments in `fun_args` should be in order:
/// an integer `result.length`, a character pointer `&result[0]`,
/// a string `arg1` of type refined_string_typet, and potentially some
/// arguments of primitive types.
string_transformation_builtin_functiont(
const exprt &return_code,
const std::vector<exprt> &fun_args,
array_poolt &array_pool);

Expand All @@ -70,6 +96,11 @@ class string_transformation_builtin_functiont : public string_builtin_functiont

optionalt<exprt>
eval(const std::function<exprt(const exprt &)> &get_value) const override;

bool maybe_testing_function() const override
{
return false;
}
};

/// Adding a character at the end of a string
Expand All @@ -82,9 +113,10 @@ class string_concat_char_builtin_functiont
/// an integer `result.length`, a character pointer `&result[0]`,
/// a string `arg1` of type refined_string_typet, and a character.
string_concat_char_builtin_functiont(
const exprt &return_code,
const std::vector<exprt> &fun_args,
array_poolt &array_pool)
: string_transformation_builtin_functiont(fun_args, array_pool)
: string_transformation_builtin_functiont(return_code, fun_args, array_pool)
{
}

Expand All @@ -96,6 +128,16 @@ class string_concat_char_builtin_functiont
{
return "concat_char";
}

exprt add_constraints(string_constraint_generatort &generator) const override
{
return generator.add_axioms_for_concat_char(result, input, args[0]);
}

exprt length_constraint() const override
{
return length_constraint_for_concat_char(result, input);
}
};

/// String inserting a string into another one
Expand All @@ -106,7 +148,6 @@ class string_insertion_builtin_functiont : public string_builtin_functiont
array_string_exprt input1;
array_string_exprt input2;
std::vector<exprt> args;
exprt return_code;

/// Constructor from arguments of a function application.
/// The arguments in `fun_args` should be in order:
Expand All @@ -115,6 +156,7 @@ class string_insertion_builtin_functiont : public string_builtin_functiont
/// a string `arg2` of type refined_string_typet,
/// and potentially some arguments of primitive types.
string_insertion_builtin_functiont(
const exprt &return_code,
const std::vector<exprt> &fun_args,
array_poolt &array_pool);

Expand All @@ -141,8 +183,34 @@ class string_insertion_builtin_functiont : public string_builtin_functiont
return "insert";
}

exprt add_constraints(string_constraint_generatort &generator) const override
{
if(args.size() == 1)
return generator.add_axioms_for_insert(result, input1, input2, args[0]);
if(args.size() == 3)
UNIMPLEMENTED;
UNREACHABLE;
};

exprt length_constraint() const override
{
if(args.size() == 1)
return length_constraint_for_insert(result, input1, input2, args[0]);
if(args.size() == 3)
UNIMPLEMENTED;
UNREACHABLE;
};

bool maybe_testing_function() const override
{
return false;
}

protected:
string_insertion_builtin_functiont() = default;
explicit string_insertion_builtin_functiont(const exprt &return_code)
: string_builtin_functiont(return_code)
{
}
};

class string_concatenation_builtin_functiont final
Expand All @@ -156,6 +224,7 @@ class string_concatenation_builtin_functiont final
/// a string `arg2` of type refined_string_typet,
/// optionally followed by an integer `start` and an integer `end`.
string_concatenation_builtin_functiont(
const exprt &return_code,
const std::vector<exprt> &fun_args,
array_poolt &array_pool);

Expand All @@ -168,6 +237,26 @@ class string_concatenation_builtin_functiont final
{
return "concat";
}

exprt add_constraints(string_constraint_generatort &generator) const override
{
if(args.size() == 0)
return generator.add_axioms_for_concat(result, input1, input2);
if(args.size() == 2)
return generator.add_axioms_for_concat_substr(
result, input1, input2, args[0], args[1]);
UNREACHABLE;
};

exprt length_constraint() const override
{
if(args.size() == 0)
return length_constraint_for_concat(result, input1, input2);
if(args.size() == 2)
return length_constraint_for_concat_substr(
result, input1, input2, args[0], args[1]);
UNREACHABLE;
}
};

/// String creation from other types
Expand All @@ -182,6 +271,11 @@ class string_creation_builtin_functiont : public string_builtin_functiont
{
return result;
}

bool maybe_testing_function() const override
{
return false;
}
};

/// String test
Expand All @@ -197,4 +291,53 @@ class string_test_builtin_functiont : public string_builtin_functiont
}
};

/// Functions that are not yet supported in this class but are supported by
/// string_constraint_generatort.
/// \note Ultimately this should be disappear, once all builtin function have
/// a corresponding string_builtin_functiont class.
class string_builtin_function_with_no_evalt : public string_builtin_functiont
{
public:
function_application_exprt function_application;
optionalt<array_string_exprt> string_res;
std::vector<array_string_exprt> string_args;
std::vector<exprt> args;

string_builtin_function_with_no_evalt(
const exprt &return_code,
const function_application_exprt &f,
array_poolt &array_pool);

std::string name() const override
{
return id2string(function_application.function().get_identifier());
}
std::vector<array_string_exprt> string_arguments() const override
{
return std::vector<array_string_exprt>(string_args);
}
optionalt<array_string_exprt> string_result() const override
{
return string_res;
}

optionalt<exprt>
eval(const std::function<exprt(const exprt &)> &get_value) const override
{
return {};
}

exprt add_constraints(string_constraint_generatort &generator) const override
{
return generator.add_axioms_for_function_application(function_application);
};

exprt length_constraint() const override
{
// For now, there is no need for implementing that as `add_constraints`
// should always be called on these functions
UNIMPLEMENTED;
}
};

#endif // CPROVER_SOLVERS_REFINEMENT_STRING_BUILTIN_FUNCTION_H