Skip to content

Commit c742e85

Browse files
Petr BauchPetr Bauch
authored andcommitted
Well-formedness check for function calls and returns
Check that returned types are matching.
1 parent 909f897 commit c742e85

File tree

5 files changed

+165
-0
lines changed

5 files changed

+165
-0
lines changed

src/goto-programs/goto_program.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,11 @@ void goto_programt::instructiont::validate(
715715
case ATOMIC_END:
716716
break;
717717
case RETURN:
718+
DATA_CHECK_WITH_DIAGNOSTICS(
719+
vm,
720+
code.get_statement() == ID_return,
721+
"return instruction should contain a return statement",
722+
source_location);
718723
break;
719724
case ASSIGN:
720725
DATA_CHECK(
@@ -751,6 +756,11 @@ void goto_programt::instructiont::validate(
751756
source_location);
752757
break;
753758
case FUNCTION_CALL:
759+
DATA_CHECK_WITH_DIAGNOSTICS(
760+
vm,
761+
code.get_statement() == ID_function_call,
762+
"function call instruction should contain a call statement",
763+
source_location);
754764
break;
755765
case THROW:
756766
break;

src/util/std_code.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,51 @@ class code_function_callt:public codet
10841084
{
10851085
return op2().operands();
10861086
}
1087+
1088+
static void check(
1089+
const codet &code,
1090+
const validation_modet vm = validation_modet::INVARIANT)
1091+
{
1092+
DATA_CHECK(
1093+
vm,
1094+
code.operands().size() == 3,
1095+
"function calls must have three operands:\n1) expression to store the "
1096+
"returned values\n2) the function being called\n3) the vector of "
1097+
"arguments");
1098+
}
1099+
1100+
static void validate(
1101+
const codet &code,
1102+
const namespacet &ns,
1103+
const validation_modet vm = validation_modet::INVARIANT)
1104+
{
1105+
check(code, vm);
1106+
1107+
if(code.op0().id() == ID_nil)
1108+
DATA_CHECK(
1109+
vm,
1110+
to_code_type(code.op1().type()).return_type().id() == ID_empty,
1111+
"void function should not return value");
1112+
else
1113+
DATA_CHECK(
1114+
vm,
1115+
base_type_eq(
1116+
code.op0().type(), to_code_type(code.op1().type()).return_type(), ns),
1117+
"function returns expression of wrong type");
1118+
}
1119+
1120+
static void validate_full(
1121+
const codet &code,
1122+
const namespacet &ns,
1123+
const validation_modet vm = validation_modet::INVARIANT)
1124+
{
1125+
for(const exprt &op : code.operands())
1126+
{
1127+
validate_full_expr(op, ns, vm);
1128+
}
1129+
1130+
validate(code, ns, vm);
1131+
}
10871132
};
10881133

10891134
template<> inline bool can_cast_expr<code_function_callt>(const exprt &base)
@@ -1106,6 +1151,11 @@ inline code_function_callt &to_code_function_call(codet &code)
11061151
return static_cast<code_function_callt &>(code);
11071152
}
11081153

1154+
inline void validate_expr(const code_function_callt &x)
1155+
{
1156+
validate_operands(x, 3, "function calls must have three operands");
1157+
}
1158+
11091159
/// \ref codet representation of a "return from a function" statement.
11101160
class code_returnt:public codet
11111161
{
@@ -1137,6 +1187,13 @@ class code_returnt:public codet
11371187
return false; // backwards compatibility
11381188
return return_value().is_not_nil();
11391189
}
1190+
1191+
static void check(
1192+
const codet &code,
1193+
const validation_modet vm = validation_modet::INVARIANT)
1194+
{
1195+
DATA_CHECK(vm, code.operands().size() == 1, "return must have one operand");
1196+
}
11401197
};
11411198

11421199
template<> inline bool can_cast_expr<code_returnt>(const exprt &base)
@@ -1159,6 +1216,11 @@ inline code_returnt &to_code_return(codet &code)
11591216
return static_cast<code_returnt &>(code);
11601217
}
11611218

