Skip to content

Move __CPROVER_uninterpreted_* conversion to C type checker #8280

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

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ CORE
uninterpreted.c

missing type information required to construct call to uninterpreted function
^EXIT=70$
^EXIT=(1|64)$
^SIGNAL=0$
--
^warning: ignoring
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include <ctype.h>

int __CPROVER_uninterpreted_tolower(int);

// clang-format off
void tl1(char *dst, __CPROVER_size_t len)
__CPROVER_requires(__CPROVER_is_fresh(dst, len))
__CPROVER_assigns(__CPROVER_object_whole(dst))
__CPROVER_ensures(__CPROVER_forall {
int i;
(0 <= i && i < len) ==> dst[i % len] ==
__CPROVER_uninterpreted_tolower(__CPROVER_old(dst[i % len]))
});
// clang-format on

void tl1(char *dst, __CPROVER_size_t len)
{
for(__CPROVER_size_t i = 0; i < len; i++)
{
dst[i] = tolower(dst[i]);
}
}

int main()
{
char st[8] = "HELLOROD";
tl1(st, 8);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CORE dfcc-only
main.c
--dfcc main --enforce-contract tl1
^\[tl1.postcondition.1\] line \d+ Check ensures clause of contract contract::tl1 for function tl1: FAILURE$
^\*\* 1 of \d+ failed
^VERIFICATION FAILED$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
--
The purpose of this test is to ensure that we can use uninterpreted functions
within quantifiers.
21 changes: 19 additions & 2 deletions src/ansi-c/c_typecheck_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ void c_typecheck_baset::typecheck_symbol(symbolt &symbol)
{
if(
(!old_it->second.is_static_lifetime || !symbol.is_static_lifetime) &&
symbol.type.id() != ID_code)
(symbol.type.id() != ID_code &&
symbol.type.id() != ID_mathematical_function))
{
error().source_location = symbol.location;
error() << "redeclaration of '" << symbol.display_name()
Expand Down Expand Up @@ -346,8 +347,12 @@ void c_typecheck_baset::typecheck_redefinition_non_type(
}

// do initializer, this may change the type
if(new_symbol.type.id() != ID_code && !new_symbol.is_macro)
if(
new_symbol.type.id() != ID_code &&
new_symbol.type.id() != ID_mathematical_function && !new_symbol.is_macro)
{
do_initializer(new_symbol);
}

const typet &final_new = new_symbol.type;

Expand Down Expand Up @@ -871,6 +876,18 @@ void c_typecheck_baset::typecheck_declaration(
irep_idt identifier=symbol.name;
declarator.set_name(identifier);

if(
symbol.type.id() == ID_code &&
identifier.starts_with(CPROVER_PREFIX "uninterpreted_"))
{
const code_typet &function_call_type = to_code_type(symbol.type);
mathematical_function_typet::domaint domain;
for(const auto &parameter : function_call_type.parameters())
domain.push_back(parameter.type());
symbol.type =
mathematical_function_typet{domain, function_call_type.return_type()};
}

typecheck_symbol(symbol);

// check the contract, if any
Expand Down
25 changes: 18 additions & 7 deletions src/ansi-c/c_typecheck_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2424,6 +2424,16 @@
}
else
{
if(identifier.starts_with(CPROVER_PREFIX "uninterpreted_"))
{
throw invalid_source_file_exceptiont{
"'" + id2string(identifier) +
"' is not declared, "
"missing type information required to construct call to "
"uninterpreted function",
expr.source_location()};
}

// This is an undeclared function that's not a builtin.
// Let's just add it.
// We do a bit of return-type guessing, but just a bit.
Expand Down Expand Up @@ -2480,13 +2490,14 @@
for(auto &p :
make_range(expr.arguments()).zip(mathematical_function_type.domain()))
{
if(p.first.type() != p.second)
{
error().source_location = p.first.source_location();
error() << "expected argument of type " << to_string(p.second)
<< " but got " << to_string(p.first.type()) << eom;
throw 0;
}
implicit_typecast(p.first, p.second);
}

if(f_op.id() != ID_lambda && f_op.id() != ID_symbol)
{
throw invalid_source_file_exceptiont{
"expected function symbol or lambda, but got " + id2string(f_op.id()),
f_op.source_location()};

Check warning on line 2500 in src/ansi-c/c_typecheck_expr.cpp

View check run for this annotation

Codecov / codecov/patch

src/ansi-c/c_typecheck_expr.cpp#L2498-L2500

Added lines #L2498 - L2500 were not covered by tests
}

function_application_exprt function_application(f_op, expr.arguments());
Expand Down
30 changes: 0 additions & 30 deletions src/ansi-c/goto-conversion/builtin_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ Author: Daniel Kroening, [email protected]
#include <util/expr_initializer.h>
#include <util/expr_util.h>
#include <util/fresh_symbol.h>
#include <util/mathematical_expr.h>
#include <util/mathematical_types.h>
#include <util/pointer_expr.h>
#include <util/rational.h>
#include <util/rational_tools.h>
Expand Down Expand Up @@ -1089,34 +1087,6 @@ void goto_convertt::do_function_call_symbol(
assignment.add_source_location() = function.source_location();
copy(assignment, ASSIGN, dest);
}
else if(identifier.starts_with(CPROVER_PREFIX "uninterpreted_"))
{
// make it a side effect if there is an LHS
if(lhs.is_nil())
return;

if(function.type().get_bool(ID_C_incomplete))
{
error().source_location = function.find_source_location();
error() << "'" << identifier << "' is not declared, "
<< "missing type information required to construct call to "
<< "uninterpreted function" << eom;
throw 0;
}

const code_typet &function_call_type = to_code_type(function.type());
mathematical_function_typet::domaint domain;
for(const auto &parameter : function_call_type.parameters())
domain.push_back(parameter.type());
mathematical_function_typet function_type{
domain, function_call_type.return_type()};
const function_application_exprt rhs(
symbol_exprt{function.get_identifier(), function_type}, arguments);

code_assignt assignment(lhs, rhs);
assignment.add_source_location() = function.source_location();
copy(assignment, ASSIGN, dest);
}
else if(identifier == CPROVER_PREFIX "array_equal")
{
do_array_op(ID_array_equal, lhs, function, arguments, dest);
Expand Down
Loading