bool
has_data_member_replaced_by_anon_dm(const diff* diff);
+bool
+is_var_1_dim_unknown_size_array_change(const diff*);
+
+bool
+is_var_1_dim_unknown_size_array_change(const var_decl_sptr& var1,
+ const var_decl_sptr& var2);
+
struct filter_base;
/// Convenience typedef for a shared pointer to filter_base
typedef shared_ptr<filter_base> filter_base_sptr;
friend bool
equals(const var_decl&, const var_decl&, change_kind*);
+ friend bool
+ var_equals_modulo_types(const var_decl&, const var_decl&, change_kind*);
+
friend bool
maybe_compare_as_member_decls(const decl_base& l,
const decl_base& r,
bool
equals(const var_decl&, const var_decl&, change_kind*);
+bool
+var_equals_modulo_types(const var_decl&, const var_decl&, change_kind*);
+
bool
equals_modulo_cv_qualifier(const array_type_def*, const array_type_def*);
return !c->data_members_replaced_by_adms().empty();
}
+/// Test if we are looking at two variables which types are both one
+/// dimension array, with one of them being of unknow size and the two
+/// variables having the same symbol size.
+///
+/// This can happen in the case of these two declarations, for instance:
+///
+/// unsigned int array[];
+///
+/// and:
+///
+/// unsigned int array[] ={0};
+///
+/// In both cases, the size of the ELF symbol of the variable 'array'
+/// is 32 bits, but, at least in the first case
+bool
+is_var_1_dim_unknown_size_array_change(const var_decl_sptr& var1,
+ const var_decl_sptr& var2)
+{
+ type_base_sptr /*first type*/ft =
+ peel_qualified_or_typedef_type(var1->get_type());
+ type_base_sptr /*second type*/st =
+ peel_qualified_or_typedef_type(var2->get_type());
+
+ array_type_def_sptr /*first array type*/fat = is_array_type(ft);
+ array_type_def_sptr /*second array type*/sat = is_array_type(st);
+
+ // The types of the variables must be arrays.
+ if (!fat || !sat)
+ return false;
+
+ // The arrays must have one dimension and at least one of them must
+ // be of unknown size.
+ if (fat->get_subranges().size() != 1
+ || sat->get_subranges().size() != 1
+ || (!fat->is_infinite() && !sat->is_infinite()))
+ return false;
+
+ // The variables must be equal modulo their type.
+ if (!var_equals_modulo_types(*var1, *var2, nullptr))
+ return false;
+
+ // The symbols of the variables must be defined and of the same
+ // non-zero size.
+ if (!var1->get_symbol()
+ || !var2->get_symbol()
+ || var1->get_symbol()->get_size() != var2->get_symbol()->get_size())
+ return false;
+
+ return true;
+}
+
+/// Test if we are looking at a diff that carries a change of
+/// variables which types are both one dimension array, with one of
+/// them being of unknow size and the two variables having the same
+/// symbol size.
+///
+/// This can happen in the case of these two declarations, for instance:
+///
+/// unsigned int array[];
+///
+/// and:
+///
+/// unsigned int array[] ={0};
+///
+/// In both cases, the size of the ELF symbol of the variable 'array'
+/// is 32 bits, but, at least in the first case
+bool
+is_var_1_dim_unknown_size_array_change(const diff* diff)
+{
+ const var_diff* d = is_var_diff(diff);
+
+ if (!d)
+ return false;
+
+ var_decl_sptr f = d->first_var(), s = d->second_var();
+
+ return is_var_1_dim_unknown_size_array_change(f, s);
+}
+
/// Test if a class_diff node has static members added or removed.
///
/// @param diff the diff node to consider.
///
/// @return true iff @p dif contains the benign array type size change.
static bool
-has_benign_infinite_array_change(const diff* dif)
+has_benign_array_of_unknown_size_change(const diff* dif)
{
- if (const var_diff* var_dif = is_var_diff(dif))
- {
- if (!var_dif->first_var()->get_symbol()
- || var_dif->second_var()->get_symbol())
- return false;
-
- if (var_dif->first_var()->get_symbol()->get_size()
- != var_dif->second_var()->get_symbol()->get_size())
- return false;
-
- const diff *d = var_dif->type_diff().get();
- if (!d)
- return false;
- d = peel_qualified_diff(d);
- if (const array_diff *a = is_array_diff(d))
- {
- array_type_def_sptr f = a->first_array(), s = a->second_array();
- if (f->is_infinite() != s->is_infinite())
- return true;
- }
- }
- return false;
+ return is_var_1_dim_unknown_size_array_change(dif);
}
/// Test if a union diff node does have changes that don't impact its
if (has_void_ptr_to_ptr_change(d))
category |= VOID_PTR_TO_PTR_CHANGE_CATEGORY;
- if (has_benign_infinite_array_change(d))
+ if (has_benign_array_of_unknown_size_change(d))
category |= BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY;
if (category)
maybe_report_diff_for_member(first, second, d.context(), out, indent);
+ maybe_report_diff_for_variable(first, second, d.context(), out, indent);
+
if (diff_sptr dif = d.type_diff())
{
if (dif->to_be_reported())
get_context_rel()->set_scope(scope);
}
-/// Compares two instances of @ref var_decl.
+/// Compares two instances of @ref var_decl without taking their type
+/// into account.
///
-/// If the two intances are different, set a bitfield to give some
-/// insight about the kind of differences there are.
+/// If the two intances are different modulo their type, set a
+/// bitfield to give some insight about the kind of differences there
+/// are.
///
/// @param l the first artifact of the comparison.
///
///
/// @return true if @p l equals @p r, false otherwise.
bool
-equals(const var_decl& l, const var_decl& r, change_kind* k)
+var_equals_modulo_types(const var_decl& l, const var_decl& r, change_kind* k)
{
bool result = true;
- // First test types of variables. This should be fast because in
- // the general case, most types should be canonicalized.
- if (*l.get_naked_type() != *r.get_naked_type())
- {
- result = false;
- if (k)
- {
- if (!types_have_similar_structure(l.get_naked_type(),
- r.get_naked_type()))
- *k |= (LOCAL_TYPE_CHANGE_KIND);
- else
- *k |= SUBTYPE_CHANGE_KIND;
- }
- else
- ABG_RETURN_FALSE;
- }
-
// If there are underlying elf symbols for these variables,
// compare them. And then compare the other parts.
const elf_symbol_sptr &s0 = l.get_symbol(), &s1 = r.get_symbol();
ABG_RETURN(result);
}
+/// Compares two instances of @ref var_decl.
+///
+/// If the two intances are different, set a bitfield to give some
+/// insight about the kind of differences there are.
+///
+/// @param l the first artifact of the comparison.
+///
+/// @param r the second artifact of the comparison.
+///
+/// @param k a pointer to a bitfield that gives information about the
+/// kind of changes there are between @p l and @p r. This one is set
+/// iff @p k is non-null and the function returns false.
+///
+/// Please note that setting k to a non-null value does have a
+/// negative performance impact because even if @p l and @p r are not
+/// equal, the function keeps up the comparison in order to determine
+/// the different kinds of ways in which they are different.
+///
+/// @return true if @p l equals @p r, false otherwise.
+bool
+equals(const var_decl& l, const var_decl& r, change_kind* k)
+{
+ bool result = true;
+
+ // First test types of variables. This should be fast because in
+ // the general case, most types should be canonicalized.
+ if (*l.get_naked_type() != *r.get_naked_type())
+ {
+ result = false;
+ if (k)
+ {
+ if (!types_have_similar_structure(l.get_naked_type(),
+ r.get_naked_type()))
+ *k |= (LOCAL_TYPE_CHANGE_KIND);
+ else
+ *k |= SUBTYPE_CHANGE_KIND;
+ }
+ else
+ ABG_RETURN_FALSE;
+ }
+
+ result &= var_equals_modulo_types(l, r, k);
+
+ ABG_RETURN(result);
+}
+
/// Comparison operator of @ref var_decl.
///
/// @param o the instance of @ref var_decl to compare against.
return reported;
}
+/// Report the differences between two generic variables.
+///
+/// @param decl1 the first version of the variable.
+///
+/// @param decl2 the second version of the variable.
+///
+/// @param ctxt the context of the diff.
+///
+/// @param out the output stream to emit the change report to.
+///
+/// @param indent the indentation prefix to emit.
+///
+/// @return true if any text has been emitted to the output stream.
+bool
+maybe_report_diff_for_variable(const decl_base_sptr& decl1,
+ const decl_base_sptr& decl2,
+ const diff_context_sptr& ctxt,
+ ostream& out,
+ const string& indent)
+{
+ bool reported = false;
+
+ var_decl_sptr var1 = is_var_decl(decl1);
+ var_decl_sptr var2 = is_var_decl(decl2);
+
+ if (!var1 || !var2)
+ return reported;
+
+ if (filtering::is_var_1_dim_unknown_size_array_change(var1, var2))
+ {
+ uint64_t var_size_in_bits = var1->get_symbol()->get_size() * 8;
+
+ out << indent;
+ show_offset_or_size("size of variable symbol (",
+ var_size_in_bits, *ctxt, out);
+ out << ") hasn't changed\n"
+ << indent << "but it does have a harmless type change\n";
+ reported = true;
+ }
+
+ return reported;
+}
+
/// Report the difference between two ELF symbols, if there is any.
///
/// @param symbol1 the first symbol to consider.
ostream& out,
const string& indent);
+bool
+maybe_report_diff_for_variable(const decl_base_sptr& decl1,
+ const decl_base_sptr& decl2,
+ const diff_context_sptr& ctxt,
+ ostream& out,
+ const string& indent);
+
void
maybe_report_diff_for_symbol(const elf_symbol_sptr& symbol1,
const elf_symbol_sptr& symbol2,
test-diff-filter/test-PR29811-unknown-size-array-dwarf-ctf-DWARF.o \
test-diff-filter/test-PR29811-unknown-size-array-dwarf-ctf-report.txt \
test-diff-filter/test-PR29811-unknown-size-array-dwarf-ctf.c \
+test-diff-filter/test-PR29811-0-report-0.txt \
+test-diff-filter/test-PR29811-0-report-1.txt \
+test-diff-filter/test-PR29811-0-v0.o \
+test-diff-filter/test-PR29811-0-v1.o \
+test-diff-filter/test-PR29811-0-v0.c \
+test-diff-filter/test-PR29811-0-v1.c \
\
test-diff-suppr/test0-type-suppr-v0.cc \
test-diff-suppr/test0-type-suppr-v1.cc \
--- /dev/null
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 0 Changed (1 filtered out), 0 Added variable
+
--- /dev/null
+Functions changes summary: 0 Removed, 0 Changed, 0 Added function
+Variables changes summary: 0 Removed, 1 Changed, 0 Added variable
+
+1 Changed variable:
+
+ [C] 'unsigned int is_basic_table[]' was changed to 'unsigned int is_basic_table[1]' at test-PR29811-0-v1.c:1:1:
+ size of variable symbol ( 32 (in bits)) hasn't changed
+ but it does have a harmless type change
+ type of variable changed:
+ type name changed from 'unsigned int[]' to 'unsigned int[1]'
+ array type size changed from 'unknown' to 32
+ array type subrange 1 changed length from 'unknown' to 1
+
--- /dev/null
+unsigned int is_basic_table[];
--- /dev/null
+unsigned int is_basic_table[] = {0};
"data/test-diff-filter/test-PR29387-report.txt",
"output/test-diff-filter/test-PR29387-report.txt",
},
+ {
+ "data/test-diff-filter/test-PR29811-0-v0.o",
+ "data/test-diff-filter/test-PR29811-0-v1.o",
+ "--no-default-suppression",
+ "data/test-diff-filter/test-PR29811-0-report-0.txt",
+ "output/test-diff-filter/test-PR29811-0-report-0.txt",
+ },
+ {
+ "data/test-diff-filter/test-PR29811-0-v0.o",
+ "data/test-diff-filter/test-PR29811-0-v1.o",
+ "--harmless --no-default-suppression",
+ "data/test-diff-filter/test-PR29811-0-report-1.txt",
+ "output/test-diff-filter/test-PR29811-0-report-1.txt",
+ },
#ifdef WITH_CTF
{
"data/test-diff-filter/test-PR29811-unknown-size-array-dwarf-ctf-DWARF.o",