1219+
inline void validate_expr(const code_returnt &x)
1220+
{
1221+
validate_operands(x, 1, "return must have one operand");
1222+
}
1223+
11621224
/// \ref codet representation of a label for branch targets.
11631225
class code_labelt:public codet
11641226
{

src/util/validate_code.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ void call_on_code(const codet &code, Args &&... args)
3535
{
3636
CALL_ON_CODE(code_deadt);
3737
}
38+
else if(code.get_statement() == ID_function_call)
39+
{
40+
CALL_ON_CODE(code_function_callt);
41+
}
42+
else if(code.get_statement() == ID_return)
43+
{
44+
CALL_ON_CODE(code_returnt);
45+
}
3846
else
3947
{
4048
#ifdef REPORT_UNIMPLEMENTED_CODE_CHECKS

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ SRC += analyses/ai/ai.cpp \
1818
goto-programs/goto_program_assume.cpp \
1919
goto-programs/goto_program_dead.cpp \
2020
goto-programs/goto_program_declaration.cpp \
21+
goto-programs/goto_program_function_call.cpp \
2122
goto-programs/goto_trace_output.cpp \
2223
goto-symex/ssa_equation.cpp \
2324
interpreter/interpreter.cpp \
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*******************************************************************\
2+
3+
Module: Unit tests for goto_program::validate
4+
5+
Author: Diffblue Ltd.
6+
7+
\*******************************************************************/
8+
9+
#include <goto-programs/goto_function.h>
10+
#include <testing-utils/catch.hpp>
11+
#include <util/arith_tools.h>
12+
13+
SCENARIO(
14+
"Validation of well-formed function call codes",
15+
"[core][goto-programs][validate]")
16+
{
17+
GIVEN("A program with one function call")
18+
{
19+
symbol_tablet symbol_table;
20+
const signedbv_typet type1(32);
21+
const signedbv_typet type2(64);
22+
const code_typet code_type({}, type1);
23+
24+
symbolt var_symbol;
25+
irep_idt var_symbol_name = "a";
26+
var_symbol.name = var_symbol_name;
27+
symbol_exprt var_a(var_symbol_name, type1);
28+
29+
symbolt var_symbol2;
30+
irep_idt var_symbol_name2 = "b";
31+
var_symbol2.name = var_symbol_name2;
32+
symbol_exprt var_b(var_symbol_name2, type2);
33+
34+
symbolt fun_symbol;
35+
irep_idt fun_symbol_name = "foo";
36+
fun_symbol.name = fun_symbol_name;
37+
symbol_exprt fun_foo(fun_symbol_name, code_type);
38+
39+
goto_functiont goto_function;
40+
auto &instructions = goto_function.body.instructions;
41+
instructions.emplace_back(goto_program_instruction_typet::FUNCTION_CALL);
42+
43+
var_symbol.type = type1;
44+
var_symbol2.type = type2;
45+
fun_symbol.type = type1;
46+
symbol_table.insert(var_symbol);
47+
symbol_table.insert(var_symbol2);
48+
symbol_table.insert(fun_symbol);
49+
namespacet ns(symbol_table);
50+
51+
WHEN("Return type matches")
52+
{
53+
code_function_callt function_call(var_a, fun_foo, {});
54+
instructions.back().make_function_call(function_call);
55+
REQUIRE(instructions.back().code.get_statement() == ID_function_call);
56+
57+
THEN("The consistency check succeeds")
58+
{
59+
goto_function.body.validate(ns, validation_modet::INVARIANT);
60+
REQUIRE(true);
61+
}
62+
}
63+
64+
WHEN("Return type differs from function type")
65+
{
66+
code_function_callt function_call(var_b, fun_foo, {});
67+
instructions.back().make_function_call(function_call);
68+
69+
THEN("The consistency check fails")
70+
{
71+
bool caught = false;
72+
try
73+
{
74+
goto_function.body.validate(ns, validation_modet::EXCEPTION);
75+
}
76+
catch(incorrect_goto_program_exceptiont &e)
77+
{
78+
caught = true;
79+
}
80+
REQUIRE(caught);
81+
}
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)