Skip to content

Commit 3b73b4d

Browse files
committed
Linking: actually replace code type information in expressions
The linker already accepted a number of type mismatches for function declarations, but did not actually modify the goto functions to reflect these type mismatches, just the type information in the symbol table was updated. These changes enable support for conflicts on function return types, which are now resolved by introducing type casts.
1 parent 2365332 commit 3b73b4d

File tree

15 files changed

+169
-67
lines changed

15 files changed

+169
-67
lines changed

regression/ansi-c/undeclared_function/fileB.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
#include <stdlib.h>
1+
void *malloc(__CPROVER_size_t s)
2+
{
3+
return (void *)0 + s;
4+
}
25

36
int f()
47
{

regression/ansi-c/undeclared_function/undeclared_function1.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KNOWNBUG
1+
CORE
22
fileA.c
33
fileB.c --validate-goto-model
44
^EXIT=0$

regression/ansi-c/incomplete-structs/test.desc renamed to regression/cbmc/incomplete-structs/test.desc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
CORE
22
typesmain.c
3-
types1.c types2.c types3.c --verbosity 2
3+
types1.c types2.c types3.c
44
reason for conflict at \*#this.u: number of members is different \(3/0\)
55
reason for conflict at \*#this.u: number of members is different \(0/3\)
66
struct U \(incomplete\)
77
warning: pointer parameter types differ between declaration and definition "bar"
88
warning: pointer parameter types differ between declaration and definition "foo"
9+
^VERIFICATION SUCCESSFUL$
910
^EXIT=0$
1011
^SIGNAL=0$
1112
--

regression/cbmc/return6/test.desc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
CORE
22
main.c
33
f_def.c
4-
^EXIT=6$
4+
^VERIFICATION SUCCESSFUL$
5+
^EXIT=0$
56
^SIGNAL=0$
6-
CONVERSION ERROR
77
--
88
^warning: ignoring
9-
^VERIFICATION SUCCESSFUL$

regression/linking-goto-binaries/chain.sh

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,19 @@ is_windows=$3
88

99
main=${*:$#}
1010
main=${main%.c}
11+
main_local=$(basename ${main})
1112
args=${*:4:$#-4}
1213
next=${args%.c}
14+
next_local=$(basename ${next})
1315

1416
if [[ "${is_windows}" == "true" ]]; then
15-
$goto_cc /c "${main}.c" "/Fo${main}.gb"
16-
$goto_cc /c "${next}.c" "/Fo${next}.gb"
17-
$goto_cc "${main}.gb" "${next}.gb" "/Fefinal.gb"
17+
$goto_cc /c "${main}.c" "/Fo${main_local}.gb"
18+
$goto_cc /c "${next}.c" "/Fo${next_local}.gb"
19+
$goto_cc "${main_local}.gb" "${next_local}.gb" "/Fefinal.gb"
1820
else
19-
$goto_cc -c -o "${main}.gb" "${main}.c"
20-
$goto_cc -c -o "${next}.gb" "${next}.c"
21-
$goto_cc "${main}.gb" "${next}.gb" -o "final.gb"
21+
$goto_cc -c -o "${main_local}.gb" "${main}.c"
22+
$goto_cc -c -o "${next_local}.gb" "${next}.c"
23+
$goto_cc "${main_local}.gb" "${next_local}.gb" -o "final.gb"
2224
fi
2325

24-
$cbmc "final.gb"
26+
$cbmc --validate-goto-model "final.gb"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
../../cbmc/Linking7/main.c
3+
../../cbmc/Linking7/module2.c
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
line 21 assertion \*g\.a == 42: SUCCESS
8+
line 22 assertion \*g\.c == 41: FAILURE
9+
^\*\* 1 of 2 failed
10+
--
11+
^warning: ignoring
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
../../cbmc/Linking7/return_type1.c
3+
../../cbmc/Linking7/return_type2.c
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
--
10+
Note issue #3081
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
../../cbmc/Linking7/main.c
3+
../../cbmc/Linking7/module.c
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
^\*\* 1 of 2 failed
8+
line 21 assertion \*g\.a == 42: SUCCESS
9+
line 22 assertion \*g\.b == 41: FAILURE
10+
--
11+
^warning: ignoring

src/goto-programs/link_goto_model.cpp

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ void finalize_linking(
152152
goto_modelt &dest,
153153
const replace_symbolt::expr_mapt &type_updates)
154154
{
155-
unchecked_replace_symbolt object_type_updates;
155+
casting_replace_symbolt object_type_updates;
156156
object_type_updates.get_expr_map().insert(
157157
type_updates.begin(), type_updates.end());
158158

@@ -200,10 +200,30 @@ void finalize_linking(
200200
{
201201
for(auto &instruction : gf_entry.second.body.instructions)
202202
{
203-
instruction.transform([&object_type_updates](exprt expr) {
204-
object_type_updates(expr);
205-
return expr;
206-
});
203+
if(instruction.is_function_call())
204+
{
205+
const bool changed =
206+
!object_type_updates.replace(instruction.call_function());
207+
if(changed && instruction.call_lhs().is_not_nil())
208+
{
209+
object_type_updates(instruction.call_lhs());
210+
if(
211+
instruction.call_lhs().type() !=
212+
to_code_type(instruction.call_function().type()).return_type())
213+
{
214+
instruction.call_lhs() = typecast_exprt{
215+
instruction.call_lhs(),
216+
to_code_type(instruction.call_function().type()).return_type()};
217+
}
218+
}
219+
}
220+
else
221+
{
222+
instruction.transform([&object_type_updates](exprt expr) {
223+
const bool changed = !object_type_updates.replace(expr);
224+
return changed ? optionalt<exprt>{expr} : nullopt;
225+
});
226+
}
207227
}
208228
}
209229
}

src/linking/linking.cpp

Lines changed: 91 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,77 @@ Author: Daniel Kroening, [email protected]
2121
#include <util/pointer_expr.h>
2222
#include <util/pointer_offset_size.h>
2323
#include <util/simplify_expr.h>
24+
#include <util/std_code.h>
2425
#include <util/symbol_table.h>
2526

2627
#include <langapi/language_util.h>
2728

2829
#include "linking_class.h"
2930

31+
bool casting_replace_symbolt::replace(exprt &dest) const
32+
{
33+
bool result = true; // unchanged
34+
35+
// first look at type
36+
37+
const exprt &const_dest(dest);
38+
if(have_to_replace(const_dest.type()))
39+
if(!replace_symbolt::replace(dest.type()))
40+
result = false;
41+
42+
// now do expression itself
43+
44+
if(!have_to_replace(dest))
45+
return result;
46+
47+
if(dest.id() == ID_side_effect)
48+
{
49+
if(auto call = expr_try_dynamic_cast<side_effect_expr_function_callt>(dest))
50+
{
51+
if(!have_to_replace(call->function()))
52+
return replace_symbolt::replace(dest);
53+
54+
exprt before = dest;
55+
code_typet type = to_code_type(call->function().type());
56+
57+
result &= replace_symbolt::replace(call->function());
58+
59+
// maybe add type casts here?
60+
for(auto &arg : call->arguments())
61+
result &= replace_symbolt::replace(arg);
62+
63+
if(
64+
type.return_type() !=
65+
to_code_type(call->function().type()).return_type())
66+
{
67+
call->type() = to_code_type(call->function().type()).return_type();
68+
dest = typecast_exprt(*call, type.return_type());
69+
result = true;
70+
}
71+
72+
return result;
73+
}
74+
}
75+
else if(dest.id() == ID_address_of)
76+
{
77+
pointer_typet ptr_type = to_pointer_type(dest.type());
78+
79+
result &= replace_symbolt::replace(dest);
80+
81+
address_of_exprt address_of = to_address_of_expr(dest);
82+
if(address_of.object().type() != ptr_type.subtype())
83+
{
84+
to_pointer_type(address_of.type()).subtype() = address_of.object().type();
85+
dest = typecast_exprt(address_of, ptr_type);
86+
result = true;
87+
}
88+
89+
return result;
90+
}
91+
92+
return replace_symbolt::replace(dest);
93+
}
94+
3095
bool casting_replace_symbolt::replace_symbol_expr(symbol_exprt &s) const
3196
{
3297
expr_mapt::const_iterator it = expr_map.find(s.get_identifier());
@@ -36,7 +101,7 @@ bool casting_replace_symbolt::replace_symbol_expr(symbol_exprt &s) const
36101

37102
const exprt &e = it->second;
38103

39-
if(e.type().id() != ID_array)
104+
if(e.type().id() != ID_array && e.type().id() != ID_code)
40105
{
41106
typet type = s.type();
42107
static_cast<exprt &>(s) = typecast_exprt::conditional_cast(e, type);
@@ -491,22 +556,9 @@ void linkingt::duplicate_code_symbol(
491556
const code_typet &old_t=to_code_type(old_symbol.type);
492557
const code_typet &new_t=to_code_type(new_symbol.type);
493558

494-
// if one of them was an implicit declaration then only conflicts on the
495-
// return type are an error as we would end up with assignments with
496-
// mismatching types; as we currently do not patch these by inserting type
497-
// casts we need to fail hard
498559
if(old_symbol.type.get_bool(ID_C_incomplete) && old_symbol.value.is_nil())
499560
{
500-
if(base_type_eq(old_t.return_type(), new_t.return_type(), ns))
501-
link_warning(
502-
old_symbol,
503-
new_symbol,
504-
"implicit function declaration");
505-
else
506-
link_error(
507-
old_symbol,
508-
new_symbol,
509-
"implicit function declaration");
561+
link_warning(old_symbol, new_symbol, "implicit function declaration");
510562

511563
old_symbol.type=new_symbol.type;
512564
old_symbol.location=new_symbol.location;
@@ -515,24 +567,15 @@ void linkingt::duplicate_code_symbol(
515567
else if(
516568
new_symbol.type.get_bool(ID_C_incomplete) && new_symbol.value.is_nil())
517569
{
518-
if(base_type_eq(old_t.return_type(), new_t.return_type(), ns))
519-
link_warning(
520-
old_symbol,
521-
new_symbol,
522-
"ignoring conflicting implicit function declaration");
523-
else
524-
link_error(
525-
old_symbol,
526-
new_symbol,
527-
"implicit function declaration");
570+
link_warning(
571+
old_symbol,
572+
new_symbol,
573+
"ignoring conflicting implicit function declaration");
528574
}
529575
// handle (incomplete) function prototypes
530-
else if(base_type_eq(old_t.return_type(), new_t.return_type(), ns) &&
531-
((old_t.parameters().empty() &&
532-
old_t.has_ellipsis() &&
576+
else if(((old_t.parameters().empty() && old_t.has_ellipsis() &&
533577
old_symbol.value.is_nil()) ||
534-
(new_t.parameters().empty() &&
535-
new_t.has_ellipsis() &&
578+
(new_t.parameters().empty() && new_t.has_ellipsis() &&
536579
new_symbol.value.is_nil())))
537580
{
538581
if(old_t.parameters().empty() &&
@@ -583,9 +626,7 @@ void linkingt::duplicate_code_symbol(
583626
}
584627
// conflicting declarations without a definition, matching return
585628
// types
586-
else if(base_type_eq(old_t.return_type(), new_t.return_type(), ns) &&
587-
old_symbol.value.is_nil() &&
588-
new_symbol.value.is_nil())
629+
else if(old_symbol.value.is_nil() && new_symbol.value.is_nil())
589630
{
590631
link_warning(
591632
old_symbol,
@@ -625,8 +666,11 @@ void linkingt::duplicate_code_symbol(
625666
conflictst conflicts;
626667

627668
if(!base_type_eq(old_t.return_type(), new_t.return_type(), ns))
628-
conflicts.push_back(
629-
std::make_pair(old_t.return_type(), new_t.return_type()));
669+
{
670+
link_warning(old_symbol, new_symbol, "conflicting return types");
671+
672+
conflicts.emplace_back(old_t.return_type(), new_t.return_type());
673+
}
630674

631675
code_typet::parameterst::const_iterator
632676
n_it=new_t.parameters().begin(),
@@ -676,21 +720,11 @@ void linkingt::duplicate_code_symbol(
676720
const typet &t1=follow_tags_symbols(ns, conflicts.front().first);
677721
const typet &t2=follow_tags_symbols(ns, conflicts.front().second);
678722

679-
// void vs. non-void return type may be acceptable if the
680-
// return value is never used
681-
if((t1.id()==ID_empty || t2.id()==ID_empty) &&
682-
(old_symbol.value.is_nil() || new_symbol.value.is_nil()))
683-
{
684-
if(warn_msg.empty())
685-
warn_msg="void/non-void return type conflict on function";
686-
replace=
687-
new_symbol.value.is_not_nil() ||
688-
(old_symbol.value.is_nil() && t2.id()==ID_empty);
689-
}
690723
// different pointer arguments without implementation may work
691-
else if((t1.id()==ID_pointer || t2.id()==ID_pointer) &&
692-
pointer_offset_bits(t1, ns)==pointer_offset_bits(t2, ns) &&
693-
old_symbol.value.is_nil() && new_symbol.value.is_nil())
724+
if(
725+
(t1.id() == ID_pointer || t2.id() == ID_pointer) &&
726+
pointer_offset_bits(t1, ns) == pointer_offset_bits(t2, ns) &&
727+
old_symbol.value.is_nil() && new_symbol.value.is_nil())
694728
{
695729
if(warn_msg.empty())
696730
warn_msg="different pointer types in extern function";
@@ -793,6 +827,9 @@ void linkingt::duplicate_code_symbol(
793827
}
794828
}
795829
}
830+
831+
object_type_updates.insert(
832+
old_symbol.symbol_expr(), old_symbol.symbol_expr());
796833
}
797834

798835
if(!new_symbol.value.is_nil())
@@ -807,6 +844,11 @@ void linkingt::duplicate_code_symbol(
807844
old_symbol.is_weak=new_symbol.is_weak;
808845
old_symbol.location=new_symbol.location;
809846
old_symbol.is_macro=new_symbol.is_macro;
847+
848+
// replace any previous update
849+
object_type_updates.erase(old_symbol.name);
850+
object_type_updates.insert(
851+
old_symbol.symbol_expr(), old_symbol.symbol_expr());
810852
}
811853
else if(to_code_type(old_symbol.type).get_inlined())
812854
{

src/linking/linking_class.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ Author: Daniel Kroening, [email protected]
2121

2222
class casting_replace_symbolt : public replace_symbolt
2323
{
24+
public:
25+
bool replace(exprt &dest) const override;
26+
2427
private:
2528
bool replace_symbol_expr(symbol_exprt &dest) const override;
2629
};

0 commit comments

Comments
 (0)