Skip to content

Commit 71d9cf1

Browse files
Petr BauchPetr Bauch
Petr Bauch
authored and
Petr Bauch
committed
Well-formedness check for function calls and returns
Check that returned types are matching.
1 parent 0787063 commit 71d9cf1

File tree

5 files changed

+158
-0
lines changed

5 files changed

+158
-0
lines changed

src/goto-programs/goto_program.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,10 @@ void goto_programt::instructiont::validate(
713713
case ATOMIC_END:
714714
break;
715715
case RETURN:
716+
DATA_CHECK_WITH_DIAGNOSTICS(
717+
code.get_statement() == ID_return,
718+
"return instruction should contain a return statement",
719+
source_location);
716720
break;
717721
case ASSIGN:
718722
DATA_CHECK(
@@ -743,6 +747,10 @@ void goto_programt::instructiont::validate(
743747
source_location);
744748
break;
745749
case FUNCTION_CALL:
750+
DATA_CHECK_WITH_DIAGNOSTICS(
751+
code.get_statement() == ID_function_call,
752+
"function call instruction should contain a call statement",
753+
source_location);
746754
break;
747755
case THROW:
748756
break;

src/util/std_code.h

+57
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,47 @@ class code_function_callt:public codet
10801080
{
10811081
return op2().operands();
10821082
}
1083+
1084+
static void check(const codet& code,
1085+
const validation_modet vm = validation_modet::INVARIANT)
1086+
{
1087+
DATA_CHECK(
1088+
code.operands().size() == 3,
1089+
"function calls must have three operands:\n1) expression to store the "
1090+
"returned values\n2) the function being called\n3) the vector of "
1091+
"arguments");
1092+
}
1093+
1094+
static void validate(
1095+
const codet &code,
1096+
const namespacet &ns,
1097+
const validation_modet vm = validation_modet::INVARIANT)
1098+
{
1099+
check(code, vm);
1100+
1101+
if(code.op0().id() == ID_nil)
1102+
DATA_CHECK(
1103+
to_code_type(code.op1().type()).return_type().id() == ID_empty,
1104+
"void function should not return value");
1105+
else
1106+
DATA_CHECK(
1107+
base_type_eq(
1108+
code.op0().type(), to_code_type(code.op1().type()).return_type(), ns),
1109+
"function returns expression of wrong type");
1110+
}
1111+
1112+
static void validate_full(
1113+
const codet &code,
1114+
const namespacet &ns,
1115+
const validation_modet vm = validation_modet::INVARIANT)
1116+
{
1117+
for(const exprt &op : code.operands())
1118+
{
1119+
validate_full_expr(op, ns, vm);
1120+
}
1121+
1122+
validate(code, ns, vm);
1123+
}
10831124
};
10841125

10851126
template<> inline bool can_cast_expr<code_function_callt>(const exprt &base)
@@ -1102,6 +1143,11 @@ inline code_function_callt &to_code_function_call(codet &code)
11021143
return static_cast<code_function_callt &>(code);
11031144
}
11041145

1146+
inline void validate_expr(const code_function_callt &x)
1147+
{
1148+
validate_operands(x, 3, "function calls must have three operands");
1149+
}
1150+
11051151
/// \ref codet representation of a "return from a function" statement.
11061152
class code_returnt:public codet
11071153
{
@@ -1133,6 +1179,12 @@ class code_returnt:public codet
11331179
return false; // backwards compatibility
11341180
return return_value().is_not_nil();
11351181
}
1182+
1183+
static void check(const codet &code,
1184+
const validation_modet vm = validation_modet::INVARIANT)
1185+
{
1186+
DATA_CHECK(code.operands().size() == 1, "return must have one operand");
1187+
}
11361188
};
11371189

11381190
template<> inline bool can_cast_expr<code_returnt>(const exprt &base)
@@ -1155,6 +1207,11 @@ inline code_returnt &to_code_return(codet &code)
11551207
return static_cast<code_returnt &>(code);
11561208
}
11571209

1210+
inline void validate_expr(const code_returnt &x)
1211+
{
1212+
validate_operands(x, 1, "return must have one operand");
1213+
}
1214+
11581215
/// \ref codet representation of a label for branch targets.
11591216
class code_labelt:public codet
11601217
{

src/util/validate_code.cpp

+8
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

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ SRC += analyses/ai/ai.cpp \
1818
goto-programs/goto_trace_output.cpp \
1919
goto-programs/goto_program_declaration.cpp \
2020
goto-programs/goto_program_assume.cpp \
21+
goto-programs/goto_program_function_call.cpp \
2122
interpreter/interpreter.cpp \
2223
json/json_parser.cpp \
2324
path_strategies.cpp \
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)