Skip to content

Commit 495c0d8

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 c3e220f commit 495c0d8

File tree

5 files changed

+174
-0
lines changed

5 files changed

+174
-0
lines changed

src/goto-programs/goto_program.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,18 @@ void goto_programt::instructiont::validate(
748748
"asserting non-boolean condition\n" + guard.pretty(),
749749
source_location);
750750
break;
751+
case FUNCTION_CALL:
752+
DATA_CHECK_WITH_DIAGNOSTICS(
753+
code.get_statement() == ID_function_call,
754+
"function call instruction should contain a call statement",
755+
source_location);
756+
break;
757+
case RETURN:
758+
DATA_CHECK_WITH_DIAGNOSTICS(
759+
code.get_statement() == ID_return,
760+
"return instruction should contain a return statement",
761+
source_location);
762+
break;
751763
case DEAD:
752764
DATA_CHECK_WITH_DIAGNOSTICS(
753765
code.get_statement() == ID_dead,

src/util/std_code.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Author: Daniel Kroening, [email protected]
1616
#include "expr.h"
1717
#include "expr_cast.h"
1818
#include "invariant.h"
19+
#include "std_types.h"
1920
#include "validate.h"
2021
#include "validate_code.h"
2122

@@ -1101,6 +1102,40 @@ class code_function_callt:public codet
11011102
{
11021103
return op2().operands();
11031104
}
1105+
1106+
void check(const validation_modet vm = validation_modet::INVARIANT) const
1107+
{
1108+
DATA_CHECK(
1109+
operands().size() == 3, "function calls must have three operands");
1110+
}
1111+
1112+
void validate(
1113+
const namespacet &ns,
1114+
const validation_modet vm = validation_modet::INVARIANT) const
1115+
{
1116+
check(vm);
1117+
if(lhs().id() == ID_nil)
1118+
DATA_CHECK(
1119+
to_code_type(function().type()).return_type().id() == ID_empty,
1120+
"void function should not return value");
1121+
else
1122+
DATA_CHECK(
1123+
base_type_eq(
1124+
lhs().type(), to_code_type(function().type()).return_type(), ns),
1125+
"function returns expression of wrong type");
1126+
}
1127+
1128+
void validate_full(
1129+
const namespacet &ns,
1130+
const validation_modet vm = validation_modet::INVARIANT) const
1131+
{
1132+
for(const exprt &op : operands())
1133+
{
1134+
validate_expr_full_pick(op, ns, vm);
1135+
}
1136+
1137+
validate(ns, vm);
1138+
}
11041139
};
11051140

11061141
template<> inline bool can_cast_expr<code_function_callt>(const exprt &base)
@@ -1123,6 +1158,11 @@ inline code_function_callt &to_code_function_call(codet &code)
11231158
return static_cast<code_function_callt &>(code);
11241159
}
11251160

1161+
inline void validate_expr(const code_function_callt &x)
1162+
{
1163+
validate_operands(x, 3, "function calls must have three operands");
1164+
}
1165+
11261166
/// \ref codet representation of a "return from a function" statement.
11271167
class code_returnt:public codet
11281168
{
@@ -1154,6 +1194,30 @@ class code_returnt:public codet
11541194
return false; // backwards compatibility
11551195
return return_value().is_not_nil();
11561196
}
1197+
1198+
void check(const validation_modet vm = validation_modet::INVARIANT) const
1199+
{
1200+
DATA_CHECK(operands().size() == 1, "return must have one operand");
1201+
}
1202+
1203+
void validate(
1204+
const namespacet &ns,
1205+
const validation_modet vm = validation_modet::INVARIANT) const
1206+
{
1207+
check(vm);
1208+
}
1209+
1210+
void validate_full(
1211+
const namespacet &ns,
1212+
const validation_modet vm = validation_modet::INVARIANT) const
1213+
{
1214+
for(const exprt &op : operands())
1215+
{
1216+
validate_expr_full_pick(op, ns, vm);
1217+
}
1218+
1219+
validate(ns, vm);
1220+
}
11571221
};
11581222

11591223
template<> inline bool can_cast_expr<code_returnt>(const exprt &base)
@@ -1176,6 +1240,11 @@ inline code_returnt &to_code_return(codet &code)
11761240
return static_cast<code_returnt &>(code);
11771241
}
11781242

1243+
inline void validate_expr(const code_returnt &x)
1244+
{
1245+
validate_operands(x, 1, "return must have one operand");
1246+
}
1247+
11791248
/// \ref codet representation of a label for branch targets.
11801249
class code_labelt:public codet
11811250
{

src/util/validate_code.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ void call_on_code(const codet &code, Args &&... args)
3030
{
3131
CALL_ON_CODE(code_declt);
3232
}
33+
else if(code.get_statement() == ID_function_call)
34+
{
35+
CALL_ON_CODE(code_function_callt);
36+
}
37+
else if(code.get_statement() == ID_return)
38+
{
39+
CALL_ON_CODE(code_returnt);
40+
}
3341
else if(code.get_statement() == ID_decl)
3442
{
3543
CALL_ON_CODE(code_declt);

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ SRC += analyses/ai/ai.cpp \
1616
analyses/does_remove_const/is_type_at_least_as_const_as.cpp \
1717
goto-programs/goto_trace_output.cpp \
1818
goto-programs/goto_program_assume.cpp \
19+
goto-programs/goto_program_function_call.cpp \
1920
goto-programs/goto_program_declaration.cpp \
2021
interpreter/interpreter.cpp \
2122
json/json_parser.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+
56+
THEN("The consistency check succeeds")
57+
{
58+
goto_function.body.validate(symbol_table, validation_modet::INVARIANT);
59+
REQUIRE(true);
60+
}
61+
}
62+
63+
WHEN("Return type differs from function type")
64+
{
65+
code_function_callt function_call(var_b, fun_foo, {});
66+
instructions.back().make_function_call(function_call);
67+
68+
THEN("The consistency check fails")
69+
{
70+
bool caught = false;
71+
try
72+
{
73+
goto_function.body.validate(
74+
symbol_table, 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)