CBMC
linking_diagnostics.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: ANSI-C Linking
4 
5 Author: Michael Tautschnig
6 
7 \*******************************************************************/
8 
11 
12 #include "linking_diagnostics.h"
13 
14 #include <util/c_types.h>
16 #include <util/message.h>
17 #include <util/namespace.h>
18 
19 #include <langapi/language_util.h>
20 
21 #include <unordered_set>
22 
23 static const typet &follow_tags_symbols(const namespacet &ns, const typet &type)
24 {
25  if(type.id() == ID_c_enum_tag)
26  return ns.follow_tag(to_c_enum_tag_type(type));
27  else if(type.id() == ID_struct_tag)
28  return ns.follow_tag(to_struct_tag_type(type));
29  else if(type.id() == ID_union_tag)
30  return ns.follow_tag(to_union_tag_type(type));
31  else
32  return type;
33 }
34 
36  const symbolt &symbol,
37  const typet &type) const
38 {
39  const typet &followed = follow_tags_symbols(ns, type);
40 
41  if(followed.id() == ID_struct || followed.id() == ID_union)
42  {
43  std::string result = followed.id_string();
44 
45  const std::string &tag = followed.get_string(ID_tag);
46  if(!tag.empty())
47  result += " " + tag;
48 
49  if(to_struct_union_type(followed).is_incomplete())
50  {
51  result += " (incomplete)";
52  }
53  else
54  {
55  result += " {\n";
56 
57  for(const auto &c : to_struct_union_type(followed).components())
58  {
59  const typet &subtype = c.type();
60  result += " ";
61  result += from_type(ns, symbol.name, subtype);
62  result += ' ';
63 
64  if(!c.get_base_name().empty())
65  result += id2string(c.get_base_name());
66  else
67  result += id2string(c.get_name());
68 
69  result += ";\n";
70  }
71 
72  result += '}';
73  }
74 
75  return result;
76  }
77  else if(followed.id() == ID_pointer)
78  {
80  symbol, to_pointer_type(followed).base_type()) +
81  " *";
82  }
83 
84  return from_type(ns, symbol.name, type);
85 }
86 
88  const symbolt &old_symbol,
89  const symbolt &new_symbol,
90  const typet &type1,
91  const typet &type2,
92  unsigned depth,
93  exprt &conflict_path)
94 {
95  bool conclusive = false;
96 
97 #ifdef DEBUG
99  log.debug() << "<BEGIN DEPTH " << depth << ">" << messaget::eom;
100 #endif
101 
102  std::string msg;
103 
104  const typet &t1 = follow_tags_symbols(ns, type1);
105  const typet &t2 = follow_tags_symbols(ns, type2);
106 
107  if(t1.id() != t2.id())
108  {
109  msg = "type classes differ";
110  conclusive = true;
111  }
112  else if(t1.id() == ID_pointer || t1.id() == ID_array)
113  {
114  if(
115  depth > 0 &&
116  to_type_with_subtype(t1).subtype() != to_type_with_subtype(t2).subtype())
117  {
118  if(conflict_path.type().id() == ID_pointer)
119  conflict_path = dereference_exprt(conflict_path);
120 
121  conclusive = detailed_conflict_report_rec(
122  old_symbol,
123  new_symbol,
124  to_type_with_subtype(t1).subtype(),
125  to_type_with_subtype(t2).subtype(),
126  depth - 1,
127  conflict_path);
128  }
129  else if(t1.id() == ID_pointer)
130  msg = "pointer types differ";
131  else
132  msg = "array types differ";
133  }
134  else if(t1.id() == ID_struct || t1.id() == ID_union)
135  {
136  const struct_union_typet::componentst &components1 =
138 
139  const struct_union_typet::componentst &components2 =
141 
142  exprt conflict_path_before = conflict_path;
143 
144  if(components1.size() != components2.size())
145  {
146  msg = "number of members is different (";
147  msg += std::to_string(components1.size()) + '/';
148  msg += std::to_string(components2.size()) + ')';
149  conclusive = true;
150  }
151  else
152  {
153  for(std::size_t i = 0; i < components1.size(); ++i)
154  {
155  const typet &subtype1 = components1[i].type();
156  const typet &subtype2 = components2[i].type();
157 
158  if(components1[i].get_name() != components2[i].get_name())
159  {
160  msg = "names of member " + std::to_string(i) + " differ (";
161  msg += id2string(components1[i].get_name()) + '/';
162  msg += id2string(components2[i].get_name()) + ')';
163  conclusive = true;
164  }
165  else if(subtype1 != subtype2)
166  {
167  typedef std::unordered_set<typet, irep_hash> type_sett;
168  type_sett parent_types;
169 
170  exprt e = conflict_path_before;
171  while(e.id() == ID_dereference || e.id() == ID_member ||
172  e.id() == ID_index)
173  {
174  parent_types.insert(e.type());
175  parent_types.insert(follow_tags_symbols(ns, e.type()));
176  if(e.id() == ID_dereference)
177  e = to_dereference_expr(e).pointer();
178  else if(e.id() == ID_member)
179  e = to_member_expr(e).compound();
180  else if(e.id() == ID_index)
181  e = to_index_expr(e).array();
182  else
183  UNREACHABLE;
184  }
185 
186  if(parent_types.find(subtype1) == parent_types.end())
187  {
188  conflict_path = conflict_path_before;
189  conflict_path.type() = t1;
190  conflict_path = member_exprt(conflict_path, components1[i]);
191 
192  if(depth > 0)
193  {
194  conclusive = detailed_conflict_report_rec(
195  old_symbol,
196  new_symbol,
197  subtype1,
198  subtype2,
199  depth - 1,
200  conflict_path);
201  }
202  }
203  else
204  {
205  msg = "type of member " + id2string(components1[i].get_name()) +
206  " differs (recursive)";
207  }
208  }
209 
210  if(conclusive)
211  break;
212  }
213  }
214  }
215  else if(t1.id() == ID_c_enum)
216  {
217  const c_enum_typet::memberst &members1 = to_c_enum_type(t1).members();
218 
219  const c_enum_typet::memberst &members2 = to_c_enum_type(t2).members();
220 
221  if(
222  to_c_enum_type(t1).underlying_type() !=
223  to_c_enum_type(t2).underlying_type())
224  {
225  msg = "enum value types are different (";
226  msg +=
227  from_type(ns, old_symbol.name, to_c_enum_type(t1).underlying_type()) +
228  '/';
229  msg +=
230  from_type(ns, new_symbol.name, to_c_enum_type(t2).underlying_type()) +
231  ')';
232  conclusive = true;
233  }
234  else if(members1.size() != members2.size())
235  {
236  msg = "number of enum members is different (";
237  msg += std::to_string(members1.size()) + '/';
238  msg += std::to_string(members2.size()) + ')';
239  conclusive = true;
240  }
241  else
242  {
243  for(std::size_t i = 0; i < members1.size(); ++i)
244  {
245  if(members1[i].get_base_name() != members2[i].get_base_name())
246  {
247  msg = "names of member " + std::to_string(i) + " differ (";
248  msg += id2string(members1[i].get_base_name()) + '/';
249  msg += id2string(members2[i].get_base_name()) + ')';
250  conclusive = true;
251  }
252  else if(members1[i].get_value() != members2[i].get_value())
253  {
254  msg = "values of member " + std::to_string(i) + " differ (";
255  msg += id2string(members1[i].get_value()) + '/';
256  msg += id2string(members2[i].get_value()) + ')';
257  conclusive = true;
258  }
259 
260  if(conclusive)
261  break;
262  }
263  }
264 
265  msg += "\nenum declarations at\n";
266  msg += t1.source_location().as_string() + " and\n";
267  msg += t1.source_location().as_string();
268  }
269  else if(t1.id() == ID_code)
270  {
271  const code_typet::parameterst &parameters1 = to_code_type(t1).parameters();
272 
273  const code_typet::parameterst &parameters2 = to_code_type(t2).parameters();
274 
275  const typet &return_type1 = to_code_type(t1).return_type();
276  const typet &return_type2 = to_code_type(t2).return_type();
277 
278  if(parameters1.size() != parameters2.size())
279  {
280  msg = "parameter counts differ (";
281  msg += std::to_string(parameters1.size()) + '/';
282  msg += std::to_string(parameters2.size()) + ')';
283  conclusive = true;
284  }
285  else if(return_type1 != return_type2)
286  {
287  conflict_path.type() = array_typet{void_type(), nil_exprt{}};
288  conflict_path = index_exprt(
289  conflict_path, constant_exprt(std::to_string(-1), integer_typet()));
290 
291  if(depth > 0)
292  {
293  conclusive = detailed_conflict_report_rec(
294  old_symbol,
295  new_symbol,
296  return_type1,
297  return_type2,
298  depth - 1,
299  conflict_path);
300  }
301  else
302  msg = "return types differ";
303  }
304  else
305  {
306  for(std::size_t i = 0; i < parameters1.size(); i++)
307  {
308  const typet &subtype1 = parameters1[i].type();
309  const typet &subtype2 = parameters2[i].type();
310 
311  if(subtype1 != subtype2)
312  {
313  conflict_path.type() = array_typet{void_type(), nil_exprt{}};
314  conflict_path = index_exprt(
315  conflict_path, constant_exprt(std::to_string(i), integer_typet()));
316 
317  if(depth > 0)
318  {
319  conclusive = detailed_conflict_report_rec(
320  old_symbol,
321  new_symbol,
322  subtype1,
323  subtype2,
324  depth - 1,
325  conflict_path);
326  }
327  else
328  msg = "parameter types differ";
329  }
330 
331  if(conclusive)
332  break;
333  }
334  }
335  }
336  else
337  {
338  msg = "conflict on POD";
339  conclusive = true;
340  }
341 
342  if(conclusive && !msg.empty())
343  {
344 #ifndef DEBUG
346 #endif
347  log.error() << '\n';
348  log.error() << "reason for conflict at "
349  << from_expr(ns, irep_idt(), conflict_path) << ": " << msg
350  << '\n';
351 
352  log.error() << '\n';
353  log.error() << type_to_string_verbose(old_symbol, t1) << '\n';
354  log.error() << type_to_string_verbose(new_symbol, t2) << '\n'
355  << messaget::eom;
356  }
357 
358 #ifdef DEBUG
359  log.debug() << "<END DEPTH " << depth << ">" << messaget::eom;
360 #endif
361 
362  return conclusive;
363 }
364 
366  const symbolt &old_symbol,
367  const symbolt &new_symbol,
368  const std::string &msg)
369 {
371  log.error().source_location = new_symbol.location;
372 
373  log.error() << msg << " '" << old_symbol.display_name() << "'" << '\n';
374  log.error() << "old definition in module '" << old_symbol.module << "' "
375  << old_symbol.location << '\n'
376  << type_to_string_verbose(old_symbol) << '\n';
377  log.error() << "new definition in module '" << new_symbol.module << "' "
378  << new_symbol.location << '\n'
379  << type_to_string_verbose(new_symbol) << messaget::eom;
380 }
381 
383  const symbolt &old_symbol,
384  const symbolt &new_symbol,
385  const std::string &msg)
386 {
388  log.warning().source_location = new_symbol.location;
389 
390  log.warning() << msg << " '" << old_symbol.display_name() << "'\n";
391  log.warning() << "old definition in module " << old_symbol.module << " "
392  << old_symbol.location << '\n'
393  << type_to_string_verbose(old_symbol) << '\n';
394  log.warning() << "new definition in module " << new_symbol.module << " "
395  << new_symbol.location << '\n'
396  << type_to_string_verbose(new_symbol) << messaget::eom;
397 }
empty_typet void_type()
Definition: c_types.cpp:245
const c_enum_typet & to_c_enum_type(const typet &type)
Cast a typet to a c_enum_typet.
Definition: c_types.h:335
const union_tag_typet & to_union_tag_type(const typet &type)
Cast a typet to a union_tag_typet.
Definition: c_types.h:224
const c_enum_tag_typet & to_c_enum_tag_type(const typet &type)
Cast a typet to a c_enum_tag_typet.
Definition: c_types.h:377
Arrays with given size.
Definition: std_types.h:807
memberst & members()
Definition: c_types.h:283
std::vector< c_enum_membert > memberst
Definition: c_types.h:275
std::vector< parametert > parameterst
Definition: std_types.h:585
const typet & return_type() const
Definition: std_types.h:689
const parameterst & parameters() const
Definition: std_types.h:699
A constant literal expression.
Definition: std_expr.h:2995
Operator to dereference a pointer.
Definition: pointer_expr.h:834
Base class for all expressions.
Definition: expr.h:56
typet & type()
Return the type of the expression.
Definition: expr.h:84
Array index operator.
Definition: std_expr.h:1470
exprt & array()
Definition: std_expr.h:1500
Unbounded, signed integers (mathematical integers, not bitvectors)
const std::string & id_string() const
Definition: irep.h:391
const irep_idt & id() const
Definition: irep.h:388
const std::string & get_string(const irep_idt &name) const
Definition: irep.h:401
message_handlert & message_handler
const namespacet & ns
std::string type_to_string_verbose(const symbolt &symbol, const typet &type) const
void warning(const symbolt &old_symbol, const symbolt &new_symbol, const std::string &msg)
bool detailed_conflict_report_rec(const symbolt &old_symbol, const symbolt &new_symbol, const typet &type1, const typet &type2, unsigned depth, exprt &conflict_path)
Returns true iff the conflict report on a particular branch of the tree of types was a definitive res...
void error(const symbolt &old_symbol, const symbolt &new_symbol, const std::string &msg)
Extract member of struct or union.
Definition: std_expr.h:2849
const exprt & compound() const
Definition: std_expr.h:2898
Class that provides messages with a built-in verbosity 'level'.
Definition: message.h:154
static eomt eom
Definition: message.h:289
const union_typet & follow_tag(const union_tag_typet &) const
Follow type tag of union type.
Definition: namespace.cpp:63
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition: namespace.h:94
The NIL expression.
Definition: std_expr.h:3086
std::string as_string() const
const componentst & components() const
Definition: std_types.h:147
std::vector< componentt > componentst
Definition: std_types.h:140
Symbol table entry.
Definition: symbol.h:28
const irep_idt & display_name() const
Return language specific display name if present.
Definition: symbol.h:55
irep_idt module
Name of module the symbol belongs to.
Definition: symbol.h:43
source_locationt location
Source code location of definition of symbol.
Definition: symbol.h:37
irep_idt name
The unique identifier.
Definition: symbol.h:40
The type of an expression, extends irept.
Definition: type.h:29
const source_locationt & source_location() const
Definition: type.h:72
std::string get_base_name(const std::string &in, bool strip_suffix)
cleans a filename from path and extension
const std::string & id2string(const irep_idt &d)
Definition: irep.h:44
std::string from_expr(const namespacet &ns, const irep_idt &identifier, const exprt &expr)
std::string from_type(const namespacet &ns, const irep_idt &identifier, const typet &type)
static const typet & follow_tags_symbols(const namespacet &ns, const typet &type)
ANSI-C Linking.
double log(double x)
Definition: math.c:2776
Mathematical types.
const dereference_exprt & to_dereference_expr(const exprt &expr)
Cast an exprt to a dereference_exprt.
Definition: pointer_expr.h:890
const pointer_typet & to_pointer_type(const typet &type)
Cast a typet to a pointer_typet.
Definition: pointer_expr.h:93
#define UNREACHABLE
This should be used to mark dead code.
Definition: invariant.h:525
const member_exprt & to_member_expr(const exprt &expr)
Cast an exprt to a member_exprt.
Definition: std_expr.h:2941
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition: std_expr.h:1538
const code_typet & to_code_type(const typet &type)
Cast a typet to a code_typet.
Definition: std_types.h:788
const struct_union_typet & to_struct_union_type(const typet &type)
Cast a typet to a struct_union_typet.
Definition: std_types.h:214
const struct_tag_typet & to_struct_tag_type(const typet &type)
Cast a typet to a struct_tag_typet.
Definition: std_types.h:518
std::string to_string(const string_not_contains_constraintt &expr)
Used for debug printing.
const type_with_subtypet & to_type_with_subtype(const typet &type)
Definition: type.h:208
dstringt irep_idt