Skip to content

Commit 7c34ea4

Browse files
Handle the case of null arguments for format
1 parent 8fc7988 commit 7c34ea4

File tree

1 file changed

+43
-14
lines changed

1 file changed

+43
-14
lines changed

src/solvers/strings/string_constraint_generator_format.cpp

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,18 @@ static std::vector<format_elementt> parse_format_string(std::string s)
239239
return al;
240240
}
241241

242+
/// Expression which is true when the string is equal to the literal "null"
243+
static exprt is_null(const array_string_exprt &string)
244+
{
245+
return and_exprt{
246+
equal_exprt{string.length(), from_integer(4, string.length().type())},
247+
and_exprt{
248+
equal_exprt{string[0], from_integer('n', string[0].type())},
249+
equal_exprt{string[1], from_integer('u', string[0].type())},
250+
equal_exprt{string[2], from_integer('l', string[0].type())},
251+
equal_exprt{string[3], from_integer('l', string[0].type())}}};
252+
}
253+
242254
/// Parse `s` and add axioms ensuring the output corresponds to the output of
243255
/// String.format. Assumes the argument is a string representing one of:
244256
/// string expr, int, float, char, boolean, hashcode, date_time.
@@ -286,8 +298,30 @@ add_axioms_for_format_specifier(
286298
fresh_symbol, res, get_arg(ID_float), array_pool, ns);
287299
return {res, std::move(return_code.second)};
288300
case format_specifiert::CHARACTER:
289-
return_code = add_axioms_from_char(res, get_arg(ID_char));
290-
return {res, std::move(return_code.second)};
301+
{
302+
exprt arg_string = get_arg(ID_char);
303+
array_string_exprt &string_expr = to_array_string_expr(arg_string);
304+
// In the case the arg is null, the result will be equal to "null" and
305+
// and otherwise we just take the 4th character of the string.
306+
const exprt is_null_literal = is_null(string_expr);
307+
constraints.existential.push_back(equal_exprt{
308+
res.length(),
309+
if_exprt{
310+
is_null_literal,
311+
from_integer(4, index_type),
312+
from_integer(1, index_type)}});
313+
constraints.existential.push_back(implies_exprt{
314+
is_null_literal,
315+
and_exprt{
316+
equal_exprt{res[0], from_integer('n', char_type)},
317+
equal_exprt{res[1], from_integer('u', char_type)},
318+
equal_exprt{res[2], from_integer('l', char_type)},
319+
equal_exprt{res[3], from_integer('l', char_type)}}});
320+
constraints.existential.push_back(implies_exprt{
321+
not_exprt{is_null_literal},
322+
equal_exprt{res[0], typecast_exprt{string_expr[3], char_type}}});
323+
return {res, constraints};
324+
}
291325
case format_specifiert::BOOLEAN:
292326
return_code = add_axioms_from_bool(res, get_arg(ID_boolean));
293327
return {res, std::move(return_code.second)};
@@ -378,23 +412,18 @@ static exprt format_arg_from_string(
378412
shl_exprt{typecast_exprt{string[2], type}, 16},
379413
typecast_exprt{string[3], type}}};
380414
}
415+
381416
if(id == ID_char)
382417
{
383-
// We assume the string has length exactly 4 and ignore the first 3
384-
// (unsigned16)string.content[3]
385-
const unsignedbv_typet type{16};
386-
return typecast_exprt{
387-
index_exprt{string.content(), from_integer(3, string.length().type())},
388-
type};
418+
// Leave the string unchanged as the "null" string is used to represent null
419+
return string;
389420
}
390421
if(id == ID_boolean)
391422
{
392-
// We assume the string has length exactly 4 and ignore the first 3
393-
// (bool)string.content[3]
394-
const c_bool_typet type{8};
395-
return typecast_exprt{
396-
index_exprt{string.content(), from_integer(3, string.length().type())},
397-
type};
423+
// We assume the string has length exactly 4, if it is "null" return false
424+
// and otherwise ignore the first 3 and return (bool)string.content[3]
425+
return if_exprt{
426+
is_null(string), false_exprt{}, typecast_exprt{string[3], bool_typet()}};
398427
}
399428
if(id == ID_float)
400429
{

0 commit comments

Comments
 (0)