Skip to content

Commit f200688

Browse files
author
Enrico Steffinlongo
committed
Adding typecheck code for shadow memory builtins.
Adding typecheck code for shadow memory builtins so they are type-checked (more thoroughly than with the C typechecker) and correctly converted to function calls.
1 parent 10c4bca commit f200688

File tree

4 files changed

+286
-0
lines changed

4 files changed

+286
-0
lines changed

src/ansi-c/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ SRC = anonymous_member.cpp \
2323
c_typecheck_code.cpp \
2424
c_typecheck_expr.cpp \
2525
c_typecheck_gcc_polymorphic_builtins.cpp \
26+
c_typecheck_shadow_memory_builtin.cpp \
2627
c_typecheck_initializer.cpp \
2728
c_typecheck_type.cpp \
2829
c_typecheck_typecast.cpp \

src/ansi-c/c_typecheck_base.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ class c_typecheck_baset:
235235
virtual code_blockt instantiate_gcc_polymorphic_builtin(
236236
const irep_idt &identifier,
237237
const symbol_exprt &function_symbol);
238+
virtual optionalt<symbol_exprt>
239+
typecheck_shadow_memory_builtin(const side_effect_expr_function_callt &expr);
238240
virtual exprt
239241
typecheck_shuffle_vector(const side_effect_expr_function_callt &expr);
240242
void disallow_subexpr_by_id(

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2282,6 +2282,38 @@ void c_typecheck_baset::typecheck_side_effect_function_call(
22822282
expr.swap(ok_expr);
22832283
return;
22842284
}
2285+
else if(
2286+
auto shadow_memory_builtin = typecheck_shadow_memory_builtin(expr))
2287+
{
2288+
irep_idt shadow_memory_builtin_id =
2289+
shadow_memory_builtin->get_identifier();
2290+
2291+
const auto builtin_code_type =
2292+
to_code_type(shadow_memory_builtin->type());
2293+
2294+
INVARIANT(
2295+
builtin_code_type.has_ellipsis() &&
2296+
builtin_code_type.parameters().empty(),
2297+
"Shadow memory builtins should be an ellipsis with 0 parameter");
2298+
2299+
// Add the symbol to the symbol table if it is not present yet.
2300+
if(!symbol_table.has_symbol(shadow_memory_builtin_id))
2301+
{
2302+
symbolt new_symbol{
2303+
shadow_memory_builtin_id, shadow_memory_builtin->type(), ID_C};
2304+
new_symbol.base_name = shadow_memory_builtin_id;
2305+
new_symbol.location = f_op.source_location();
2306+
// Add an empty implementation to avoid warnings about missing
2307+
// implementation later on
2308+
new_symbol.value = code_blockt{};
2309+
2310+
symbol_table.add(new_symbol);
2311+
}
2312+
2313+
// Swap the current `function` field with the newly generated
2314+
// `shadow_memory_builtin` `symbol_exprt` and carry on with typechecking
2315+
f_op = std::move(*shadow_memory_builtin);
2316+
}
22852317
else if(
22862318
auto gcc_polymorphic = typecheck_gcc_polymorphic_builtin(
22872319
identifier, expr.arguments(), f_op.source_location()))
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
//
2+
// Author: Enrico Steffinlongo for Diffblue Ltd
3+
//
4+
5+
#include <util/c_types.h>
6+
#include <util/config.h>
7+
#include <util/cprover_prefix.h>
8+
#include <util/expr.h>
9+
#include <util/string_constant.h>
10+
11+
#include "c_typecheck_base.h"
12+
#include "expr2c.h"
13+
14+
/// Function to typecheck a shadow memory field_declaration function.
15+
/// \return a symbol of the shadow memory function
16+
/// \throws invalid_source_file_exceptiont if the typecheck fails.
17+
static symbol_exprt typecheck_field_decl(
18+
const side_effect_expr_function_callt &expr,
19+
const irep_idt &identifier,
20+
const namespacet &ns)
21+
{
22+
// Check correct number of arguments
23+
if(expr.arguments().size() != 2)
24+
{
25+
std::ostringstream error_message;
26+
error_message << identifier << " takes exactly 2 arguments, but "
27+
<< expr.arguments().size() << " were provided";
28+
throw invalid_source_file_exceptiont{
29+
error_message.str(), expr.source_location()};
30+
}
31+
32+
const auto &arg1 = expr.arguments()[0];
33+
if(!can_cast_expr<string_constantt>(arg1))
34+
{
35+
// Can't declare a shadow memory field that has a name that is not a
36+
// constant string
37+
std::ostringstream error_message;
38+
error_message << identifier
39+
<< " argument 1 must be string constant (literal), but ("
40+
<< expr2c(arg1, ns) << ") has type `"
41+
<< type2c(arg1.type(), ns) << '`';
42+
throw invalid_source_file_exceptiont{
43+
error_message.str(), expr.source_location()};
44+
}
45+
46+
// The second argument type must be either a boolean or a bitvector of size
47+
// less or equal to the size of a char.
48+
const auto &arg2 = expr.arguments()[1];
49+
const auto &arg2_type = arg2.type();
50+
const auto arg2_type_as_bv =
51+
type_try_dynamic_cast<bitvector_typet>(arg2_type);
52+
if(
53+
(!arg2_type_as_bv ||
54+
arg2_type_as_bv->get_width() > config.ansi_c.char_width) &&
55+
!can_cast_type<bool_typet>(arg2_type))
56+
{
57+
// Can't declare a shadow memory field with a non-boolean and non bitvector
58+
// type or with a bitvector type greater than 8 bit.
59+
std::ostringstream error_message;
60+
error_message << identifier
61+
<< " argument 2 must be a byte-sized integer, but ("
62+
<< expr2c(arg2, ns) << ") has type `" << type2c(arg2_type, ns)
63+
<< '`';
64+
throw invalid_source_file_exceptiont{
65+
error_message.str(), expr.source_location()};
66+
}
67+
68+
// The function type is just an ellipsis, otherwise the non-ellipsis arguments
69+
// will be typechecked and implicitly casted.
70+
code_typet t({}, void_type());
71+
t.make_ellipsis();
72+
73+
// Create the symbol with the right type.
74+
symbol_exprt result(identifier, std::move(t));
75+
result.add_source_location() = expr.source_location();
76+
return result;
77+
}
78+
79+
/// Function to typecheck a shadow memory get_field function.
80+
/// \return a symbol of the shadow memory function.
81+
/// \throws invalid_source_file_exceptiont if the typecheck fails.
82+
static symbol_exprt typecheck_get_field(
83+
const side_effect_expr_function_callt &expr,
84+
const irep_idt &identifier,
85+
const namespacet &ns)
86+
{
87+
// Check correct number of arguments
88+
if(expr.arguments().size() != 2)
89+
{
90+
std::ostringstream error_message;
91+
error_message << identifier << " takes exactly 2 arguments, but "
92+
<< expr.arguments().size() << " were provided";
93+
throw invalid_source_file_exceptiont{
94+
error_message.str(), expr.source_location()};
95+
}
96+
97+
const auto &arg1 = expr.arguments()[0];
98+
const auto arg1_type_as_ptr =
99+
type_try_dynamic_cast<pointer_typet>(arg1.type());
100+
if(
101+
!arg1_type_as_ptr || !arg1_type_as_ptr->has_subtype() ||
102+
to_type_with_subtype(*arg1_type_as_ptr).subtype() == empty_typet{})
103+
{
104+
// Can't get the shadow memory value of anything other than a non-void
105+
// pointer
106+
std::ostringstream error_message;
107+
error_message << identifier
108+
<< " argument 1 must be a non-void pointer, but ("
109+
<< expr2c(arg1, ns) << ") has type `"
110+
<< type2c(arg1.type(), ns)
111+
<< "`. Insert a cast to the type that you want to access.";
112+
throw invalid_source_file_exceptiont{
113+
error_message.str(), expr.source_location()};
114+
}
115+
116+
const auto &arg2 = expr.arguments()[1];
117+
if(!can_cast_expr<string_constantt>(arg2))
118+
{
119+
// Can't get value from a shadow memory field that has a name that is
120+
// not a constant string
121+
std::ostringstream error_message;
122+
error_message << identifier
123+
<< " argument 2 must be string constant (literal), but ("
124+
<< expr2c(arg2, ns) << ") has type `"
125+
<< type2c(arg2.type(), ns) << '`';
126+
throw invalid_source_file_exceptiont{
127+
error_message.str(), expr.source_location()};
128+
}
129+
130+
// The function type is just an ellipsis, otherwise the non-ellipsis arguments
131+
// will be typechecked and implicitly casted.
132+
// Setting the return type to `signed_int_type` otherwise it was creating
133+
// problem with signed-unsigned char comparison. Having `signed_int_type`
134+
// seems to fix the issue as it contains more bits for the conversion.
135+
code_typet t({}, signed_int_type());
136+
t.make_ellipsis();
137+
138+
// Create the symbol with the right type.
139+
symbol_exprt result(identifier, std::move(t));
140+
result.add_source_location() = expr.source_location();
141+
return result;
142+
}
143+
144+
/// Function to typecheck a shadow memory set_field function.
145+
/// \return a symbol of the shadow memory function.
146+
/// \throws invalid_source_file_exceptiont if the typecheck fails.
147+
static symbol_exprt typecheck_set_field(
148+
const side_effect_expr_function_callt &expr,
149+
const irep_idt &identifier,
150+
const namespacet &ns)
151+
{
152+
// Check correct number of arguments
153+
if(expr.arguments().size() != 3)
154+
{
155+
std::ostringstream error_message;
156+
error_message << identifier << " takes exactly 3 arguments, but "
157+
<< expr.arguments().size() << " were provided";
158+
throw invalid_source_file_exceptiont{
159+
error_message.str(), expr.source_location()};
160+
}
161+
162+
const auto &arg1 = expr.arguments()[0];
163+
const auto arg1_type_as_ptr =
164+
type_try_dynamic_cast<pointer_typet>(arg1.type());
165+
if(
166+
!arg1_type_as_ptr || !arg1_type_as_ptr->has_subtype() ||
167+
to_type_with_subtype(*arg1_type_as_ptr).subtype() == empty_typet{})
168+
{
169+
// Can't set the shadow memory value of anything other than a non-void
170+
// pointer
171+
std::ostringstream error_message;
172+
error_message << identifier
173+
<< " argument 1 must be a non-void pointer, but ("
174+
<< expr2c(arg1, ns) << ") has type `"
175+
<< type2c(arg1.type(), ns)
176+
<< "`. Insert a cast to the type that you want to access.";
177+
throw invalid_source_file_exceptiont{
178+
error_message.str(), expr.source_location()};
179+
}
180+
181+
const auto &arg2 = expr.arguments()[1];
182+
if(!can_cast_expr<string_constantt>(arg2))
183+
{
184+
// Can't set value from a shadow memory field that has a name that is
185+
// not a constant string
186+
std::ostringstream error_message;
187+
error_message << identifier
188+
<< " argument 2 must be string constant (literal), but ("
189+
<< expr2c(arg2, ns) << ") has type `"
190+
<< type2c(arg2.type(), ns) << '`';
191+
throw invalid_source_file_exceptiont{
192+
error_message.str(), expr.source_location()};
193+
}
194+
195+
// The third argument type must be either a boolean or a bitvector.
196+
const auto &arg3 = expr.arguments()[2];
197+
const auto &arg3_type = arg3.type();
198+
if(
199+
!can_cast_type<bitvector_typet>(arg3_type) &&
200+
!can_cast_type<bool_typet>(arg3_type))
201+
{
202+
// Can't set the shadow memory value with a non-boolean and non-bitvector
203+
// value.
204+
std::ostringstream error_message;
205+
error_message << identifier
206+
<< " argument 3 must be a boolean or of integer value, but ("
207+
<< expr2c(arg3, ns) << ") has type `" << type2c(arg3_type, ns)
208+
<< '`';
209+
throw invalid_source_file_exceptiont{
210+
error_message.str(), expr.source_location()};
211+
}
212+
213+
// The function type is just an ellipsis, otherwise the non-ellipsis arguments
214+
// will be typechecked and implicitly casted.
215+
code_typet t({}, void_type());
216+
t.make_ellipsis();
217+
218+
// Create the symbol with the right type.
219+
symbol_exprt result(identifier, std::move(t));
220+
result.add_source_location() = expr.source_location();
221+
return result;
222+
}
223+
224+
/// Typecheck the function if it is a shadow_memory builtin and return a symbol
225+
/// for it. Otherwise return empty.
226+
optionalt<symbol_exprt> c_typecheck_baset::typecheck_shadow_memory_builtin(
227+
const side_effect_expr_function_callt &expr)
228+
{
229+
const exprt &f_op = expr.function();
230+
INVARIANT(
231+
can_cast_expr<symbol_exprt>(f_op),
232+
"expr.function() has to be a symbol_expr");
233+
const irep_idt &identifier = to_symbol_expr(f_op).get_identifier();
234+
235+
if(
236+
identifier == CPROVER_PREFIX "field_decl_global" ||
237+
identifier == CPROVER_PREFIX "field_decl_local")
238+
{
239+
return typecheck_field_decl(expr, identifier, *this);
240+
}
241+
else if(identifier == CPROVER_PREFIX "get_field")
242+
{
243+
return typecheck_get_field(expr, identifier, *this);
244+
}
245+
else if(identifier == CPROVER_PREFIX "set_field")
246+
{
247+
return typecheck_set_field(expr, identifier, *this);
248+
}
249+
// The function is not a shadow memory builtin, so we return <empty>.
250+
return {};
251+
}

0 commit comments

Comments
 (0)