Skip to content

Commit 6a0cafa

Browse files
authored
Merge pull request #3188 from xbauch/well-formedness-checking-goto
Well formedness checking goto
2 parents 8c16edb + 2c9a5c3 commit 6a0cafa

9 files changed

+518
-0
lines changed

src/goto-programs/goto_program.cpp

+55
Original file line numberDiff line numberDiff line change
@@ -676,15 +676,38 @@ void goto_programt::instructiont::validate(
676676
validate_full_code(code, ns, vm);
677677
validate_full_expr(guard, ns, vm);
678678

679+
const symbolt *table_symbol;
680+
679681
switch(type)
680682
{
681683
case NO_INSTRUCTION_TYPE:
682684
break;
683685
case GOTO:
686+
DATA_CHECK_WITH_DIAGNOSTICS(
687+
vm,
688+
has_target(),
689+
"goto instruction expects at least one target",
690+
source_location);
691+
// get_target checks that targets.size()==1
692+
DATA_CHECK_WITH_DIAGNOSTICS(
693+
vm,
694+
get_target()->is_target() && get_target()->target_number != 0,
695+
"goto target has to be a target",
696+
source_location);
684697
break;
685698
case ASSUME:
699+
DATA_CHECK_WITH_DIAGNOSTICS(
700+
vm,
701+
targets.empty(),
702+
"assume instruction should not have a target",
703+
source_location);
686704
break;
687705
case ASSERT:
706+
DATA_CHECK_WITH_DIAGNOSTICS(
707+
vm,
708+
targets.empty(),
709+
"assert instruction should not have a target",
710+
source_location);
688711
break;
689712
case OTHER:
690713
break;
@@ -703,6 +726,11 @@ void goto_programt::instructiont::validate(
703726
case ATOMIC_END:
704727
break;
705728
case RETURN:
729+
DATA_CHECK_WITH_DIAGNOSTICS(
730+
vm,
731+
code.get_statement() == ID_return,
732+
"return instruction should contain a return statement",
733+
source_location);
706734
break;
707735
case ASSIGN:
708736
DATA_CHECK(
@@ -713,10 +741,37 @@ void goto_programt::instructiont::validate(
713741
vm, targets.empty(), "assign instruction should not have a target");
714742
break;
715743
case DECL:
744+
DATA_CHECK_WITH_DIAGNOSTICS(
745+
vm,
746+
code.get_statement() == ID_decl,
747+
"declaration instructions should contain a declaration statement",
748+
source_location);
749+
DATA_CHECK_WITH_DIAGNOSTICS(
750+
vm,
751+
!ns.lookup(to_code_decl(code).get_identifier(), table_symbol),
752+
"declared symbols should be known",
753+
id2string(to_code_decl(code).get_identifier()),
754+
source_location);
716755
break;
717756
case DEAD:
757+
DATA_CHECK_WITH_DIAGNOSTICS(
758+
vm,
759+
code.get_statement() == ID_dead,
760+
"dead instructions should contain a dead statement",
761+
source_location);
762+
DATA_CHECK_WITH_DIAGNOSTICS(
763+
vm,
764+
!ns.lookup(to_code_dead(code).get_identifier(), table_symbol),
765+
"removed symbols should be known",
766+
id2string(to_code_dead(code).get_identifier()),
767+
source_location);
718768
break;
719769
case FUNCTION_CALL:
770+
DATA_CHECK_WITH_DIAGNOSTICS(
771+
vm,
772+
code.get_statement() == ID_function_call,
773+
"function call instruction should contain a call statement",
774+
source_location);
720775
break;
721776
case THROW:
722777
break;

src/util/std_code.h

+100
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,19 @@ class code_declt:public codet
371371
{
372372
return symbol().get_identifier();
373373
}
374+
375+
static void check(
376+
const codet &code,
377+
const validation_modet vm = validation_modet::INVARIANT)
378+
{
379+
DATA_CHECK(
380+
vm, code.operands().size() == 1, "declaration must have one operand");
381+
DATA_CHECK(
382+
vm,
383+
code.op0().id() == ID_symbol,
384+
"declaring a non-symbol: " +
385+
id2string(to_symbol_expr(code.op0()).get_identifier()));
386+
}
374387
};
375388

376389
template<> inline bool can_cast_expr<code_declt>(const exprt &base)
@@ -430,6 +443,21 @@ class code_deadt:public codet
430443
{
431444
return symbol().get_identifier();
432445
}
446+
447+
static void check(
448+
const codet &code,
449+
const validation_modet vm = validation_modet::INVARIANT)
450+
{
451+
DATA_CHECK(
452+
vm,
453+
code.operands().size() == 1,
454+
"removal (code_deadt) must have one operand");
455+
DATA_CHECK(
456+
vm,
457+
code.op0().id() == ID_symbol,
458+
"removing a non-symbol: " +
459+
id2string(to_symbol_expr(code.op0()).get_identifier()) + "from scope");
460+
}
433461
};
434462

435463
template<> inline bool can_cast_expr<code_deadt>(const exprt &base)
@@ -510,6 +538,11 @@ inline code_assumet &to_code_assume(codet &code)
510538
return static_cast<code_assumet &>(code);
511539
}
512540

541+
inline void validate_expr(const code_assumet &x)
542+
{
543+
validate_operands(x, 1, "assume must have one operand");
544+
}
545+
513546
/// A non-fatal assertion, which checks a condition then permits execution to
514547
/// continue.
515548
class code_assertt:public codet
@@ -557,6 +590,11 @@ inline code_assertt &to_code_assert(codet &code)
557590
return static_cast<code_assertt &>(code);
558591
}
559592

