Skip to content

Commit 116c66d

Browse files
committed
Move __CPROVER_uninterpreted_* conversion to C type checker
We already had type checking of mathematical_function types to support lambda expressions. Special-case symbols with name `__CPROVER_uninterpreted_*` will now also produce these types. GOTO conversion will no longer give specific treatment to function calls with symbol name `__CPROVER_uninterpreted_*`.
1 parent 8bab263 commit 116c66d

File tree

6 files changed

+79
-40
lines changed

6 files changed

+79
-40
lines changed

regression/ansi-c/undeclared_function/undeclared_uninterpreted.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ CORE
22
uninterpreted.c
33

44
missing type information required to construct call to uninterpreted function
5-
^EXIT=70$
5+
^EXIT=(1|64)$
66
^SIGNAL=0$
77
--
88
^warning: ignoring
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include <ctype.h>
2+
3+
int __CPROVER_uninterpreted_tolower(int);
4+
5+
// clang-format off
6+
void tl1(char *dst, __CPROVER_size_t len)
7+
__CPROVER_requires(__CPROVER_is_fresh(dst, len))
8+
__CPROVER_assigns(__CPROVER_object_whole(dst))
9+
__CPROVER_ensures(__CPROVER_forall {
10+
int i;
11+
(0 <= i && i < len) ==> dst[i % len] ==
12+
__CPROVER_uninterpreted_tolower(__CPROVER_old(dst[i % len]))
13+
});
14+
// clang-format on
15+
16+
void tl1(char *dst, __CPROVER_size_t len)
17+
{
18+
for(__CPROVER_size_t i = 0; i < len; i++)
19+
{
20+
dst[i] = tolower(dst[i]);
21+
}
22+
}
23+
24+
int main()
25+
{
26+
char st[8] = "HELLOROD";
27+
tl1(st, 8);
28+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE dfcc-only
2+
main.c
3+
--dfcc main --enforce-contract tl1
4+
^\[tl1.postcondition.1\] line \d+ Check ensures clause of contract contract::tl1 for function tl1: FAILURE$
5+
^\*\* 1 of \d+ failed
6+
^VERIFICATION FAILED$
7+
^EXIT=10$
8+
^SIGNAL=0$
9+
--
10+
^warning: ignoring
11+
--
12+
The purpose of this test is to ensure that we can use uninterpreted functions
13+
within quantifiers.

src/ansi-c/c_typecheck_base.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ void c_typecheck_baset::typecheck_symbol(symbolt &symbol)
142142
{
143143
if(
144144
(!old_it->second.is_static_lifetime || !symbol.is_static_lifetime) &&
145-
symbol.type.id() != ID_code)
145+
(symbol.type.id() != ID_code &&
146+
symbol.type.id() != ID_mathematical_function))
146147
{
147148
error().source_location = symbol.location;
148149
error() << "redeclaration of '" << symbol.display_name()
@@ -346,8 +347,12 @@ void c_typecheck_baset::typecheck_redefinition_non_type(
346347
}
347348

348349
// do initializer, this may change the type
349-
if(new_symbol.type.id() != ID_code && !new_symbol.is_macro)
350+
if(
351+
new_symbol.type.id() != ID_code &&
352+
new_symbol.type.id() != ID_mathematical_function && !new_symbol.is_macro)
353+
{
350354
do_initializer(new_symbol);
355+
}
351356

352357
const typet &final_new = new_symbol.type;
353358

@@ -871,6 +876,18 @@ void c_typecheck_baset::typecheck_declaration(
871876
irep_idt identifier=symbol.name;
872877
declarator.set_name(identifier);
873878

879+
if(
880+
symbol.type.id() == ID_code &&
881+
identifier.starts_with(CPROVER_PREFIX "uninterpreted_"))
882+
{
883+
const code_typet &function_call_type = to_code_type(symbol.type);
884+
mathematical_function_typet::domaint domain;
885+
for(const auto &parameter : function_call_type.parameters())
886+
domain.push_back(parameter.type());
887+
symbol.type =
888+
mathematical_function_typet{domain, function_call_type.return_type()};
889+
}
890+
874891
typecheck_symbol(symbol);
875892

876893
// check the contract, if any

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2424,6 +2424,16 @@ void c_typecheck_baset::typecheck_side_effect_function_call(
24242424
}
24252425
else
24262426
{
2427+
if(identifier.starts_with(CPROVER_PREFIX "uninterpreted_"))
2428+
{
2429+
throw invalid_source_file_exceptiont{
2430+
"'" + id2string(identifier) +
2431+
"' is not declared, "
2432+
"missing type information required to construct call to "
2433+
"uninterpreted function",
2434+
expr.source_location()};
2435+
}
2436+
24272437
// This is an undeclared function that's not a builtin.
24282438
// Let's just add it.
24292439
// We do a bit of return-type guessing, but just a bit.
@@ -2480,13 +2490,14 @@ void c_typecheck_baset::typecheck_side_effect_function_call(
24802490
for(auto &p :
24812491
make_range(expr.arguments()).zip(mathematical_function_type.domain()))
24822492
{
2483-
if(p.first.type() != p.second)
2484-
{
2485-
error().source_location = p.first.source_location();
2486-
error() << "expected argument of type " << to_string(p.second)
2487-
<< " but got " << to_string(p.first.type()) << eom;
2488-
throw 0;
2489-
}
2493+
implicit_typecast(p.first, p.second);
2494+
}
2495+
2496+
if(f_op.id() != ID_lambda && f_op.id() != ID_symbol)
2497+
{
2498+
throw invalid_source_file_exceptiont{
2499+
"expected function symbol or lambda, but got " + id2string(f_op.id()),
2500+
f_op.source_location()};
24902501
}
24912502

24922503
function_application_exprt function_application(f_op, expr.arguments());

src/ansi-c/goto-conversion/builtin_functions.cpp

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ Author: Daniel Kroening, [email protected]
1717
#include <util/expr_initializer.h>
1818
#include <util/expr_util.h>
1919
#include <util/fresh_symbol.h>
20-
#include <util/mathematical_expr.h>
21-
#include <util/mathematical_types.h>
2220
#include <util/pointer_expr.h>
2321
#include <util/rational.h>
2422
#include <util/rational_tools.h>
@@ -1089,34 +1087,6 @@ void goto_convertt::do_function_call_symbol(
10891087
assignment.add_source_location() = function.source_location();
10901088
copy(assignment, ASSIGN, dest);
10911089
}
1092-
else if(identifier.starts_with(CPROVER_PREFIX "uninterpreted_"))
1093-
{
1094-
// make it a side effect if there is an LHS
1095-
if(lhs.is_nil())
1096-
return;
1097-
1098-
if(function.type().get_bool(ID_C_incomplete))
1099-
{
1100-
error().source_location = function.find_source_location();
1101-
error() << "'" << identifier << "' is not declared, "
1102-
<< "missing type information required to construct call to "
1103-
<< "uninterpreted function" << eom;
1104-
throw 0;
1105-
}
1106-
1107-
const code_typet &function_call_type = to_code_type(function.type());
1108-
mathematical_function_typet::domaint domain;
1109-
for(const auto &parameter : function_call_type.parameters())
1110-
domain.push_back(parameter.type());
1111-
mathematical_function_typet function_type{
1112-
domain, function_call_type.return_type()};
1113-
const function_application_exprt rhs(
1114-
symbol_exprt{function.get_identifier(), function_type}, arguments);
1115-
1116-
code_assignt assignment(lhs, rhs);
1117-
assignment.add_source_location() = function.source_location();
1118-
copy(assignment, ASSIGN, dest);
1119-
}
11201090
else if(identifier == CPROVER_PREFIX "array_equal")
11211091
{
11221092
do_array_op(ID_array_equal, lhs, function, arguments, dest);

0 commit comments

Comments
 (0)