Skip to content

Commit 068a9d8

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 f9afff2 commit 068a9d8

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);
@@ -480,22 +545,9 @@ void linkingt::duplicate_code_symbol(
480545
const code_typet &old_t=to_code_type(old_symbol.type);
481546
const code_typet &new_t=to_code_type(new_symbol.type);
482547

483-
// if one of them was an implicit declaration then only conflicts on the
484-
// return type are an error as we would end up with assignments with
485-
// mismatching types; as we currently do not patch these by inserting type
486-
// casts we need to fail hard
487548
if(old_symbol.type.get_bool(ID_C_incomplete) && old_symbol.value.is_nil())
488549
{
489-
if(base_type_eq(old_t.return_type(), new_t.return_type(), ns))
490-
link_warning(
491-
old_symbol,
492-
new_symbol,
493-
"implicit function declaration");
494-
else
495-
link_error(
496-
old_symbol,
497-
new_symbol,
498-
"implicit function declaration");
550+
link_warning(old_symbol, new_symbol, "implicit function declaration");
499551

500552
old_symbol.type=new_symbol.type;
501553
old_symbol.location=new_symbol.location;
@@ -504,24 +556,15 @@ void linkingt::duplicate_code_symbol(
504556
else if(
505557
new_symbol.type.get_bool(ID_C_incomplete) && new_symbol.value.is_nil())
506558
{
507-
if(base_type_eq(old_t.return_type(), new_t.return_type(), ns))
508-
link_warning(
509-
old_symbol,
510-
new_symbol,
511-
"ignoring conflicting implicit function declaration");
512-
else
513-
link_error(
514-
old_symbol,
515-
new_symbol,
516-
"implicit function declaration");
559+
link_warning(
560+
old_symbol,
561+
new_symbol,
562+
"ignoring conflicting implicit function declaration");
517563
}
518564
// handle (incomplete) function prototypes
519-
else if(base_type_eq(old_t.return_type(), new_t.return_type(), ns) &&
520-
((old_t.parameters().empty() &&
521-
old_t.has_ellipsis() &&
565+
else if(((old_t.parameters().empty() && old_t.has_ellipsis() &&
522566
old_symbol.value.is_nil()) ||
523-
(new_t.parameters().empty() &&
524-
new_t.has_ellipsis() &&
567+
(new_t.parameters().empty() && new_t.has_ellipsis() &&
525568
new_symbol.value.is_nil())))
526569
{
527570
if(old_t.parameters().empty() &&
@@ -572,9 +615,7 @@ void linkingt::duplicate_code_symbol(
572615
}
573616
// conflicting declarations without a definition, matching return
574617
// types
575-
else if(base_type_eq(old_t.return_type(), new_t.return_type(), ns) &&
576-
old_symbol.value.is_nil() &&
577-
new_symbol.value.is_nil())
618+
else if(old_symbol.value.is_nil() && new_symbol.value.is_nil())
578619
{
579620
link_warning(
580621
old_symbol,
@@ -614,8 +655,11 @@ void linkingt::duplicate_code_symbol(
614655
conflictst conflicts;
615656

616657
if(!base_type_eq(old_t.return_type(), new_t.return_type(), ns))
617-
conflicts.push_back(
618-
std::make_pair(old_t.return_type(), new_t.return_type()));
658+
{
659+
link_warning(old_symbol, new_symbol, "conflicting return types");
660+
661+
conflicts.emplace_back(old_t.return_type(), new_t.return_type());
662+
}
619663

620664
code_typet::parameterst::const_iterator
621665
n_it=new_t.parameters().begin(),
@@ -665,21 +709,11 @@ void linkingt::duplicate_code_symbol(
665709
const typet &t1=follow_tags_symbols(ns, conflicts.front().first);
666710
const typet &t2=follow_tags_symbols(ns, conflicts.front().second);
667711

668-
// void vs. non-void return type may be acceptable if the
669-
// return value is never used
670-
if((t1.id()==ID_empty || t2.id()==ID_empty) &&
671-
(old_symbol.value.is_nil() || new_symbol.value.is_nil()))
672-
{
673-
if(warn_msg.empty())
674-
warn_msg="void/non-void return type conflict on function";
675-
replace=
676-
new_symbol.value.is_not_nil() ||
677-
(old_symbol.value.is_nil() && t2.id()==ID_empty);
678-
}
679712
// different pointer arguments without implementation may work
680-
else if((t1.id()==ID_pointer || t2.id()==ID_pointer) &&
681-
pointer_offset_bits(t1, ns)==pointer_offset_bits(t2, ns) &&
682-
old_symbol.value.is_nil() && new_symbol.value.is_nil())
713+
if(
714+
(t1.id() == ID_pointer || t2.id() == ID_pointer) &&
715+
pointer_offset_bits(t1, ns) == pointer_offset_bits(t2, ns) &&
716+
old_symbol.value.is_nil() && new_symbol.value.is_nil())
683717
{
684718
if(warn_msg.empty())
685719
warn_msg="different pointer types in extern function";
@@ -782,6 +816,9 @@ void linkingt::duplicate_code_symbol(
782816
}
783817
}
784818
}
819+
820+
object_type_updates.insert(
821+
old_symbol.symbol_expr(), old_symbol.symbol_expr());
785822
}
786823

787824
if(!new_symbol.value.is_nil())
@@ -796,6 +833,11 @@ void linkingt::duplicate_code_symbol(
796833
old_symbol.is_weak=new_symbol.is_weak;
797834
old_symbol.location=new_symbol.location;
798835
old_symbol.is_macro=new_symbol.is_macro;
836+
837+
// replace any previous update
838+
object_type_updates.erase(old_symbol.name);
839+
object_type_updates.insert(
840+
old_symbol.symbol_expr(), old_symbol.symbol_expr());
799841
}
800842
else if(to_code_type(old_symbol.type).get_inlined())
801843
{

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)