593+
inline void validate_expr(const code_assertt &x)
594+
{
595+
validate_operands(x, 1, "assert must have one operand");
596+
}
597+
560598
/// Create a fatal assertion, which checks a condition and then halts if it does
561599
/// not hold. Equivalent to `ASSERT(condition); ASSUME(condition)`.
562600
///
@@ -1046,6 +1084,51 @@ class code_function_callt:public codet
10461084
{
10471085
return op2().operands();
10481086
}
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+
}
10491132
};
10501133

10511134
template<> inline bool can_cast_expr<code_function_callt>(const exprt &base)
@@ -1068,6 +1151,11 @@ inline code_function_callt &to_code_function_call(codet &code)
10681151
return static_cast<code_function_callt &>(code);
10691152
}
10701153

1154+
inline void validate_expr(const code_function_callt &x)
1155+
{
1156+
validate_operands(x, 3, "function calls must have three operands");
1157+
}
1158+
10711159
/// \ref codet representation of a "return from a function" statement.
10721160
class code_returnt:public codet
10731161
{
@@ -1099,6 +1187,13 @@ class code_returnt:public codet
10991187
return false; // backwards compatibility
11001188
return return_value().is_not_nil();
11011189
}
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+
}
11021197
};
11031198

11041199
template<> inline bool can_cast_expr<code_returnt>(const exprt &base)
@@ -1121,6 +1216,11 @@ inline code_returnt &to_code_return(codet &code)
11211216
return static_cast<code_returnt &>(code);
11221217
}
11231218

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

src/util/validate_code.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ void call_on_code(const codet &code, Args &&... args)
3131
{
3232
CALL_ON_CODE(code_declt);
3333
}
34+
else if(code.get_statement() == ID_dead)
35+
{
36+
CALL_ON_CODE(code_deadt);
37+
}
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+
}
3446
else
3547
{
3648
#ifdef REPORT_UNIMPLEMENTED_CODE_CHECKS

unit/Makefile

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ SRC += analyses/ai/ai.cpp \
1515
analyses/does_remove_const/does_type_preserve_const_correctness.cpp \
1616
analyses/does_remove_const/is_type_at_least_as_const_as.cpp \
1717
compound_block_locations.cpp \
18+
goto-programs/goto_program_assume.cpp \
19+
goto-programs/goto_program_dead.cpp \
20+
goto-programs/goto_program_declaration.cpp \
21+
goto-programs/goto_program_function_call.cpp \
22+
goto-programs/goto_program_goto_target.cpp \
1823
goto-programs/goto_trace_output.cpp \
1924
goto-symex/ssa_equation.cpp \
2025
interpreter/interpreter.cpp \
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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 assert/assume codes",
15+
"[core][goto-programs][validate]")
16+
{
17+
GIVEN("A program with one assumption")
18+
{
19+
symbol_tablet symbol_table;
20+
const typet type1 = signedbv_typet(32);
21+
symbolt symbol;
22+
irep_idt symbol_name = "a";
23+
symbol.name = symbol_name;
24+
symbol_exprt varx(symbol_name, type1);
25+
exprt val10 = from_integer(10, type1);
26+
binary_relation_exprt x_le_10(varx, ID_le, val10);
27+
28+
goto_functiont goto_function;
29+
auto &instructions = goto_function.body.instructions;
30+
instructions.emplace_back(goto_program_instruction_typet::ASSUME);
31+
32+
symbol.type = type1;
33+
symbol_table.insert(symbol);
34+
namespacet ns(symbol_table);
35+
instructions.back().make_assertion(x_le_10);
36+
37+
WHEN("Instruction has no targets")
38+
{
39+
THEN("The consistency check succeeds")
40+
{
41+
goto_function.body.validate(ns, validation_modet::INVARIANT);
42+
REQUIRE(true);
43+
}
44+
}
45+
46+
WHEN("Instruction has a target")
47+
{
48+
instructions.back().targets.push_back(instructions.begin());
49+
THEN("The consistency check fails")
50+
{
51+
bool caught = false;
52+
try
53+
{
54+
goto_function.body.validate(ns, validation_modet::EXCEPTION);
55+
}
56+
catch(incorrect_goto_program_exceptiont &e)
57+
{
58+
caught = true;
59+
}
60+
REQUIRE(caught);
61+
}
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)