Declaration-only classes shouldn't have canonical types

I am seeing issues related to the fact that a declaration-only class A
would compare different to the full version of class A.  This is due
to the fact that that the declaration-only A and the full A have
different hashes, even though they structurally compare equal.  So
they have different canonical types, with the current code.  This
patch arranges for declaration-only classes to have no canonical type,
forcing it to compare structurally to other types.  Then the patch
adjusts strip_typedef() that used to expect that all types it sees
have canonical types.  Then the patch changes the type hashing code to
avoid making it cache their hash, because otherwise, in some cases
when we hash a type (too) early, a temporary hash of it gets stored ad
infinitum, even after the type has been later updated.  Last but not
least, the patch returns a zero hash for declaration-only classes.

	* include/abg-fwd.h (keep_type_alive): Declare new function.
	* src/abg-ir.cc (strip_typedef): Simplify logic.  Support types
	that are not canonicalized.
	(type_base::get_canonical_type_for): For declaration-only classes,
	return an empty canonical class, forcing the class to be compared
	structurally.
	(keep_type_alive): Define new function.
	* src/abg-hash.cc ({decl_base, type_decl, scope_type_decl,
	qualified_type_def, pointer_type_def, reference_type_def,
	array_type_def, enum_type_decl, typedef_decl,
	class_decl::member_class_template, class_decl, type_tparameter,
	template_tparameter, }:#️⃣:operator()): Do not cache the
	computed hash.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
Dodji Seketeli 2015-03-13 17:14:03 +01:00
parent 5244794279
commit 7f1dd88907
3 changed files with 212 additions and 234 deletions

View File

@ -496,6 +496,9 @@ canonicalize(shared_ptr<type_base>);
bool
type_has_non_canonicalized_subtype(shared_ptr<type_base> t);
void
keep_type_alive(shared_ptr<type_base> t);
} // end namespace ir
using namespace abigail::ir;

View File

@ -104,23 +104,19 @@ struct decl_base::hash
size_t
operator()(const decl_base& d) const
{
if (d.peek_hash_value() == 0)
{
std::tr1::hash<string> str_hash;
std::tr1::hash<string> str_hash;
size_t v = str_hash(typeid(d).name());
if (!d.get_linkage_name().empty())
v = hashing::combine_hashes(v, str_hash(d.get_linkage_name()));
else if (!d.get_name().empty())
v = hashing::combine_hashes(v, str_hash(d.get_qualified_name()));
if (is_member_decl(d))
{
v = hashing::combine_hashes(v, get_member_access_specifier(d));
v = hashing::combine_hashes(v, get_member_is_static(d));
}
d.set_hash(v);
size_t v = str_hash(typeid(d).name());
if (!d.get_linkage_name().empty())
v = hashing::combine_hashes(v, str_hash(d.get_linkage_name()));
else if (!d.get_name().empty())
v = hashing::combine_hashes(v, str_hash(d.get_qualified_name()));
if (is_member_decl(d))
{
v = hashing::combine_hashes(v, get_member_access_specifier(d));
v = hashing::combine_hashes(v, get_member_is_static(d));
}
return d.peek_hash_value();
return v;
}
}; // end struct decl_base::hash
@ -129,18 +125,15 @@ struct type_decl::hash
size_t
operator()(const type_decl& t) const
{
if (t.peek_hash_value() == 0 || t.hashing_started())
{
decl_base::hash decl_hash;
type_base::hash type_hash;
std::tr1::hash<string> str_hash;
decl_base::hash decl_hash;
type_base::hash type_hash;
std::tr1::hash<string> str_hash;
size_t v = str_hash(typeid(t).name());
v = hashing::combine_hashes(v, decl_hash(t));
v = hashing::combine_hashes(v, type_hash(t));
t.set_hash(v);
}
return t.peek_hash_value();
size_t v = str_hash(typeid(t).name());
v = hashing::combine_hashes(v, decl_hash(t));
v = hashing::combine_hashes(v, type_hash(t));
return v;
}
};
@ -180,19 +173,15 @@ struct scope_type_decl::hash
size_t
operator()(const scope_type_decl& t) const
{
if (t.peek_hash_value() == 0 || t.hashing_started())
{
decl_base::hash decl_hash;
type_base::hash type_hash;
std::tr1::hash<string> str_hash;
decl_base::hash decl_hash;
type_base::hash type_hash;
std::tr1::hash<string> str_hash;
size_t v = str_hash(typeid(t).name());
v = hashing::combine_hashes(v, decl_hash(t));
v = hashing::combine_hashes(v, type_hash(t));
t.set_hash(v);
}
size_t v = str_hash(typeid(t).name());
v = hashing::combine_hashes(v, decl_hash(t));
v = hashing::combine_hashes(v, type_hash(t));
return t.peek_hash_value();
return v;
}
};
@ -201,19 +190,15 @@ struct qualified_type_def::hash
size_t
operator()(const qualified_type_def& t) const
{
if (t.peek_hash_value() == 0 || t.hashing_started())
{
type_base::hash type_hash;
decl_base::hash decl_hash;
std::tr1::hash<string> str_hash;
type_base::hash type_hash;
decl_base::hash decl_hash;
std::tr1::hash<string> str_hash;
size_t v = str_hash(typeid(t).name());
v = hashing::combine_hashes(v, type_hash(t));
v = hashing::combine_hashes(v, decl_hash(t));
v = hashing::combine_hashes(v, t.get_cv_quals());
t.set_hash(v);
}
return t.peek_hash_value();
size_t v = str_hash(typeid(t).name());
v = hashing::combine_hashes(v, type_hash(t));
v = hashing::combine_hashes(v, decl_hash(t));
v = hashing::combine_hashes(v, t.get_cv_quals());
return v;
}
};
@ -222,21 +207,16 @@ struct pointer_type_def::hash
size_t
operator()(const pointer_type_def& t) const
{
if (t.peek_hash_value() == 0 || t.hashing_started())
{
std::tr1::hash<string> str_hash;
type_base::hash type_base_hash;
decl_base::hash decl_hash;
type_base::shared_ptr_hash hash_type_ptr;
std::tr1::hash<string> str_hash;
type_base::hash type_base_hash;
decl_base::hash decl_hash;
type_base::shared_ptr_hash hash_type_ptr;
size_t v = str_hash(typeid(t).name());
v = hashing::combine_hashes(v, decl_hash(t));
v = hashing::combine_hashes(v, type_base_hash(t));
v = hashing::combine_hashes(v, hash_type_ptr(t.get_pointed_to_type()));
t.set_hash(v);
}
return t.peek_hash_value();
size_t v = str_hash(typeid(t).name());
v = hashing::combine_hashes(v, decl_hash(t));
v = hashing::combine_hashes(v, type_base_hash(t));
v = hashing::combine_hashes(v, hash_type_ptr(t.get_pointed_to_type()));
return v ;
}
};
@ -245,24 +225,19 @@ struct reference_type_def::hash
size_t
operator()(const reference_type_def& t)
{
if (t.peek_hash_value() == 0 || t.hashing_started())
{
std::tr1::hash<string> hash_str;
type_base::hash hash_type_base;
decl_base::hash hash_decl;
type_base::shared_ptr_hash hash_type_ptr;
std::tr1::hash<string> hash_str;
type_base::hash hash_type_base;
decl_base::hash hash_decl;
type_base::shared_ptr_hash hash_type_ptr;
size_t v = hash_str(typeid(t).name());
v = hashing::combine_hashes(v, hash_str(t.is_lvalue()
? "lvalue"
: "rvalue"));
v = hashing::combine_hashes(v, hash_type_base(t));
v = hashing::combine_hashes(v, hash_decl(t));
v = hashing::combine_hashes(v, hash_type_ptr(t.get_pointed_to_type()));
t.set_hash(v);
}
return t.peek_hash_value();
size_t v = hash_str(typeid(t).name());
v = hashing::combine_hashes(v, hash_str(t.is_lvalue()
? "lvalue"
: "rvalue"));
v = hashing::combine_hashes(v, hash_type_base(t));
v = hashing::combine_hashes(v, hash_decl(t));
v = hashing::combine_hashes(v, hash_type_ptr(t.get_pointed_to_type()));
return v;
}
};
@ -283,29 +258,25 @@ struct array_type_def::hash
size_t
operator()(const array_type_def& t)
{
if (t.peek_hash_value() == 0 || t.hashing_started())
{
std::tr1::hash<string> hash_str;
type_base::hash hash_type_base;
decl_base::hash hash_decl;
type_base::shared_ptr_hash hash_type_ptr;
array_type_def::subrange_type::hash hash_subrange;
std::tr1::hash<string> hash_str;
type_base::hash hash_type_base;
decl_base::hash hash_decl;
type_base::shared_ptr_hash hash_type_ptr;
array_type_def::subrange_type::hash hash_subrange;
size_t v = hash_str(typeid(t).name());
size_t v = hash_str(typeid(t).name());
v = hashing::combine_hashes(v, hash_type_base(t));
v = hashing::combine_hashes(v, hash_decl(t));
v = hashing::combine_hashes(v, hash_type_ptr(t.get_element_type()));
v = hashing::combine_hashes(v, hash_type_base(t));
v = hashing::combine_hashes(v, hash_decl(t));
v = hashing::combine_hashes(v, hash_type_ptr(t.get_element_type()));
for (vector<array_type_def::subrange_sptr >::const_iterator i =
t.get_subranges().begin();
i != t.get_subranges().end();
++i)
v = hashing::combine_hashes(v, hash_subrange(**i));
for (vector<array_type_def::subrange_sptr >::const_iterator i =
t.get_subranges().begin();
i != t.get_subranges().end();
++i)
v = hashing::combine_hashes(v, hash_subrange(**i));
t.set_hash(v);
}
return t.peek_hash_value();
return v;
}
};
@ -314,27 +285,23 @@ struct enum_type_decl::hash
size_t
operator()(const enum_type_decl& t) const
{
if (t.peek_hash_value() == 0 || t.hashing_started())
{
std::tr1::hash<string> str_hash;
decl_base::hash decl_hash;
type_base::shared_ptr_hash type_ptr_hash;
std::tr1::hash<size_t> size_t_hash;
std::tr1::hash<string> str_hash;
decl_base::hash decl_hash;
type_base::shared_ptr_hash type_ptr_hash;
std::tr1::hash<size_t> size_t_hash;
size_t v = str_hash(typeid(t).name());
v = hashing::combine_hashes(v, decl_hash(t));
v = hashing::combine_hashes(v, type_ptr_hash(t.get_underlying_type()));
for (enum_type_decl::enumerators::const_iterator i =
t.get_enumerators().begin();
i != t.get_enumerators().end();
++i)
{
v = hashing::combine_hashes(v, str_hash(i->get_name()));
v = hashing::combine_hashes(v, size_t_hash(i->get_value()));
}
t.set_hash(v);
size_t v = str_hash(typeid(t).name());
v = hashing::combine_hashes(v, decl_hash(t));
v = hashing::combine_hashes(v, type_ptr_hash(t.get_underlying_type()));
for (enum_type_decl::enumerators::const_iterator i =
t.get_enumerators().begin();
i != t.get_enumerators().end();
++i)
{
v = hashing::combine_hashes(v, str_hash(i->get_name()));
v = hashing::combine_hashes(v, size_t_hash(i->get_value()));
}
return t.peek_hash_value();
return v;
}
};
@ -343,22 +310,17 @@ struct typedef_decl::hash
size_t
operator()(const typedef_decl& t) const
{
if (t.peek_hash_value() == 0 || t.hashing_started())
{
std::tr1::hash<string> str_hash;
type_base::hash hash_type;
decl_base::hash decl_hash;
type_base::shared_ptr_hash type_ptr_hash;
std::tr1::hash<string> str_hash;
type_base::hash hash_type;
decl_base::hash decl_hash;
type_base::shared_ptr_hash type_ptr_hash;
size_t v = str_hash(typeid(t).name());
v = hashing::combine_hashes(v, hash_type(t));
v = hashing::combine_hashes(v, decl_hash(t));
v = hashing::combine_hashes(v, type_ptr_hash(t.get_underlying_type()));
t.set_hash(v);
}
return t.peek_hash_value();
}
size_t v = str_hash(typeid(t).name());
v = hashing::combine_hashes(v, hash_type(t));
v = hashing::combine_hashes(v, decl_hash(t));
v = hashing::combine_hashes(v, type_ptr_hash(t.get_underlying_type()));
return v;
}
};
/// Compute a hash for an instance @ref var_decl.
@ -628,79 +590,75 @@ class_decl::member_class_template::hash::operator()
size_t
class_decl::hash::operator()(const class_decl& t) const
{
if (t.hashing_started())
return 0;
if (t.hashing_started() || t.get_is_declaration_only())
return 0;
if (t.peek_hash_value() == 0)
{
std::tr1::hash<string> hash_string;
std::tr1::hash<bool> hash_bool;
std::tr1::hash<string> hash_string;
std::tr1::hash<bool> hash_bool;
#if 0
type_base::dynamic_hash hash_type;
type_base::dynamic_hash hash_type;
#endif
scope_type_decl::hash hash_scope_type;
class_decl::base_spec::hash hash_base;
var_decl::hash hash_data_member;
function_decl::hash hash_member_fn;
class_decl::member_function_template::hash hash_member_fn_tmpl;
class_decl::member_class_template::hash hash_member_class_tmpl;
scope_type_decl::hash hash_scope_type;
class_decl::base_spec::hash hash_base;
var_decl::hash hash_data_member;
function_decl::hash hash_member_fn;
class_decl::member_function_template::hash hash_member_fn_tmpl;
class_decl::member_class_template::hash hash_member_class_tmpl;
size_t v = hash_string(typeid(t).name());
v = hashing::combine_hashes(v, hash_scope_type(t));
v = hashing::combine_hashes(v, hash_bool(t.get_is_declaration_only()));
size_t v = hash_string(typeid(t).name());
v = hashing::combine_hashes(v, hash_scope_type(t));
v = hashing::combine_hashes(v, hash_bool(t.get_is_declaration_only()));
t.hashing_started(true);
t.hashing_started(true);
// Hash bases.
for (class_decl::base_specs::const_iterator b =
t.get_base_specifiers().begin();
b != t.get_base_specifiers().end();
++b)
v = hashing::combine_hashes(v, hash_base(**b));
// Hash bases.
for (class_decl::base_specs::const_iterator b =
t.get_base_specifiers().begin();
b != t.get_base_specifiers().end();
++b)
v = hashing::combine_hashes(v, hash_base(**b));
// Hash member types.
// Hash member types.
#if 0
for (class_decl::member_types::const_iterator ti =
t.get_member_types().begin();
ti != t.get_member_types().end();
++ti)
v = hashing::combine_hashes(v, hash_type((*ti).get()));
for (class_decl::member_types::const_iterator ti =
t.get_member_types().begin();
ti != t.get_member_types().end();
++ti)
v = hashing::combine_hashes(v, hash_type((*ti).get()));
#endif
// Hash data members.
for (class_decl::data_members::const_iterator d =
t.get_data_members().begin();
d != t.get_data_members().end();
++d)
v = hashing::combine_hashes(v, hash_data_member(**d));
// Hash data members.
for (class_decl::data_members::const_iterator d =
t.get_data_members().begin();
d != t.get_data_members().end();
++d)
v = hashing::combine_hashes(v, hash_data_member(**d));
// Hash member_function
for (class_decl::member_functions::const_iterator f =
t.get_member_functions().begin();
f != t.get_member_functions().end();
++f)
if (get_member_function_is_virtual(*f))
v = hashing::combine_hashes(v, hash_member_fn(**f));
// Hash member_function
for (class_decl::member_functions::const_iterator f =
t.get_member_functions().begin();
f != t.get_member_functions().end();
++f)
if (get_member_function_is_virtual(*f))
v = hashing::combine_hashes(v, hash_member_fn(**f));
// Hash member function templates
for (class_decl::member_function_templates::const_iterator f =
t.get_member_function_templates().begin();
f != t.get_member_function_templates().end();
++f)
v = hashing::combine_hashes(v, hash_member_fn_tmpl(**f));
// Hash member function templates
for (class_decl::member_function_templates::const_iterator f =
t.get_member_function_templates().begin();
f != t.get_member_function_templates().end();
++f)
v = hashing::combine_hashes(v, hash_member_fn_tmpl(**f));
// Hash member class templates
for (class_decl::member_class_templates::const_iterator c =
t.get_member_class_templates().begin();
c != t.get_member_class_templates().end();
++c)
v = hashing::combine_hashes(v, hash_member_class_tmpl(**c));
// Hash member class templates
for (class_decl::member_class_templates::const_iterator c =
t.get_member_class_templates().begin();
c != t.get_member_class_templates().end();
++c)
v = hashing::combine_hashes(v, hash_member_class_tmpl(**c));
t.hashing_started(false);
t.hashing_started(false);
t.set_hash(v);
}
return t.peek_hash_value();
return v;
}
/// Compute a hash for a @ref class_decl
@ -779,19 +737,15 @@ struct type_tparameter::hash
size_t
operator()(const type_tparameter& t) const
{
if (t.peek_hash_value() == 0 || t.hashing_started())
{
std::tr1::hash<string> hash_string;
template_parameter::hash hash_template_parameter;
type_decl::hash hash_type;
std::tr1::hash<string> hash_string;
template_parameter::hash hash_template_parameter;
type_decl::hash hash_type;
size_t v = hash_string(typeid(t).name());
v = hashing::combine_hashes(v, hash_template_parameter(t));
v = hashing::combine_hashes(v, hash_type(t));
size_t v = hash_string(typeid(t).name());
v = hashing::combine_hashes(v, hash_template_parameter(t));
v = hashing::combine_hashes(v, hash_type(t));
t.set_hash(v);
}
return t.peek_hash_value();
return v;
}
};
@ -829,19 +783,15 @@ struct template_tparameter::hash
size_t
operator()(const template_tparameter& t) const
{
if (t.peek_hash_value() == 0 || t.hashing_started())
{
std::tr1::hash<string> hash_string;
type_tparameter::hash hash_template_type_parm;
template_decl::hash hash_template_decl;
std::tr1::hash<string> hash_string;
type_tparameter::hash hash_template_type_parm;
template_decl::hash hash_template_decl;
size_t v = hash_string(typeid(t).name());
v = hashing::combine_hashes(v, hash_template_type_parm(t));
v = hashing::combine_hashes(v, hash_template_decl(t));
t.set_hash(v);
}
size_t v = hash_string(typeid(t).name());
v = hashing::combine_hashes(v, hash_template_type_parm(t));
v = hashing::combine_hashes(v, hash_template_decl(t));
return t.peek_hash_value();
return v;
}
};

View File

@ -2316,11 +2316,16 @@ set_member_function_is_virtual(const function_decl_sptr& fn, bool is_virtual)
/// Also recursively strip typedefs from the sub-types of the type
/// given in arguments.
///
/// Note that this function can only acts on types that are
/// canonicalized. The returned type is a new type that is stripped
/// from all of its typedefs. It's actually a canonical type that is
/// guaranteed to be live for during the entire life time of this
/// library.
/// Note that this function builds types in which typedefs are
/// stripped off. Usually, types are held by their scope, so their
/// life time is bound to the life time of their scope. But as this
/// function cannot really insert the built type into it's scope, it
/// must ensure that the newly built type stays live long enough.
///
/// So, if the newly built type has a canonical type, this function
/// returns the canonical type. Otherwise, this function ensure that
/// the newly built type has a life time that is the same as the life
/// time of the entire libabigail library.
///
/// @param type the type to strip the typedefs from.
///
@ -2332,6 +2337,17 @@ strip_typedef(const type_base_sptr type)
if (!type)
return type;
// If type is a class type the do not try to strip typedefs from it.
// And if it has no canonical type (which can mean that it's a
// declaration-only class), then, make sure its live for ever and
// return it.
if (class_decl_sptr cl = is_class_type(type))
{
if (!cl->get_canonical_type())
keep_type_alive(type);
return type;
}
assert(type->get_canonical_type());
type_base_sptr t = type;
@ -2340,8 +2356,7 @@ strip_typedef(const type_base_sptr type)
t = strip_typedef(type_or_void(ty->get_underlying_type()));
else if (const reference_type_def_sptr ty = is_reference_type(t))
{
type_base_sptr p =
canonicalize(strip_typedef(type_or_void(ty->get_pointed_to_type())));
type_base_sptr p = strip_typedef(type_or_void(ty->get_pointed_to_type()));
assert(p);
t.reset(new reference_type_def(p,
ty->is_lvalue(),
@ -2351,8 +2366,7 @@ strip_typedef(const type_base_sptr type)
}
else if (const pointer_type_def_sptr ty = is_pointer_type(t))
{
type_base_sptr p =
canonicalize(strip_typedef(type_or_void(ty->get_pointed_to_type())));
type_base_sptr p = strip_typedef(type_or_void(ty->get_pointed_to_type()));
assert(p);
t.reset(new pointer_type_def(p,
ty->get_size_in_bits(),
@ -2361,7 +2375,7 @@ strip_typedef(const type_base_sptr type)
}
else if (const qualified_type_def_sptr ty = is_qualified_type(t))
{
type_base_sptr p = canonicalize(strip_typedef(ty->get_underlying_type()));
type_base_sptr p = strip_typedef(type_or_void(ty->get_underlying_type()));
assert(p);
t.reset(new qualified_type_def(p,
ty->get_cv_quals(),
@ -2369,7 +2383,8 @@ strip_typedef(const type_base_sptr type)
}
else if (const array_type_def_sptr ty = is_array_type(t))
{
type_base_sptr p = canonicalize(strip_typedef(ty->get_element_type()));
type_base_sptr p = strip_typedef(ty->get_element_type());
assert(p);
t.reset(new array_type_def(p, ty->get_subranges(), ty->get_location()));
}
else if (const method_type_sptr ty = is_method_type(t))
@ -2381,7 +2396,7 @@ strip_typedef(const type_base_sptr type)
++i)
{
function_decl::parameter_sptr p = *i;
type_base_sptr typ = canonicalize(strip_typedef(p->get_type()));
type_base_sptr typ = strip_typedef(p->get_type());
assert(typ);
function_decl::parameter_sptr stripped
(new function_decl::parameter(typ,
@ -2409,8 +2424,8 @@ strip_typedef(const type_base_sptr type)
++i)
{
function_decl::parameter_sptr p = *i;
type_base_sptr typ = canonicalize(strip_typedef(p->get_type()));
assert(p);
type_base_sptr typ = strip_typedef(p->get_type());
assert(typ);
function_decl::parameter_sptr stripped
(new function_decl::parameter(typ,
p->get_index(),
@ -2420,15 +2435,17 @@ strip_typedef(const type_base_sptr type)
p->get_artificial()));
parm.push_back(stripped);
}
type_base_sptr p = canonicalize(ty->get_return_type());
type_base_sptr p = strip_typedef(ty->get_return_type());
assert(p);
t.reset(new function_type(p, parm,
ty->get_size_in_bits(),
ty->get_alignment_in_bits()));
}
canonicalize(t);
return t;
if (!canonicalize(t))
keep_type_alive(t);
return t->get_canonical_type() ? t->get_canonical_type() : t;
}
/// Add a member decl to this scope. Note that user code should not
@ -3848,9 +3865,7 @@ type_base::get_canonical_type_for(type_base_sptr t)
// Look through declaration-only classes
if (class_decl_sptr class_declaration = is_class_type(t))
if (class_declaration->get_is_declaration_only())
if (class_decl_sptr klass =
class_declaration->get_definition_of_declaration())
t = klass;
return type_base_sptr();
if (t->get_canonical_type())
return t->get_canonical_type();
@ -3921,7 +3936,6 @@ canonicalize(type_base_sptr t)
return t->get_canonical_type();
type_base_sptr canonical = type_base::get_canonical_type_for(t);
assert(canonical);
t->priv_->canonical_type = canonical;
@ -9840,6 +9854,17 @@ type_has_non_canonicalized_subtype(type_base_sptr t)
return false;
}
/// Make sure that the life time of a given (smart pointer to a) type
/// is the same as the life time of the libabigail library.
///
/// @param t the type to consider.
void
keep_type_alive(type_base_sptr t)
{
static vector<type_base_sptr> extra_live_types;
extra_live_types.push_back(t);
}
bool
ir_traversable_base::traverse(ir_node_visitor&)
{return true;}