[06/11] Support incomplete enums in core and diff code.

Message ID 20200610115940.26035-7-gprocida@google.com
State Committed
Headers
Series Add incomplete enum support. |

Commit Message

Giuliano Procida June 10, 2020, 11:59 a.m. UTC
  This is an almost blind attempt at addition of support for incomplete,
also known as forward-declared, enum types. I've not made any attempt
to refactor or share logic with the struct/union code and I've
probably got other things wrong.

This patch:

- adds enums_type and enum_type_decl_wptr typedefs
- adds declaration-only machinery to enum_type_decl
- adds is_compatible_with_enum_type, look_through_decl_only_class,
  there_is_a_decl_only_enum and lookup_enum_type helpers
- adds has_enum_decl_only_def_change functions
- inserts has_enum_decl_only_def_change wherever
  has_class_decl_only_def_change is present
- adds reporting of enum declaration/definition diffs
- doesn't address the DWARF reader, XML writer or XML reader

Signed-off-by: Giuliano Procida <gprocida@google.com>
---
 include/abg-comp-filter.h   |   7 ++
 include/abg-fwd.h           |  24 +++++
 include/abg-ir.h            |  15 +++
 src/abg-comp-filter.cc      |  74 +++++++++++++-
 src/abg-comparison.cc       |   4 +-
 src/abg-default-reporter.cc |  19 ++++
 src/abg-ir.cc               | 196 +++++++++++++++++++++++++++++++++++-
 7 files changed, 333 insertions(+), 6 deletions(-)
  

Comments

Dodji Seketeli July 6, 2020, 11:14 a.m. UTC | #1
Hello,

Giuliano Procida <gprocida@google.com> a écrit:

> This is an almost blind attempt at addition of support for incomplete,
> also known as forward-declared, enum types. I've not made any attempt
> to refactor or share logic with the struct/union code and I've
> probably got other things wrong.
>
> This patch:
>
> - adds enums_type and enum_type_decl_wptr typedefs
> - adds declaration-only machinery to enum_type_decl
> - adds is_compatible_with_enum_type, look_through_decl_only_class,
>   there_is_a_decl_only_enum and lookup_enum_type helpers
> - adds has_enum_decl_only_def_change functions
> - inserts has_enum_decl_only_def_change wherever
>   has_class_decl_only_def_change is present
> - adds reporting of enum declaration/definition diffs
> - doesn't address the DWARF reader, XML writer or XML reader

So, I have amended this patch to make it so that declaration-only-ness
becomes a property of any declaration.  Namely, it becomes a property of
abigail::ir::decl_base.

Besides the obvious code sharing advantage, it allows the underlying
type of enums to also be flagged as decl-only whenever an enum is
decl-only.  Thus, when the underlying type of an enum changes from
decl-only to defined that change is filtered out from the leaf reporter,
as it is for class, unions and now enums.

I have also added a ChangeLog to the commit log of the patch.

You'll find the amended patch below, but you can also get it from the
branch dodji/incomp-enums, browsable online at
https://sourceware.org/git/?p=libabigail.git;a=shortlog;h=refs/heads/dodji/incomp-enums.

From 7dd6e05d28d2f023e6b2158cd517c0b6db8eec5d Mon Sep 17 00:00:00 2001
From: Giuliano Procida <gprocida@google.com>
Date: Wed, 10 Jun 2020 12:59:35 +0100
Subject: [PATCH 1/4] Support incomplete enums in core and diff code.

This is an initial implementation of the support for incomplete, also
known as forward-declared, enum types. I've not made any attempt to
refactor or share logic with the struct/union code.

	* include/abg-comp-filter.h (has_decl_only_def_change) : Declare
	New function.
	* src/abg-comp-filter.cc (there_is_a_decl_only_enum): Define new
	static function and ...
	(type_size_changed): ... use it here.
	(has_decl_only_def_change): Define new function and ...
	(categorize_harm{less, ful}_diff_node): ... use it here.
	* include/abg-fwd.h (enums_type, decl_base_wptr): Declare new
	typedefs.
	(look_through_decl_only_class): Declare new overload for
	class_or_union*.
	(is_compatible_with_enum_type, is_compatible_with_enum_type)
	(look_through_decl_only, lookup_enum_types, lookup_enum_types):
	Declare new functions.
	* include/abg-ir.h (decl_base::{get_is_declaration_only,
	set_is_declaration_only, set_definition_of_declaration,
	get_definition_of_declaration,
	get_naked_definition_of_declaration}): Declare new member
	functions.  They were moved here from the class_or_union class.
	(class_or_union::{get_earlier_declaration,
	set_earlier_declaration, get_definition_of_declaration,
	set_definition_of_declaration,
	get_naked_definition_of_declaration, get_is_declaration_only,
	set_is_declaration_only}): Remove these member functions.
	* src/abg-ir.cc (decl_base::priv::{declaration_,
	definition_of_declaration_, naked_definition_of_declaration_,
	is_declaration_only_}): Define data members.  Moved here from
	class_or_union.
	(decl_base::priv::priv): Adjust to initialize the new data
	members.
	(decl_base::{get_earlier_declaration, set_earlier_declaration,
	get_definition_of_declaration,
	get_naked_definition_of_declaration, get_is_declaration_only,
	set_is_declaration_only, set_definition_of_declaration}): Define
	member functions.
	(operator|): In the overload for (change_kind, change_kind),
	adjust the return type of the call to
	decl_base::get_definition_of_declaration.
	(look_through_decl_only): Define new function.
	(look_through_decl_only_class): Adjust.
	(look_through_decl_only_enum): Likewise.
	(maybe_update_types_lookup_map<class_decl>): Adjust return type of
	call to decl_base::get_definition_of_declaration.
	(types_defined_same_linux_kernel_corpus_public): Use
	look_through_decl_only_class rather than open coding it.
	(class_or_union::priv::{declaration_, definition_of_declaration_,
	naked_definition_of_declaration_, is_declaration_only_}): Remove
	these data members.  They are now carried by decl_base::priv.
	(class_or_union::{g,s}et_alignment_in_bits): Adjust.
	(class_or_union::{g,s}et_size_in_bits): Likewise.
	(class_or_union::operator==): Likewise.
	(equals): Adjust the overload for class_or_union.
	(is_compatible_with_enum_type)
	* src/abg-comparison.cc (try_to_diff<class_decl>): Adjust the
	return type of decl_base::get_definition_of_declaration.
	(leaf_diff_node_marker_visitor::visit_begin): Use
	filtering::has_decl_only_def_change rather than
	filtering::has_class_decl_only_def_change.  Decl-only changes to
	enums (or any other type really) will thus not be recorded as leaf
	changes.
	* src/abg-dwarf-reader.cc (get_scope_for_die): Adjust return type
	of decl_base::get_definition_of_declaration.
	* src/abg-default-reporter.cc (default_reporter::report): Report
	enum decl-only <-> definition changes.
	* src/abg-hash.cc (class_or_union::hash::operator()): In the
	overload for class_or_union& adjust the return type for
	decl_base::get_definition_of_declaration.

Signed-off-by: Giuliano Procida <gprocida@google.com>
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 include/abg-comp-filter.h   |  14 ++
 include/abg-fwd.h           |  36 ++++
 include/abg-ir.h            |  48 ++---
 src/abg-comp-filter.cc      | 131 ++++++++++++-
 src/abg-comparison.cc       |  10 +-
 src/abg-default-reporter.cc |  19 ++
 src/abg-dwarf-reader.cc     |   3 +-
 src/abg-hash.cc             |   3 +-
 src/abg-ir.cc               | 456 +++++++++++++++++++++++++++-----------------
 9 files changed, 509 insertions(+), 211 deletions(-)

diff --git a/include/abg-comp-filter.h b/include/abg-comp-filter.h
index 8113003..556bad4 100644
--- a/include/abg-comp-filter.h
+++ b/include/abg-comp-filter.h
@@ -65,13 +65,27 @@ bool
 is_decl_only_class_with_size_change(const diff *diff);
 
 bool
+has_decl_only_def_change(const decl_base_sptr& first,
+			 const decl_base_sptr& second);
+
+bool
+has_decl_only_def_change(const diff *d);
+
+bool
 has_class_decl_only_def_change(const class_or_union_sptr& first,
 			       const class_or_union_sptr& second);
 
 bool
+has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
+			      const enum_type_decl_sptr& second);
+
+bool
 has_class_decl_only_def_change(const diff *diff);
 
 bool
+has_enum_decl_only_def_change(const diff *diff);
+
+bool
 has_basic_type_name_change(const diff *);
 
 bool
diff --git a/include/abg-fwd.h b/include/abg-fwd.h
index 999b071..7a85db3 100644
--- a/include/abg-fwd.h
+++ b/include/abg-fwd.h
@@ -155,6 +155,12 @@ class enum_type_decl;
 /// Convenience typedef for shared pointer to a @ref enum_type_decl.
 typedef shared_ptr<enum_type_decl> enum_type_decl_sptr;
 
+/// Convenience typedef for a vector of @ref enum_type_decl_sptr
+typedef vector<enum_type_decl_sptr> enums_type;
+
+/// Convenience typedef for a weak pointer to a @ref decl_base.
+typedef weak_ptr<decl_base> decl_base_wptr;
+
 class class_or_union;
 
 typedef shared_ptr<class_or_union> class_or_union_sptr;
@@ -424,6 +430,12 @@ typedef_decl*
 is_typedef(type_base*);
 
 enum_type_decl_sptr
+is_compatible_with_enum_type(const type_base_sptr&);
+
+enum_type_decl_sptr
+is_compatible_with_enum_type(const decl_base_sptr&);
+
+enum_type_decl_sptr
 is_enum_type(const type_or_decl_base_sptr&);
 
 const enum_type_decl*
@@ -513,6 +525,24 @@ look_through_decl_only_class(const class_or_union&);
 class_or_union_sptr
 look_through_decl_only_class(class_or_union_sptr);
 
+class_or_union*
+look_through_decl_only_class(class_or_union*);
+
+enum_type_decl_sptr
+look_through_decl_only_enum(const enum_type_decl&);
+
+enum_type_decl_sptr
+look_through_decl_only_enum(enum_type_decl_sptr);
+
+decl_base_sptr
+look_through_decl_only(const decl_base&);
+
+decl_base*
+look_through_decl_only(decl_base*);
+
+decl_base_sptr
+look_through_decl_only(const decl_base_sptr&);
+
 var_decl*
 is_var_decl(const type_or_decl_base*);
 
@@ -1085,6 +1115,12 @@ lookup_enum_type(const string&, const corpus&);
 enum_type_decl_sptr
 lookup_enum_type(const interned_string&, const corpus&);
 
+const type_base_wptrs_type*
+lookup_enum_types(const interned_string&, const corpus&);
+
+const type_base_wptrs_type*
+lookup_enum_types(const string&, const corpus&);
+
 enum_type_decl_sptr
 lookup_enum_type_per_location(const interned_string&, const corpus&);
 
diff --git a/include/abg-ir.h b/include/abg-ir.h
index d81de21..468198c 100644
--- a/include/abg-ir.h
+++ b/include/abg-ir.h
@@ -1553,6 +1553,27 @@ public:
   void
   set_visibility(visibility v);
 
+  const decl_base_sptr
+  get_earlier_declaration() const;
+
+  void
+  set_earlier_declaration(const decl_base_sptr&);
+
+  const decl_base_sptr
+  get_definition_of_declaration() const;
+
+  void
+  set_definition_of_declaration(const decl_base_sptr&);
+
+  const decl_base*
+  get_naked_definition_of_declaration() const;
+
+  bool
+  get_is_declaration_only() const;
+
+  void
+  set_is_declaration_only(bool f);
+
   friend type_base_sptr
   canonicalize(type_base_sptr);
 
@@ -3781,27 +3802,6 @@ public:
   void
   set_naming_typedef(const typedef_decl_sptr&);
 
-  bool
-  get_is_declaration_only() const;
-
-  void
-  set_is_declaration_only(bool f);
-
-  void
-  set_definition_of_declaration(class_or_union_sptr);
-
-  const class_or_union_sptr
-  get_definition_of_declaration() const;
-
-  const class_or_union*
-  get_naked_definition_of_declaration() const;
-
-  decl_base_sptr
-  get_earlier_declaration() const;
-
-  void
-  set_earlier_declaration(decl_base_sptr declaration);
-
  void
   insert_member_type(type_base_sptr t,
 		     declarations::iterator before);
@@ -4013,12 +4013,6 @@ public:
   class_decl(const environment* env, const string& name, bool is_struct,
 	     bool is_declaration_only = true);
 
-  const class_decl_sptr
-  get_definition_of_declaration() const;
-
-  const class_decl*
-  get_naked_definition_of_declaration() const;
-
   virtual string
   get_pretty_representation(bool internal = false,
 			    bool qualified_name = true) const;
diff --git a/src/abg-comp-filter.cc b/src/abg-comp-filter.cc
index 702d223..9b87ee7 100644
--- a/src/abg-comp-filter.cc
+++ b/src/abg-comp-filter.cc
@@ -118,6 +118,25 @@ there_is_a_decl_only_class(const class_decl_sptr& class1,
   return false;
 }
 
+/// Test if there is a enum that is declaration-only among the two
+/// enums in parameter.
+///
+/// @param enum1 the first enum to consider.
+///
+/// @param enum2 the second enum to consider.
+///
+/// @return true if either enums are declaration-only, false
+/// otherwise.
+static bool
+there_is_a_decl_only_enum(const enum_type_decl_sptr& enum1,
+			  const enum_type_decl_sptr& enum2)
+{
+  if ((enum1 && enum1->get_is_declaration_only())
+      || (enum2 && enum2->get_is_declaration_only()))
+    return true;
+  return false;
+}
+
 /// Test if the diff involves a declaration-only class.
 ///
 /// @param diff the class diff to consider.
@@ -146,7 +165,9 @@ type_size_changed(const type_base_sptr f, const type_base_sptr s)
       || f->get_size_in_bits() == 0
       || s->get_size_in_bits() == 0
       || there_is_a_decl_only_class(is_compatible_with_class_type(f),
-				    is_compatible_with_class_type(s)))
+				    is_compatible_with_class_type(s))
+      || there_is_a_decl_only_enum(is_compatible_with_enum_type(f),
+				   is_compatible_with_enum_type(s)))
     return false;
 
   return f->get_size_in_bits() != s->get_size_in_bits();
@@ -893,10 +914,8 @@ is_decl_only_class_with_size_change(const class_or_union_sptr& first,
   if (!first || !second)
     return false;
 
-  class_or_union_sptr f =
-    look_through_decl_only_class(first);
-  class_or_union_sptr s =
-    look_through_decl_only_class(second);
+  class_or_union_sptr f = look_through_decl_only_class(first);
+  class_or_union_sptr s = look_through_decl_only_class(second);
 
   return is_decl_only_class_with_size_change(*f, *s);
 }
@@ -929,6 +948,57 @@ is_decl_only_class_with_size_change(const diff *diff)
   return is_decl_only_class_with_size_change(f, s);
 }
 
+/// Test if two @ref decl_base_sptr are different just by the
+/// fact that one is decl-only and the other one is defined.
+///
+/// @param first the first decl to consider.
+///
+/// @param second the second decl to consider.
+///
+/// @return true iff the two arguments are different just by the fact
+/// that one is decl-only and the other one is defined.
+bool
+has_decl_only_def_change(const decl_base_sptr& first,
+			 const decl_base_sptr& second)
+{
+  if (!first || !second)
+    return false;
+
+  decl_base_sptr f =
+    look_through_decl_only(first);
+  decl_base_sptr s =
+    look_through_decl_only(second);
+
+  if (f->get_qualified_name() != s->get_qualified_name())
+    return false;
+
+  return (f->get_is_declaration_only() != s->get_is_declaration_only());
+}
+
+/// Test if a diff carries a change in which the two decls are
+/// different by the fact that one is a decl-only and the other one is
+/// defined.
+///
+/// @param diff the diff node to consider.
+///
+/// @return true if the diff carries a change in which the two decls
+/// are different by the fact that one is a decl-only and the other
+/// one is defined.
+bool
+has_decl_only_def_change(const diff *d)
+{
+  if (!d)
+    return false;
+
+  decl_base_sptr f =
+    look_through_decl_only(is_decl(d->first_subject()));
+  decl_base_sptr s =
+    look_through_decl_only(is_decl(d->second_subject()));
+
+  return has_decl_only_def_change(f, s);
+}
+
+
 /// Test if two @ref class_or_union_sptr are different just by the
 /// fact that one is decl-only and the other one is defined.
 ///
@@ -956,6 +1026,31 @@ has_class_decl_only_def_change(const class_or_union_sptr& first,
   return (f->get_is_declaration_only() != s->get_is_declaration_only());
 }
 
+/// Test if two @ref enum_sptr are different just by the
+/// fact that one is decl-only and the other one is defined.
+///
+/// @param first the first enum to consider.
+///
+/// @param second the second enum to consider.
+///
+/// @return true iff the two arguments are different just by the fact
+/// that one is decl-only and the other one is defined.
+bool
+has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
+			      const enum_type_decl_sptr& second)
+{
+  if (!first || !second)
+    return false;
+
+  enum_type_decl_sptr f = look_through_decl_only_enum(first);
+  enum_type_decl_sptr s = look_through_decl_only_enum(second);
+
+  if (f->get_qualified_name() != s->get_qualified_name())
+    return false;
+
+  return (f->get_is_declaration_only() != s->get_is_declaration_only());
+}
+
 /// Test if a class_or_union_diff carries a change in which the two
 /// classes are different by the fact that one is a decl-only and the
 /// other one is defined.
@@ -980,6 +1075,28 @@ has_class_decl_only_def_change(const diff *diff)
   return has_class_decl_only_def_change(f, s);
 }
 
+/// Test if a enum_diff carries a change in which the two enums are
+/// different by the fact that one is a decl-only and the other one is
+/// defined.
+///
+/// @param diff the diff node to consider.
+///
+/// @return true if the enum_diff carries a change in which the two
+/// enums are different by the fact that one is a decl-only and the
+/// other one is defined.
+bool
+has_enum_decl_only_def_change(const diff *diff)
+{
+  const enum_diff *d = dynamic_cast<const enum_diff*>(diff);
+  if (!d)
+    return false;
+
+  enum_type_decl_sptr f = look_through_decl_only_enum(d->first_enum());
+  enum_type_decl_sptr s = look_through_decl_only_enum(d->second_enum());
+
+  return has_enum_decl_only_def_change(f, s);
+}
+
 /// Test if a diff node carries a basic type name change.
 ///
 /// @param d the diff node to consider.
@@ -1517,7 +1634,8 @@ categorize_harmless_diff_node(diff *d, bool pre)
       decl_base_sptr f = is_decl(d->first_subject()),
 	s = is_decl(d->second_subject());
 
-      if (has_class_decl_only_def_change(d))
+      if (has_class_decl_only_def_change(d)
+	  || has_enum_decl_only_def_change(d))
 	category |= TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY;
 
       if (access_changed(f, s))
@@ -1608,6 +1726,7 @@ categorize_harmful_diff_node(diff *d, bool pre)
       //
       // TODO: be more specific -- not all size changes are harmful.
       if (!has_class_decl_only_def_change(d)
+	  && !has_enum_decl_only_def_change(d)
 	  && (type_size_changed(f, s)
 	      || data_member_offset_changed(f, s)
 	      || non_static_data_member_type_size_changed(f, s)
diff --git a/src/abg-comparison.cc b/src/abg-comparison.cc
index d0f1d21..b6f065f 100644
--- a/src/abg-comparison.cc
+++ b/src/abg-comparison.cc
@@ -2827,13 +2827,15 @@ try_to_diff<class_decl>(const type_or_decl_base_sptr first,
 
       if (f->get_is_declaration_only())
 	{
-	  class_decl_sptr f2 = f->get_definition_of_declaration();
+	  class_decl_sptr f2 =
+	    is_class_type (f->get_definition_of_declaration());
 	  if (f2)
 	    f = f2;
 	}
       if (s->get_is_declaration_only())
 	{
-	  class_decl_sptr s2 = s->get_definition_of_declaration();
+	  class_decl_sptr s2 =
+	    is_class_type(s->get_definition_of_declaration());
 	  if (s2)
 	    s = s2;
 	}
@@ -10953,8 +10955,8 @@ struct leaf_diff_node_marker_visitor : public diff_node_visitor
 	// typedef change which underlying type is an anonymous
 	// struct/union.
 	&& !is_anonymous_class_or_union_diff(d)
-	// Don't show decl-only-ness changes of classes either.
-	&& !filtering::has_class_decl_only_def_change(d)
+	// Don't show decl-only-ness changes either.
+	&& !filtering::has_decl_only_def_change(d)
 	// Sometime, we can encounter artifacts of bogus DWARF that
 	// yield a diff node for a decl-only class (and empty class
 	// with the is_declaration flag set) that carries a non-zero
diff --git a/src/abg-default-reporter.cc b/src/abg-default-reporter.cc
index 2acb695..cbf8c2b 100644
--- a/src/abg-default-reporter.cc
+++ b/src/abg-default-reporter.cc
@@ -99,6 +99,25 @@ default_reporter::report(const enum_diff& d, ostream& out,
   enum_type_decl_sptr first = d.first_enum(), second = d.second_enum();
 
   const diff_context_sptr& ctxt = d.context();
+
+  // Report enum decl-only <-> definition changes.
+  if (ctxt->get_allowed_category() & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY)
+    if (filtering::has_enum_decl_only_def_change(first, second))
+      {
+	string was =
+	  first->get_is_declaration_only()
+	  ? " was a declaration-only enum type"
+	  : " was a defined enum type";
+
+	string is_now =
+	  second->get_is_declaration_only()
+	  ? " and is now a declaration-only enum type"
+	  : " and is now a defined enum type";
+
+	out << indent << "enum type " << name << was << is_now << "\n";
+	return;
+      }
+
   report_name_size_and_alignment_changes(first, second, ctxt,
 					 out, indent);
   maybe_report_diff_for_member(first, second, ctxt, out, indent);
diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc
index cf789d0..afe574d 100644
--- a/src/abg-dwarf-reader.cc
+++ b/src/abg-dwarf-reader.cc
@@ -12315,7 +12315,8 @@ get_scope_for_die(read_context& ctxt,
   class_decl_sptr cl = dynamic_pointer_cast<class_decl>(d);
   if (cl && cl->get_is_declaration_only())
     {
-      scope_decl_sptr scop (cl->get_definition_of_declaration());
+      scope_decl_sptr scop  =
+	dynamic_pointer_cast<scope_decl>(cl->get_definition_of_declaration());
       if (scop)
 	s = scop;
       else
diff --git a/src/abg-hash.cc b/src/abg-hash.cc
index c1cdc57..5d2861f 100644
--- a/src/abg-hash.cc
+++ b/src/abg-hash.cc
@@ -635,7 +635,8 @@ class_or_union::hash::operator()(const class_or_union& t) const
   if (t.get_is_declaration_only())
     {
       ABG_ASSERT(t.get_definition_of_declaration());
-      size_t v = operator()(*t.get_definition_of_declaration());
+      size_t v = operator()
+	(*is_class_or_union_type(t.get_definition_of_declaration()));
       return v;
     }
 
diff --git a/src/abg-ir.cc b/src/abg-ir.cc
index 4813035..940f9ec 100644
--- a/src/abg-ir.cc
+++ b/src/abg-ir.cc
@@ -3416,6 +3416,10 @@ struct decl_base::priv
     interned_string	scoped_name_;
   interned_string	linkage_name_;
   visibility		visibility_;
+  decl_base_sptr	declaration_;
+  decl_base_wptr	definition_of_declaration_;
+  decl_base*		naked_definition_of_declaration_;
+  bool			is_declaration_only_;
 
   priv()
     : in_pub_sym_tab_(false),
@@ -3423,7 +3427,9 @@ struct decl_base::priv
       is_artificial_(false),
       has_anonymous_parent_(false),
       context_(),
-      visibility_(VISIBILITY_DEFAULT)
+      visibility_(VISIBILITY_DEFAULT),
+      naked_definition_of_declaration_(),
+      is_declaration_only_()
   {}
 
   priv(interned_string name, const location& locus,
@@ -3434,7 +3440,9 @@ struct decl_base::priv
       name_(name),
       qualified_name_(name),
       linkage_name_(linkage_name),
-      visibility_(vis)
+      visibility_(vis),
+      naked_definition_of_declaration_(),
+      is_declaration_only_()
   {
     is_anonymous_ = name_.empty();
     has_anonymous_parent_ = false;
@@ -3446,7 +3454,9 @@ struct decl_base::priv
       has_anonymous_parent_(false),
       location_(l),
       context_(),
-      visibility_(VISIBILITY_DEFAULT)
+      visibility_(VISIBILITY_DEFAULT),
+      naked_definition_of_declaration_(),
+      is_declaration_only_()
   {}
 
   ~priv()
@@ -3881,6 +3891,80 @@ const interned_string&
 decl_base::get_scoped_name() const
 {return priv_->scoped_name_;}
 
+/// If this @ref decl_base is a definition, get its earlier
+/// declaration.
+///
+/// @return the earlier declaration of the class, if any.
+const decl_base_sptr
+decl_base::get_earlier_declaration() const
+{return priv_->declaration_;}
+
+/// set the earlier declaration of this @ref decl_base definition.
+///
+/// @param d the earlier declaration to set.  Note that it's set only
+/// if it's a pure declaration.
+void
+decl_base::set_earlier_declaration(const decl_base_sptr& d)
+{
+  if (d && d->get_is_declaration_only())
+    priv_->declaration_ = d;
+}
+
+
+/// If this @ref decl_base is declaration-only, get its definition, if
+/// any.
+///
+/// @return the definition of this decl-only @ref decl_base.
+const decl_base_sptr
+decl_base::get_definition_of_declaration() const
+{return priv_->definition_of_declaration_.lock();}
+
+///  If this @ref decl_base is declaration-only, get its definition,
+///  if any.
+///
+/// Note that this function doesn't return a smart pointer, but rather
+/// the underlying pointer managed by the smart pointer.  So it's as
+/// fast as possible.  This getter is to be used in code paths that
+/// are proven to be performance hot spots; especially, when comparing
+/// sensitive types like enums, classes or unions.  Those are compared
+/// extremely frequently and thus, their access to the definition of
+/// declaration must be fast.
+///
+/// @return the definition of the declaration.
+const decl_base*
+decl_base::get_naked_definition_of_declaration() const
+{return priv_->naked_definition_of_declaration_;}
+
+/// Test if a @ref decl_base is a declaration-only decl.
+///
+/// @return true iff the current @ref decl_base is declaration-only.
+bool
+decl_base::get_is_declaration_only() const
+{return priv_->is_declaration_only_;}
+
+/// Set a flag saying if the @ref enum_type_decl is a declaration-only
+/// @ref enum_type_decl.
+///
+/// @param f true if the @ref enum_type_decl is a decalaration-only
+/// @ref enum_type_decl.
+void
+decl_base::set_is_declaration_only(bool f)
+{
+  bool update_types_lookup_map = !f && priv_->is_declaration_only_;
+
+  priv_->is_declaration_only_ = f;
+
+  if (update_types_lookup_map)
+    if (scope_decl* s = get_scope())
+      {
+	scope_decl::declarations::iterator i;
+	if (s->find_iterator_for_member(this, i))
+	  maybe_update_types_lookup_map(*i);
+	else
+	  ABG_ASSERT_NOT_REACHED;
+      }
+}
+
 change_kind
 operator|(change_kind l, change_kind r)
 {
@@ -6686,7 +6770,7 @@ get_location(const decl_base_sptr& decl)
       if (class_or_union_sptr c = is_class_or_union_type(decl))
 	if (c->get_is_declaration_only() && c->get_definition_of_declaration())
 	  {
-	    c = c->get_definition_of_declaration();
+	    c = is_class_or_union_type(c->get_definition_of_declaration());
 	    loc = c->get_location();
 	  }
     }
@@ -7925,6 +8009,38 @@ typedef_decl*
 is_typedef(type_base* t)
 {return dynamic_cast<typedef_decl*>(t);}
 
+/// Test if a type is a enum. This function looks through typedefs.
+///
+/// @parm t the type to consider.
+///
+/// @return the enum_decl if @p t is a enum_decl or null otherwise.
+enum_type_decl_sptr
+is_compatible_with_enum_type(const type_base_sptr& t)
+{
+  if (!t)
+    return enum_type_decl_sptr();
+
+  // Normally we should strip typedefs entirely, but this is
+  // potentially costly, especially on binaries with huge changesets
+  // like the Linux Kernel.  So we just get the leaf types for now.
+  //
+  // Maybe there should be an option by which users accepts to pay the
+  // CPU usage toll in exchange for finer filtering?
+
+  // type_base_sptr ty = strip_typedef(t);
+  type_base_sptr ty = peel_typedef_type(t);;
+  return is_enum_type(ty);
+}
+
+/// Test if a type is a enum. This function looks through typedefs.
+///
+/// @parm t the type to consider.
+///
+/// @return the enum_decl if @p t is a enum_decl or null otherwise.
+enum_type_decl_sptr
+is_compatible_with_enum_type(const decl_base_sptr& t)
+{return is_compatible_with_enum_type(is_type(t));}
+
 /// Test if a decl is an enum_type_decl
 ///
 /// @param d the decl to test for.
@@ -8270,24 +8386,19 @@ is_method_type(type_or_decl_base* t)
 /// @param the_klass the class (or union) to consider.
 ///
 /// @return either the definition of the class, or the class itself.
+class_or_union*
+look_through_decl_only_class(class_or_union* the_class)
+{return is_class_or_union_type(look_through_decl_only(the_class));}
+
+/// If a class (or union) is a decl-only class, get its definition.
+/// Otherwise, just return the initial class.
+///
+/// @param the_klass the class (or union) to consider.
+///
+/// @return either the definition of the class, or the class itself.
 class_or_union_sptr
 look_through_decl_only_class(const class_or_union& the_class)
-{
-  class_or_union_sptr klass;
-  if (the_class.get_is_declaration_only())
-    klass = the_class.get_definition_of_declaration();
-
-  if (!klass)
-    return klass;
-
-  while (klass
-	 && klass->get_is_declaration_only()
-	 && klass->get_definition_of_declaration())
-    klass = klass->get_definition_of_declaration();
-
-  ABG_ASSERT(klass);
-  return klass;
-}
+{return is_class_or_union_type(look_through_decl_only(the_class));}
 
 /// If a class (or union) is a decl-only class, get its definition.
 /// Otherwise, just return the initial class.
@@ -8297,13 +8408,85 @@ look_through_decl_only_class(const class_or_union& the_class)
 /// @return either the definition of the class, or the class itself.
 class_or_union_sptr
 look_through_decl_only_class(class_or_union_sptr klass)
+{return is_class_or_union_type(look_through_decl_only(klass));}
+
+/// If a enum is a decl-only enum, get its definition.
+/// Otherwise, just return the initial enum.
+///
+/// @param the_enum the enum to consider.
+///
+/// @return either the definition of the enum, or the enum itself.
+enum_type_decl_sptr
+look_through_decl_only_enum(const enum_type_decl& the_enum)
+{return is_enum_type(look_through_decl_only(the_enum));}
+
+/// If a enum is a decl-only enum, get its definition.
+/// Otherwise, just return the initial enum.
+///
+/// @param enom the enum to consider.
+///
+/// @return either the definition of the enum, or the enum itself.
+enum_type_decl_sptr
+look_through_decl_only_enum(enum_type_decl_sptr enom)
+{return is_enum_type(look_through_decl_only(enom));}
+
+/// If a decl is decl-only get its definition.  Otherwise, just return nil.
+///
+/// @param d the decl to consider.
+///
+/// @return either the definition of the decl, or nil.
+decl_base_sptr
+look_through_decl_only(const decl_base& d)
+{
+  decl_base_sptr decl;
+  if (d.get_is_declaration_only())
+    decl = d.get_definition_of_declaration();
+
+  if (!decl)
+    return decl;
+
+  while (decl
+	 && decl->get_is_declaration_only()
+	 && decl->get_definition_of_declaration())
+    decl = decl->get_definition_of_declaration();
+
+  ABG_ASSERT(decl);
+  return decl;
+}
+
+/// If a decl is decl-only enum, get its definition.  Otherwise, just
+/// return the initial decl.
+///
+/// @param d the decl to consider.
+///
+/// @return either the definition of the enum, or the decl itself.
+decl_base*
+look_through_decl_only(decl_base* d)
+{
+  if (!d)
+    return d;
+
+  decl_base* result = look_through_decl_only(*d).get();
+  if (!result)
+    result = d;
+
+  return result;
+}
+
+/// If a decl is decl-only get its definition.  Otherwise, just return nil.
+///
+/// @param d the decl to consider.
+///
+/// @return either the definition of the decl, or nil.
+decl_base_sptr
+look_through_decl_only(const decl_base_sptr& d)
 {
-  if (!klass)
-    return klass;
+  if (!d)
+    return d;
 
-  class_or_union_sptr result = look_through_decl_only_class(*klass);
+  decl_base_sptr result = look_through_decl_only(*d);
   if (!result)
-    result = klass;
+    result = d;
 
   return result;
 }
@@ -10166,6 +10349,37 @@ lookup_enum_type(const interned_string& qualified_name, const corpus& corp)
   return result;
 }
 
+/// Look into a given corpus to find the enum type*s* that have a
+/// given qualified name.
+///
+/// @param qualified_name the qualified name of the type to look for.
+///
+/// @param corp the corpus to look into.
+///
+/// @return the vector of enum types that which name is @p qualified_name.
+const type_base_wptrs_type *
+lookup_enum_types(const interned_string& qualified_name, const corpus& corp)
+{
+  const istring_type_base_wptrs_map_type& m = corp.get_types().enum_types();
+
+  return lookup_types_in_map(qualified_name, m);
+}
+
+/// Look into a given corpus to find the enum type*s* that have a
+/// given qualified name.
+///
+/// @param qualified_name the qualified name of the type to look for.
+///
+/// @param corp the corpus to look into.
+///
+/// @return the vector of enum types that which name is @p qualified_name.
+const type_base_wptrs_type*
+lookup_enum_types(const string& qualified_name, const corpus& corp)
+{
+  interned_string s = corp.get_environment()->intern(qualified_name);
+  return lookup_enum_types(s, corp);
+}
+
 /// Look up an @ref enum_type_decl from a given corpus, by its location.
 ///
 /// @param loc the location to consider.
@@ -10767,7 +10981,8 @@ maybe_update_types_lookup_map<class_decl>(const class_decl_sptr& class_type,
   bool update_qname_map = true;
   if (type->get_is_declaration_only())
     {
-      if (class_decl_sptr def = class_type->get_definition_of_declaration())
+      if (class_decl_sptr def =
+	  is_class_type(class_type->get_definition_of_declaration()))
 	type = def;
       else
 	update_qname_map = false;
@@ -11683,10 +11898,8 @@ types_defined_same_linux_kernel_corpus_public(const type_base& t1,
 
   // Look through declaration-only types.  That is, get the associated
   // definition type.
-  if (c1 && c1->get_is_declaration_only())
-    c1 = c1->get_definition_of_declaration().get();
-  if (c2 && c2->get_is_declaration_only())
-    c2 = c2->get_definition_of_declaration().get();
+  c1 = look_through_decl_only_class(c1);
+  c2 = look_through_decl_only_class(c2);
 
   if (c1 && c2)
     {
@@ -12084,6 +12297,22 @@ re_canonicalize(type_base_sptr t)
   return canonicalize(t);
 }
 
+/// Set the definition of this declaration-only @ref decl_base.
+///
+/// @param d the new definition to set.
+void
+decl_base::set_definition_of_declaration(const decl_base_sptr& d)
+{
+  ABG_ASSERT(get_is_declaration_only());
+  priv_->definition_of_declaration_ = d;
+  priv_->definition_of_declaration_ = d;
+  if (type_base *t = is_type(this))
+    if (type_base_sptr canonical_type = is_type(d)->get_canonical_type())
+      t->priv_->canonical_type = canonical_type;
+
+  priv_->naked_definition_of_declaration_ = const_cast<decl_base*>(d.get());
+}
+
 /// The constructor of @ref type_base.
 ///
 /// @param s the size of the type, in bits.
@@ -14983,7 +15212,6 @@ class enum_type_decl::priv
   priv();
 
 public:
-
   priv(type_base_sptr underlying_type,
        enumerators& enumerators)
     : underlying_type_(underlying_type),
@@ -17992,9 +18220,6 @@ function_decl::parameter::get_pretty_representation(bool internal,
 struct class_or_union::priv
 {
   typedef_decl_wptr		naming_typedef_;
-  decl_base_sptr		declaration_;
-  class_or_union_wptr		definition_of_declaration_;
-  class_or_union*		naked_definition_of_declaration_;
   member_types			member_types_;
   data_members			data_members_;
   data_members			non_static_data_members_;
@@ -18006,21 +18231,16 @@ struct class_or_union::priv
   string_mem_fn_ptr_map_type	signature_2_mem_fn_map_;
   member_function_templates	member_function_templates_;
   member_class_templates	member_class_templates_;
-  bool				is_declaration_only_;
 
   priv()
-    : naked_definition_of_declaration_(),
-      is_declaration_only_(false)
   {}
 
   priv(class_or_union::member_types& mbr_types,
        class_or_union::data_members& data_mbrs,
        class_or_union::member_functions& mbr_fns)
-    : naked_definition_of_declaration_(),
-      member_types_(mbr_types),
+    : member_types_(mbr_types),
       data_members_(data_mbrs),
-      member_functions_(mbr_fns),
-      is_declaration_only_(false)
+      member_functions_(mbr_fns)
   {
     for (data_members::const_iterator i = data_members_.begin();
 	 i != data_members_.end();
@@ -18029,11 +18249,6 @@ struct class_or_union::priv
 	non_static_data_members_.push_back(*i);
   }
 
-  priv(bool is_declaration_only)
-    : naked_definition_of_declaration_(),
-      is_declaration_only_(is_declaration_only)
-  {}
-
   /// Mark a class or union or union as being currently compared using
   /// the class_or_union== operator.
   ///
@@ -18249,8 +18464,10 @@ class_or_union::class_or_union(const environment* env, const string& name,
     decl_base(env, name, location(), name),
     type_base(env, 0, 0),
     scope_type_decl(env, name, 0, 0, location()),
-    priv_(new priv(is_declaration_only))
-{}
+    priv_(new priv)
+{
+  set_is_declaration_only(is_declaration_only);
+}
 
 /// This implements the ir_traversable_base::traverse pure virtual
 /// function.
@@ -18470,7 +18687,8 @@ size_t
 class_or_union::get_alignment_in_bits() const
 {
   if (get_is_declaration_only() && get_definition_of_declaration())
-    return get_definition_of_declaration()->get_alignment_in_bits();
+    return is_class_or_union_type
+      (get_definition_of_declaration())->get_alignment_in_bits();
 
    return type_base::get_alignment_in_bits();
 }
@@ -18485,7 +18703,8 @@ void
 class_or_union::set_alignment_in_bits(size_t a)
 {
   if (get_is_declaration_only() && get_definition_of_declaration())
-    get_definition_of_declaration()->set_alignment_in_bits(a);
+    is_class_or_union_type
+      (get_definition_of_declaration()) ->set_alignment_in_bits(a);
   else
     type_base::set_alignment_in_bits(a);
 }
@@ -18500,7 +18719,8 @@ void
 class_or_union::set_size_in_bits(size_t s)
 {
   if (get_is_declaration_only() && get_definition_of_declaration())
-    get_definition_of_declaration()->set_size_in_bits(s);
+    is_class_or_union_type
+      (get_definition_of_declaration())->set_size_in_bits(s);
   else
     type_base::set_size_in_bits(s);
 }
@@ -18515,42 +18735,12 @@ size_t
 class_or_union::get_size_in_bits() const
 {
   if (get_is_declaration_only() && get_definition_of_declaration())
-    return get_definition_of_declaration()->get_size_in_bits();
+    return is_class_or_union_type
+      (get_definition_of_declaration())->get_size_in_bits();
 
   return type_base::get_size_in_bits();
 }
 
-/// Test if a @ref class_or_union is a declaration-only @ref
-/// class_or_union.
-///
-/// @return true iff the current @ref class_or_union is a
-/// declaration-only @ref class_or_union.
-bool
-class_or_union::get_is_declaration_only() const
-{return priv_->is_declaration_only_;}
-
-/// Set a flag saying if the @ref class_or_union is a declaration-only
-/// @ref class_or_union.
-///
-/// @param f true if the @ref class_or_union is a decalaration-only
-/// @ref class_or_union.
-void
-class_or_union::set_is_declaration_only(bool f)
-{
-  bool update_types_lookup_map = !f && priv_->is_declaration_only_;
-
-  priv_->is_declaration_only_ = f;
-
-  if (update_types_lookup_map)
-    if (scope_decl* s = get_scope())
-      {
-	declarations::iterator i;
-	if (s->find_iterator_for_member(this, i))
-	  maybe_update_types_lookup_map(*i);
-	else
-	  ABG_ASSERT_NOT_REACHED;
-      }
-}
 
 /// Getter for the naming typedef of the current class.
 ///
@@ -18582,64 +18772,6 @@ class_or_union::set_naming_typedef(const typedef_decl_sptr& typedef_type)
   priv_->naming_typedef_ = typedef_type;
 }
 
-/// Set the definition of this declaration-only @ref class_or_union.
-///
-/// @param d the new definition to set.
-void
-class_or_union::set_definition_of_declaration(class_or_union_sptr d)
-{
-  ABG_ASSERT(get_is_declaration_only());
-  priv_->definition_of_declaration_ = d;
-  if (d->get_canonical_type())
-    type_base::priv_->canonical_type = d->get_canonical_type();
-
-  priv_->naked_definition_of_declaration_ = d.get();
-}
-
-/// If this @ref class_or_union_sptr is declaration-only, get its
-/// definition, if any.
-///
-/// @return the definition of this decl-only class.
-const class_or_union_sptr
-class_or_union::get_definition_of_declaration() const
-{return priv_->definition_of_declaration_.lock();}
-
-///  If this @ref class_or_union is declaration-only, get its
-///  definition, if any.
-///
-/// Note that this function doesn't return a smart pointer, but rather
-/// the underlying pointer managed by the smart pointer.  So it's as
-/// fast as possible.  This getter is to be used in code paths that
-/// are proven to be performance hot spots; especially, when comparing
-/// sensitive types like class or unions.  Those are compared
-/// extremely frequently and thus, their access to the definition of
-/// declaration must be fast.
-///
-/// @return the definition of the class.
-const class_or_union*
-class_or_union::get_naked_definition_of_declaration() const
-{return priv_->naked_definition_of_declaration_;}
-
-/// If this @ref class_or_union_sptr is a definitin, get its earlier
-/// declaration.
-///
-/// @return the earlier declaration of the class, if any.
-decl_base_sptr
-class_or_union::get_earlier_declaration() const
-{return priv_->declaration_;}
-
-/// set the earlier declaration of this @ref class_or_union definition.
-///
-/// @param declaration the earlier declaration to set.  Note that it's
-/// set only if it's a pure declaration.
-void
-class_or_union::set_earlier_declaration(decl_base_sptr declaration)
-{
-  class_or_union_sptr cl = dynamic_pointer_cast<class_or_union>(declaration);
-  if (cl && cl->get_is_declaration_only())
-    priv_->declaration_ = declaration;
-}
-
 /// Get the member types of this @ref class_or_union.
 ///
 /// @return a vector of the member types of this ref class_or_union.
@@ -19087,15 +19219,16 @@ class_or_union::operator==(const decl_base& other) const
   if (!canonical_type
       && get_is_declaration_only()
       && get_naked_definition_of_declaration())
-    canonical_type =
-      get_naked_definition_of_declaration()->get_naked_canonical_type();
+    canonical_type = is_class_or_union_type
+      (get_naked_definition_of_declaration())->get_naked_canonical_type();
 
   // Likewise for the other class.
   if (!other_canonical_type
       && op->get_is_declaration_only()
       && op->get_naked_definition_of_declaration())
     other_canonical_type =
-      op->get_naked_definition_of_declaration()->get_naked_canonical_type();
+      is_class_or_union_type
+      (op->get_naked_definition_of_declaration())->get_naked_canonical_type();
 
   if (canonical_type && other_canonical_type)
     return canonical_type == other_canonical_type;
@@ -19166,11 +19299,11 @@ equals(const class_or_union& l, const class_or_union& r, change_kind* k)
   if (l_is_decl_only || r_is_decl_only)
     {
       const class_or_union* def1 = l_is_decl_only
-	? l.get_naked_definition_of_declaration()
+	? is_class_or_union_type(l.get_naked_definition_of_declaration())
 	: &l;
 
       const class_or_union* def2 = r_is_decl_only
-	? r.get_naked_definition_of_declaration()
+	? is_class_or_union_type(r.get_naked_definition_of_declaration())
 	: &r;
 
       if (!def1 || !def2)
@@ -19682,30 +19815,6 @@ class_decl::on_canonical_type_set()
     sort_virtual_member_functions(i->second);
 }
 
-///  If this @ref class_decl is declaration-only, get its definition,
-///  if any.
-///
-/// @return the definition of the class.
-const class_decl_sptr
-class_decl::get_definition_of_declaration() const
-{return is_class_type(class_or_union::get_definition_of_declaration());}
-
-///  If this @ref class_decl is declaration-only, get its definition,
-///  if any.
-///
-/// Note that this function doesn't return a smart pointer, but rather
-/// the underlying pointer managed by the smart pointer.  So it's as
-/// fast as possible.  This getter is to be used in code paths that
-/// are proven to be performance hot spots; especially, when comparing
-/// sensitive types like class or unions.  Those are compared
-/// extremely frequently and thus, their access to the definition of
-/// declaration must be fast.
-///
-/// @return the definition of the class.
-const class_decl*
-class_decl::get_naked_definition_of_declaration() const
-{return is_class_type(class_or_union::get_naked_definition_of_declaration());}
-
 /// Set the "is-struct" flag of the class.
 ///
 /// @param f the new value of the flag.
@@ -20836,14 +20945,16 @@ class_decl::operator==(const decl_base& other) const
       && get_is_declaration_only()
       && get_naked_definition_of_declaration())
     canonical_type =
-      get_naked_definition_of_declaration()->get_naked_canonical_type();
+      is_class_type
+      (get_naked_definition_of_declaration())->get_naked_canonical_type();
 
   // Likewise for the other class.
   if (!other_canonical_type
       && op->get_is_declaration_only()
       && op->get_naked_definition_of_declaration())
     other_canonical_type =
-      op->get_naked_definition_of_declaration()->get_naked_canonical_type();
+      is_class_type
+      (op->get_naked_definition_of_declaration())->get_naked_canonical_type();
 
   if (canonical_type && other_canonical_type)
     return canonical_type == other_canonical_type;
@@ -22845,7 +22956,8 @@ hash_type(const type_base *t)
 	// The is a declaration-only class, so it has no canonical
 	// type; but then it's class definition has one.  Let's
 	// use that one.
-	return hash_type(cl->get_naked_definition_of_declaration());
+	return hash_type
+	  (is_class_type(cl->get_naked_definition_of_declaration()));
       else
 	{
 	  // The class really has no canonical type, let's use the
  
Dodji Seketeli July 8, 2020, 9:22 a.m. UTC | #2
Hello Giuliano,

Giuliano Procida <gprocida@google.com> a écrit:

> Hi Dodji.
>
> Overall this looks like an excellent compromise. In terms of the
> surgery on the type hierarchy, my main concerns would be that
>
> 1. things that are not class/union/enum but which fall under decl_base
> continue to get the same treatment

Agreed.

> 2. old places where class/union had declaration-only manipulation and
> new places where enums have the same both get the right down-casting

Yes.

> It looks like these are there. I have some further comments in-line,
> nothing significant.

Thanks.

[...]

>> +bool
>> +has_decl_only_def_change(const decl_base_sptr& first,
>> +                        const decl_base_sptr& second)
>> +{
>> +  if (!first || !second)
>> +    return false;
>> +
>> +  decl_base_sptr f =
>> +    look_through_decl_only(first);
>> +  decl_base_sptr s =
>> +    look_through_decl_only(second);
>> +
>> +  if (f->get_qualified_name() != s->get_qualified_name())
>> +    return false;
>> +
>> +  return (f->get_is_declaration_only() != s->get_is_declaration_only());
>
> This return statement doesn't need the outer ( ).

Fixed.

[...]

>>  /// Test if two @ref class_or_union_sptr are different just by the
>>  /// fact that one is decl-only and the other one is defined.
>>  ///
>> @@ -956,6 +1026,31 @@ has_class_decl_only_def_change(const class_or_union_sptr& first,
>>    return (f->get_is_declaration_only() != s->get_is_declaration_only());
>
> Same here on the return statement.

Fixed.

[...]

>> +bool
>> +has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
>> +                             const enum_type_decl_sptr& second)
>> +{
>> +  if (!first || !second)
>> +    return false;
>> +
>> +  enum_type_decl_sptr f = look_through_decl_only_enum(first);
>> +  enum_type_decl_sptr s = look_through_decl_only_enum(second);
>> +
>> +  if (f->get_qualified_name() != s->get_qualified_name())
>> +    return false;
>> +
>> +  return (f->get_is_declaration_only() != s->get_is_declaration_only());
>
> And again.

Fixed.

[...]

>> @@ -3423,7 +3427,9 @@ struct decl_base::priv
>>        is_artificial_(false),
>>        has_anonymous_parent_(false),
>>        context_(),
>> -      visibility_(VISIBILITY_DEFAULT)
>> +      visibility_(VISIBILITY_DEFAULT),
>> +      naked_definition_of_declaration_(),
>> +      is_declaration_only_()
>
> Would be clearer to say (false).

Fixed.

>>    {}
>>
>>    priv(interned_string name, const location& locus,
>> @@ -3434,7 +3440,9 @@ struct decl_base::priv
>>        name_(name),
>>        qualified_name_(name),
>>        linkage_name_(linkage_name),
>> -      visibility_(vis)
>> +      visibility_(vis),
>> +      naked_definition_of_declaration_(),
>> +      is_declaration_only_()
>
> Same here.

Fixed.

>>    {
>>      is_anonymous_ = name_.empty();
>>      has_anonymous_parent_ = false;
>> @@ -3446,7 +3454,9 @@ struct decl_base::priv
>>        has_anonymous_parent_(false),
>>        location_(l),
>>        context_(),
>> -      visibility_(VISIBILITY_DEFAULT)
>> +      visibility_(VISIBILITY_DEFAULT),
>> +      naked_definition_of_declaration_(),
>> +      is_declaration_only_()
>
> And again.

Fixed.

[...]

>> +/// Set a flag saying if the @ref enum_type_decl is a declaration-only
>> +/// @ref enum_type_decl.
>> +///
>> +/// @param f true if the @ref enum_type_decl is a decalaration-only
>
> Typo: decalaration / declaration.

Fixed.

[...]

>> +/// Test if a type is a enum. This function looks through typedefs.
>
> Typo: a enum / an enum.

Fixed.

[...]

>> +/// Test if a type is a enum. This function looks through typedefs.
>
> Same typo.

Fixed.

>> +///
>> +/// @parm t the type to consider.
>> +///
>> +/// @return the enum_decl if @p t is a enum_decl or null otherwise.
>
> Technically a typo: a enum_decl / an enum_decl.
> I'm not sure how it will render if you @ref the types.

Fixed.

>> +enum_type_decl_sptr
>> +is_compatible_with_enum_type(const decl_base_sptr& t)
>> +{return is_compatible_with_enum_type(is_type(t));}
>> +
>>  /// Test if a decl is an enum_type_decl
>>  ///
>>  /// @param d the decl to test for.
>> @@ -8270,24 +8386,19 @@ is_method_type(type_or_decl_base* t)
>>  /// @param the_klass the class (or union) to consider.
>
> Parameter is the_class.

Fixed.

>>  ///
>>  /// @return either the definition of the class, or the class itself.
>> +class_or_union*
>> +look_through_decl_only_class(class_or_union* the_class)
>> +{return is_class_or_union_type(look_through_decl_only(the_class));}
>> +
>> +/// If a class (or union) is a decl-only class, get its definition.
>> +/// Otherwise, just return the initial class.
>> +///
>> +/// @param the_klass the class (or union) to consider.
>
> Same here.

Fixed.

[...]


>> +decl_base_sptr
>> +look_through_decl_only(const decl_base& d)
>> +{
>> +  decl_base_sptr decl;
>> +  if (d.get_is_declaration_only())
>> +    decl = d.get_definition_of_declaration();
>> +
>> +  if (!decl)
>> +    return decl;
>> +
>> +  while (decl
>> +        && decl->get_is_declaration_only()
>> +        && decl->get_definition_of_declaration())
>> +    decl = decl->get_definition_of_declaration();
>> +
>> +  ABG_ASSERT(decl);
>> +  return decl;
>
> I had a suspicion that this code could be simplified a while ago.
>
> You should be able to simplify it to (something like):
>
> look_through_decl_only_class(class_or_union_sptr klass)
> {
>   if (!klass)
>     return klass;
>   while (klass->get_is_declaration_only()
>          && klass->get_definition_of_declaration())
>     klass = klass->get_definition_of_declaration();
>   return klass;
> }
>
> This is two things:
> 1. The function is simpler if it takes a (shared) pointer argument.
> You probably don't need the other overload.

Actually, I prefer keeping the function that takes reference.  It's
reused in the overload that takes a pointer.  Also, I have some
test/play programs on the side that use types instantiated on the stack
etc and that make use of these.  So they are useful in general, I think.

> 2. You don't need to retest decl on every loop or assert it at the
> end, just check once at the top as the loop condition preserves the
> invariant.

Yes, you are correct.  I have made this change in the updated patch.
Thanks.

[...]

[...]

>> +/// Look into a given corpus to find the enum type*s* that have a
>> +/// given qualified name.
>> +///
>> +/// @param qualified_name the qualified name of the type to look for.
>> +///
>> +/// @param corp the corpus to look into.
>> +///
>> +/// @return the vector of enum types that which name is @p qualified_name.
>
> Suggest: "that which name is" -> "named" (or "which have the name")
> (and again, below)

Of course.  Fixed.

[...]

Please find below an updated patch that is present in my updated
dodji/incomp-enum branch.

Thanks.


From 377b577cbbdc313e78b58631f5fb9bcc2c913a25 Mon Sep 17 00:00:00 2001
From: Giuliano Procida <gprocida@google.com>
Date: Wed, 10 Jun 2020 12:59:35 +0100
Subject: [PATCH 1/4] Support incomplete enums in core and diff code.

This is an initial implementation of the support for incomplete, also
known as forward-declared, enum types. I've not made any attempt to
refactor or share logic with the struct/union code.

	* include/abg-comp-filter.h (has_decl_only_def_change) : Declare
	New function.
	* src/abg-comp-filter.cc (there_is_a_decl_only_enum): Define new
	static function and ...
	(type_size_changed): ... use it here.
	(has_decl_only_def_change): Define new function and ...
	(categorize_harm{less, ful}_diff_node): ... use it here.
	* include/abg-fwd.h (enums_type, decl_base_wptr): Declare new
	typedefs.
	(look_through_decl_only_class): Declare new overload for
	class_or_union*.
	(is_compatible_with_enum_type, is_compatible_with_enum_type)
	(look_through_decl_only, lookup_enum_types, lookup_enum_types):
	Declare new functions.
	* include/abg-ir.h (decl_base::{get_is_declaration_only,
	set_is_declaration_only, set_definition_of_declaration,
	get_definition_of_declaration,
	get_naked_definition_of_declaration}): Declare new member
	functions.  They were moved here from the class_or_union class.
	(class_or_union::{get_earlier_declaration,
	set_earlier_declaration, get_definition_of_declaration,
	set_definition_of_declaration,
	get_naked_definition_of_declaration, get_is_declaration_only,
	set_is_declaration_only}): Remove these member functions.
	* src/abg-ir.cc (decl_base::priv::{declaration_,
	definition_of_declaration_, naked_definition_of_declaration_,
	is_declaration_only_}): Define data members.  Moved here from
	class_or_union.
	(decl_base::priv::priv): Adjust to initialize the new data
	members.
	(decl_base::{get_earlier_declaration, set_earlier_declaration,
	get_definition_of_declaration,
	get_naked_definition_of_declaration, get_is_declaration_only,
	set_is_declaration_only, set_definition_of_declaration}): Define
	member functions.
	(operator|): In the overload for (change_kind, change_kind),
	adjust the return type of the call to
	decl_base::get_definition_of_declaration.
	(look_through_decl_only): Define new function.
	(look_through_decl_only_class): Adjust.
	(look_through_decl_only_enum): Likewise.
	(maybe_update_types_lookup_map<class_decl>): Adjust return type of
	call to decl_base::get_definition_of_declaration.
	(types_defined_same_linux_kernel_corpus_public): Use
	look_through_decl_only_class rather than open coding it.
	(class_or_union::priv::{declaration_, definition_of_declaration_,
	naked_definition_of_declaration_, is_declaration_only_}): Remove
	these data members.  They are now carried by decl_base::priv.
	(class_or_union::{g,s}et_alignment_in_bits): Adjust.
	(class_or_union::{g,s}et_size_in_bits): Likewise.
	(class_or_union::operator==): Likewise.
	(equals): Adjust the overload for class_or_union.
	(is_compatible_with_enum_type)
	* src/abg-comparison.cc (try_to_diff<class_decl>): Adjust the
	return type of decl_base::get_definition_of_declaration.
	(leaf_diff_node_marker_visitor::visit_begin): Use
	filtering::has_decl_only_def_change rather than
	filtering::has_class_decl_only_def_change.  Decl-only changes to
	enums (or any other type really) will thus not be recorded as leaf
	changes.
	* src/abg-dwarf-reader.cc (get_scope_for_die): Adjust return type
	of decl_base::get_definition_of_declaration.
	* src/abg-default-reporter.cc (default_reporter::report): Report
	enum decl-only <-> definition changes.
	* src/abg-hash.cc (class_or_union::hash::operator()): In the
	overload for class_or_union& adjust the return type for
	decl_base::get_definition_of_declaration.

Signed-off-by: Giuliano Procida <gprocida@google.com>
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 include/abg-comp-filter.h   |  14 ++
 include/abg-fwd.h           |  36 ++++
 include/abg-ir.h            |  48 ++---
 src/abg-comp-filter.cc      | 133 ++++++++++++-
 src/abg-comparison.cc       |  10 +-
 src/abg-default-reporter.cc |  19 ++
 src/abg-dwarf-reader.cc     |   3 +-
 src/abg-hash.cc             |   3 +-
 src/abg-ir.cc               | 472 +++++++++++++++++++++++++++-----------------
 9 files changed, 519 insertions(+), 219 deletions(-)

diff --git a/include/abg-comp-filter.h b/include/abg-comp-filter.h
index 8113003..556bad4 100644
--- a/include/abg-comp-filter.h
+++ b/include/abg-comp-filter.h
@@ -65,13 +65,27 @@ bool
 is_decl_only_class_with_size_change(const diff *diff);
 
 bool
+has_decl_only_def_change(const decl_base_sptr& first,
+			 const decl_base_sptr& second);
+
+bool
+has_decl_only_def_change(const diff *d);
+
+bool
 has_class_decl_only_def_change(const class_or_union_sptr& first,
 			       const class_or_union_sptr& second);
 
 bool
+has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
+			      const enum_type_decl_sptr& second);
+
+bool
 has_class_decl_only_def_change(const diff *diff);
 
 bool
+has_enum_decl_only_def_change(const diff *diff);
+
+bool
 has_basic_type_name_change(const diff *);
 
 bool
diff --git a/include/abg-fwd.h b/include/abg-fwd.h
index f23f4a4..bfd9f88 100644
--- a/include/abg-fwd.h
+++ b/include/abg-fwd.h
@@ -155,6 +155,12 @@ class enum_type_decl;
 /// Convenience typedef for shared pointer to a @ref enum_type_decl.
 typedef shared_ptr<enum_type_decl> enum_type_decl_sptr;
 
+/// Convenience typedef for a vector of @ref enum_type_decl_sptr
+typedef vector<enum_type_decl_sptr> enums_type;
+
+/// Convenience typedef for a weak pointer to a @ref decl_base.
+typedef weak_ptr<decl_base> decl_base_wptr;
+
 class class_or_union;
 
 typedef shared_ptr<class_or_union> class_or_union_sptr;
@@ -424,6 +430,12 @@ typedef_decl*
 is_typedef(type_base*);
 
 enum_type_decl_sptr
+is_compatible_with_enum_type(const type_base_sptr&);
+
+enum_type_decl_sptr
+is_compatible_with_enum_type(const decl_base_sptr&);
+
+enum_type_decl_sptr
 is_enum_type(const type_or_decl_base_sptr&);
 
 const enum_type_decl*
@@ -513,6 +525,24 @@ look_through_decl_only_class(const class_or_union&);
 class_or_union_sptr
 look_through_decl_only_class(class_or_union_sptr);
 
+class_or_union*
+look_through_decl_only_class(class_or_union*);
+
+enum_type_decl_sptr
+look_through_decl_only_enum(const enum_type_decl&);
+
+enum_type_decl_sptr
+look_through_decl_only_enum(enum_type_decl_sptr);
+
+decl_base_sptr
+look_through_decl_only(const decl_base&);
+
+decl_base*
+look_through_decl_only(decl_base*);
+
+decl_base_sptr
+look_through_decl_only(const decl_base_sptr&);
+
 var_decl*
 is_var_decl(const type_or_decl_base*);
 
@@ -1085,6 +1115,12 @@ lookup_enum_type(const string&, const corpus&);
 enum_type_decl_sptr
 lookup_enum_type(const interned_string&, const corpus&);
 
+const type_base_wptrs_type*
+lookup_enum_types(const interned_string&, const corpus&);
+
+const type_base_wptrs_type*
+lookup_enum_types(const string&, const corpus&);
+
 enum_type_decl_sptr
 lookup_enum_type_per_location(const interned_string&, const corpus&);
 
diff --git a/include/abg-ir.h b/include/abg-ir.h
index c2b66c4..5b13dc9 100644
--- a/include/abg-ir.h
+++ b/include/abg-ir.h
@@ -1553,6 +1553,27 @@ public:
   void
   set_visibility(visibility v);
 
+  const decl_base_sptr
+  get_earlier_declaration() const;
+
+  void
+  set_earlier_declaration(const decl_base_sptr&);
+
+  const decl_base_sptr
+  get_definition_of_declaration() const;
+
+  void
+  set_definition_of_declaration(const decl_base_sptr&);
+
+  const decl_base*
+  get_naked_definition_of_declaration() const;
+
+  bool
+  get_is_declaration_only() const;
+
+  void
+  set_is_declaration_only(bool f);
+
   friend type_base_sptr
   canonicalize(type_base_sptr);
 
@@ -3776,27 +3797,6 @@ public:
   void
   set_naming_typedef(const typedef_decl_sptr&);
 
-  bool
-  get_is_declaration_only() const;
-
-  void
-  set_is_declaration_only(bool f);
-
-  void
-  set_definition_of_declaration(class_or_union_sptr);
-
-  const class_or_union_sptr
-  get_definition_of_declaration() const;
-
-  const class_or_union*
-  get_naked_definition_of_declaration() const;
-
-  decl_base_sptr
-  get_earlier_declaration() const;
-
-  void
-  set_earlier_declaration(decl_base_sptr declaration);
-
  void
   insert_member_type(type_base_sptr t,
 		     declarations::iterator before);
@@ -4020,12 +4020,6 @@ public:
   class_decl(const environment* env, const string& name, bool is_struct,
 	     bool is_declaration_only = true);
 
-  const class_decl_sptr
-  get_definition_of_declaration() const;
-
-  const class_decl*
-  get_naked_definition_of_declaration() const;
-
   virtual string
   get_pretty_representation(bool internal = false,
 			    bool qualified_name = true) const;
diff --git a/src/abg-comp-filter.cc b/src/abg-comp-filter.cc
index 702d223..0b0fbe4 100644
--- a/src/abg-comp-filter.cc
+++ b/src/abg-comp-filter.cc
@@ -118,6 +118,25 @@ there_is_a_decl_only_class(const class_decl_sptr& class1,
   return false;
 }
 
+/// Test if there is a enum that is declaration-only among the two
+/// enums in parameter.
+///
+/// @param enum1 the first enum to consider.
+///
+/// @param enum2 the second enum to consider.
+///
+/// @return true if either enums are declaration-only, false
+/// otherwise.
+static bool
+there_is_a_decl_only_enum(const enum_type_decl_sptr& enum1,
+			  const enum_type_decl_sptr& enum2)
+{
+  if ((enum1 && enum1->get_is_declaration_only())
+      || (enum2 && enum2->get_is_declaration_only()))
+    return true;
+  return false;
+}
+
 /// Test if the diff involves a declaration-only class.
 ///
 /// @param diff the class diff to consider.
@@ -146,7 +165,9 @@ type_size_changed(const type_base_sptr f, const type_base_sptr s)
       || f->get_size_in_bits() == 0
       || s->get_size_in_bits() == 0
       || there_is_a_decl_only_class(is_compatible_with_class_type(f),
-				    is_compatible_with_class_type(s)))
+				    is_compatible_with_class_type(s))
+      || there_is_a_decl_only_enum(is_compatible_with_enum_type(f),
+				   is_compatible_with_enum_type(s)))
     return false;
 
   return f->get_size_in_bits() != s->get_size_in_bits();
@@ -893,10 +914,8 @@ is_decl_only_class_with_size_change(const class_or_union_sptr& first,
   if (!first || !second)
     return false;
 
-  class_or_union_sptr f =
-    look_through_decl_only_class(first);
-  class_or_union_sptr s =
-    look_through_decl_only_class(second);
+  class_or_union_sptr f = look_through_decl_only_class(first);
+  class_or_union_sptr s = look_through_decl_only_class(second);
 
   return is_decl_only_class_with_size_change(*f, *s);
 }
@@ -929,6 +948,57 @@ is_decl_only_class_with_size_change(const diff *diff)
   return is_decl_only_class_with_size_change(f, s);
 }
 
+/// Test if two @ref decl_base_sptr are different just by the
+/// fact that one is decl-only and the other one is defined.
+///
+/// @param first the first decl to consider.
+///
+/// @param second the second decl to consider.
+///
+/// @return true iff the two arguments are different just by the fact
+/// that one is decl-only and the other one is defined.
+bool
+has_decl_only_def_change(const decl_base_sptr& first,
+			 const decl_base_sptr& second)
+{
+  if (!first || !second)
+    return false;
+
+  decl_base_sptr f =
+    look_through_decl_only(first);
+  decl_base_sptr s =
+    look_through_decl_only(second);
+
+  if (f->get_qualified_name() != s->get_qualified_name())
+    return false;
+
+  return f->get_is_declaration_only() != s->get_is_declaration_only();
+}
+
+/// Test if a diff carries a change in which the two decls are
+/// different by the fact that one is a decl-only and the other one is
+/// defined.
+///
+/// @param diff the diff node to consider.
+///
+/// @return true if the diff carries a change in which the two decls
+/// are different by the fact that one is a decl-only and the other
+/// one is defined.
+bool
+has_decl_only_def_change(const diff *d)
+{
+  if (!d)
+    return false;
+
+  decl_base_sptr f =
+    look_through_decl_only(is_decl(d->first_subject()));
+  decl_base_sptr s =
+    look_through_decl_only(is_decl(d->second_subject()));
+
+  return has_decl_only_def_change(f, s);
+}
+
+
 /// Test if two @ref class_or_union_sptr are different just by the
 /// fact that one is decl-only and the other one is defined.
 ///
@@ -953,7 +1023,32 @@ has_class_decl_only_def_change(const class_or_union_sptr& first,
   if (f->get_qualified_name() != s->get_qualified_name())
     return false;
 
-  return (f->get_is_declaration_only() != s->get_is_declaration_only());
+  return f->get_is_declaration_only() != s->get_is_declaration_only();
+}
+
+/// Test if two @ref enum_sptr are different just by the
+/// fact that one is decl-only and the other one is defined.
+///
+/// @param first the first enum to consider.
+///
+/// @param second the second enum to consider.
+///
+/// @return true iff the two arguments are different just by the fact
+/// that one is decl-only and the other one is defined.
+bool
+has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
+			      const enum_type_decl_sptr& second)
+{
+  if (!first || !second)
+    return false;
+
+  enum_type_decl_sptr f = look_through_decl_only_enum(first);
+  enum_type_decl_sptr s = look_through_decl_only_enum(second);
+
+  if (f->get_qualified_name() != s->get_qualified_name())
+    return false;
+
+  return f->get_is_declaration_only() != s->get_is_declaration_only();
 }
 
 /// Test if a class_or_union_diff carries a change in which the two
@@ -980,6 +1075,28 @@ has_class_decl_only_def_change(const diff *diff)
   return has_class_decl_only_def_change(f, s);
 }
 
+/// Test if a enum_diff carries a change in which the two enums are
+/// different by the fact that one is a decl-only and the other one is
+/// defined.
+///
+/// @param diff the diff node to consider.
+///
+/// @return true if the enum_diff carries a change in which the two
+/// enums are different by the fact that one is a decl-only and the
+/// other one is defined.
+bool
+has_enum_decl_only_def_change(const diff *diff)
+{
+  const enum_diff *d = dynamic_cast<const enum_diff*>(diff);
+  if (!d)
+    return false;
+
+  enum_type_decl_sptr f = look_through_decl_only_enum(d->first_enum());
+  enum_type_decl_sptr s = look_through_decl_only_enum(d->second_enum());
+
+  return has_enum_decl_only_def_change(f, s);
+}
+
 /// Test if a diff node carries a basic type name change.
 ///
 /// @param d the diff node to consider.
@@ -1517,7 +1634,8 @@ categorize_harmless_diff_node(diff *d, bool pre)
       decl_base_sptr f = is_decl(d->first_subject()),
 	s = is_decl(d->second_subject());
 
-      if (has_class_decl_only_def_change(d))
+      if (has_class_decl_only_def_change(d)
+	  || has_enum_decl_only_def_change(d))
 	category |= TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY;
 
       if (access_changed(f, s))
@@ -1608,6 +1726,7 @@ categorize_harmful_diff_node(diff *d, bool pre)
       //
       // TODO: be more specific -- not all size changes are harmful.
       if (!has_class_decl_only_def_change(d)
+	  && !has_enum_decl_only_def_change(d)
 	  && (type_size_changed(f, s)
 	      || data_member_offset_changed(f, s)
 	      || non_static_data_member_type_size_changed(f, s)
diff --git a/src/abg-comparison.cc b/src/abg-comparison.cc
index d0f1d21..b6f065f 100644
--- a/src/abg-comparison.cc
+++ b/src/abg-comparison.cc
@@ -2827,13 +2827,15 @@ try_to_diff<class_decl>(const type_or_decl_base_sptr first,
 
       if (f->get_is_declaration_only())
 	{
-	  class_decl_sptr f2 = f->get_definition_of_declaration();
+	  class_decl_sptr f2 =
+	    is_class_type (f->get_definition_of_declaration());
 	  if (f2)
 	    f = f2;
 	}
       if (s->get_is_declaration_only())
 	{
-	  class_decl_sptr s2 = s->get_definition_of_declaration();
+	  class_decl_sptr s2 =
+	    is_class_type(s->get_definition_of_declaration());
 	  if (s2)
 	    s = s2;
 	}
@@ -10953,8 +10955,8 @@ struct leaf_diff_node_marker_visitor : public diff_node_visitor
 	// typedef change which underlying type is an anonymous
 	// struct/union.
 	&& !is_anonymous_class_or_union_diff(d)
-	// Don't show decl-only-ness changes of classes either.
-	&& !filtering::has_class_decl_only_def_change(d)
+	// Don't show decl-only-ness changes either.
+	&& !filtering::has_decl_only_def_change(d)
 	// Sometime, we can encounter artifacts of bogus DWARF that
 	// yield a diff node for a decl-only class (and empty class
 	// with the is_declaration flag set) that carries a non-zero
diff --git a/src/abg-default-reporter.cc b/src/abg-default-reporter.cc
index 2acb695..cbf8c2b 100644
--- a/src/abg-default-reporter.cc
+++ b/src/abg-default-reporter.cc
@@ -99,6 +99,25 @@ default_reporter::report(const enum_diff& d, ostream& out,
   enum_type_decl_sptr first = d.first_enum(), second = d.second_enum();
 
   const diff_context_sptr& ctxt = d.context();
+
+  // Report enum decl-only <-> definition changes.
+  if (ctxt->get_allowed_category() & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY)
+    if (filtering::has_enum_decl_only_def_change(first, second))
+      {
+	string was =
+	  first->get_is_declaration_only()
+	  ? " was a declaration-only enum type"
+	  : " was a defined enum type";
+
+	string is_now =
+	  second->get_is_declaration_only()
+	  ? " and is now a declaration-only enum type"
+	  : " and is now a defined enum type";
+
+	out << indent << "enum type " << name << was << is_now << "\n";
+	return;
+      }
+
   report_name_size_and_alignment_changes(first, second, ctxt,
 					 out, indent);
   maybe_report_diff_for_member(first, second, ctxt, out, indent);
diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc
index ba4e750..db43aa7 100644
--- a/src/abg-dwarf-reader.cc
+++ b/src/abg-dwarf-reader.cc
@@ -12315,7 +12315,8 @@ get_scope_for_die(read_context& ctxt,
   class_decl_sptr cl = dynamic_pointer_cast<class_decl>(d);
   if (cl && cl->get_is_declaration_only())
     {
-      scope_decl_sptr scop (cl->get_definition_of_declaration());
+      scope_decl_sptr scop  =
+	dynamic_pointer_cast<scope_decl>(cl->get_definition_of_declaration());
       if (scop)
 	s = scop;
       else
diff --git a/src/abg-hash.cc b/src/abg-hash.cc
index c1cdc57..5d2861f 100644
--- a/src/abg-hash.cc
+++ b/src/abg-hash.cc
@@ -635,7 +635,8 @@ class_or_union::hash::operator()(const class_or_union& t) const
   if (t.get_is_declaration_only())
     {
       ABG_ASSERT(t.get_definition_of_declaration());
-      size_t v = operator()(*t.get_definition_of_declaration());
+      size_t v = operator()
+	(*is_class_or_union_type(t.get_definition_of_declaration()));
       return v;
     }
 
diff --git a/src/abg-ir.cc b/src/abg-ir.cc
index a434ec6..15bf124 100644
--- a/src/abg-ir.cc
+++ b/src/abg-ir.cc
@@ -3416,6 +3416,10 @@ struct decl_base::priv
     interned_string	scoped_name_;
   interned_string	linkage_name_;
   visibility		visibility_;
+  decl_base_sptr	declaration_;
+  decl_base_wptr	definition_of_declaration_;
+  decl_base*		naked_definition_of_declaration_;
+  bool			is_declaration_only_;
 
   priv()
     : in_pub_sym_tab_(false),
@@ -3423,7 +3427,9 @@ struct decl_base::priv
       is_artificial_(false),
       has_anonymous_parent_(false),
       context_(),
-      visibility_(VISIBILITY_DEFAULT)
+      visibility_(VISIBILITY_DEFAULT),
+      naked_definition_of_declaration_(),
+      is_declaration_only_(false)
   {}
 
   priv(interned_string name, const location& locus,
@@ -3434,7 +3440,9 @@ struct decl_base::priv
       name_(name),
       qualified_name_(name),
       linkage_name_(linkage_name),
-      visibility_(vis)
+      visibility_(vis),
+      naked_definition_of_declaration_(),
+      is_declaration_only_(false)
   {
     is_anonymous_ = name_.empty();
     has_anonymous_parent_ = false;
@@ -3446,7 +3454,9 @@ struct decl_base::priv
       has_anonymous_parent_(false),
       location_(l),
       context_(),
-      visibility_(VISIBILITY_DEFAULT)
+      visibility_(VISIBILITY_DEFAULT),
+      naked_definition_of_declaration_(),
+      is_declaration_only_(false)
   {}
 
   ~priv()
@@ -3881,6 +3891,80 @@ const interned_string&
 decl_base::get_scoped_name() const
 {return priv_->scoped_name_;}
 
+/// If this @ref decl_base is a definition, get its earlier
+/// declaration.
+///
+/// @return the earlier declaration of the class, if any.
+const decl_base_sptr
+decl_base::get_earlier_declaration() const
+{return priv_->declaration_;}
+
+/// set the earlier declaration of this @ref decl_base definition.
+///
+/// @param d the earlier declaration to set.  Note that it's set only
+/// if it's a pure declaration.
+void
+decl_base::set_earlier_declaration(const decl_base_sptr& d)
+{
+  if (d && d->get_is_declaration_only())
+    priv_->declaration_ = d;
+}
+
+
+/// If this @ref decl_base is declaration-only, get its definition, if
+/// any.
+///
+/// @return the definition of this decl-only @ref decl_base.
+const decl_base_sptr
+decl_base::get_definition_of_declaration() const
+{return priv_->definition_of_declaration_.lock();}
+
+///  If this @ref decl_base is declaration-only, get its definition,
+///  if any.
+///
+/// Note that this function doesn't return a smart pointer, but rather
+/// the underlying pointer managed by the smart pointer.  So it's as
+/// fast as possible.  This getter is to be used in code paths that
+/// are proven to be performance hot spots; especially, when comparing
+/// sensitive types like enums, classes or unions.  Those are compared
+/// extremely frequently and thus, their access to the definition of
+/// declaration must be fast.
+///
+/// @return the definition of the declaration.
+const decl_base*
+decl_base::get_naked_definition_of_declaration() const
+{return priv_->naked_definition_of_declaration_;}
+
+/// Test if a @ref decl_base is a declaration-only decl.
+///
+/// @return true iff the current @ref decl_base is declaration-only.
+bool
+decl_base::get_is_declaration_only() const
+{return priv_->is_declaration_only_;}
+
+/// Set a flag saying if the @ref enum_type_decl is a declaration-only
+/// @ref enum_type_decl.
+///
+/// @param f true if the @ref enum_type_decl is a declaration-only
+/// @ref enum_type_decl.
+void
+decl_base::set_is_declaration_only(bool f)
+{
+  bool update_types_lookup_map = !f && priv_->is_declaration_only_;
+
+  priv_->is_declaration_only_ = f;
+
+  if (update_types_lookup_map)
+    if (scope_decl* s = get_scope())
+      {
+	scope_decl::declarations::iterator i;
+	if (s->find_iterator_for_member(this, i))
+	  maybe_update_types_lookup_map(*i);
+	else
+	  ABG_ASSERT_NOT_REACHED;
+      }
+}
+
 change_kind
 operator|(change_kind l, change_kind r)
 {
@@ -4236,7 +4320,7 @@ operator!=(const type_base_sptr& l, const type_base_sptr& r)
 
 /// Tests if a declaration has got a scope.
 ///
-/// @param d the decalaration to consider.
+/// @param d the declaration to consider.
 ///
 /// @return true if the declaration has got a scope, false otherwise.
 bool
@@ -4245,7 +4329,7 @@ has_scope(const decl_base& d)
 
 /// Tests if a declaration has got a scope.
 ///
-/// @param d the decalaration to consider.
+/// @param d the declaration to consider.
 ///
 /// @return true if the declaration has got a scope, false otherwise.
 bool
@@ -6686,7 +6770,7 @@ get_location(const decl_base_sptr& decl)
       if (class_or_union_sptr c = is_class_or_union_type(decl))
 	if (c->get_is_declaration_only() && c->get_definition_of_declaration())
 	  {
-	    c = c->get_definition_of_declaration();
+	    c = is_class_or_union_type(c->get_definition_of_declaration());
 	    loc = c->get_location();
 	  }
     }
@@ -7925,6 +8009,40 @@ typedef_decl*
 is_typedef(type_base* t)
 {return dynamic_cast<typedef_decl*>(t);}
 
+/// Test if a type is an enum. This function looks through typedefs.
+///
+/// @parm t the type to consider.
+///
+/// @return the enum_decl if @p t is an @ref enum_decl or null
+/// otherwise.
+enum_type_decl_sptr
+is_compatible_with_enum_type(const type_base_sptr& t)
+{
+  if (!t)
+    return enum_type_decl_sptr();
+
+  // Normally we should strip typedefs entirely, but this is
+  // potentially costly, especially on binaries with huge changesets
+  // like the Linux Kernel.  So we just get the leaf types for now.
+  //
+  // Maybe there should be an option by which users accepts to pay the
+  // CPU usage toll in exchange for finer filtering?
+
+  // type_base_sptr ty = strip_typedef(t);
+  type_base_sptr ty = peel_typedef_type(t);;
+  return is_enum_type(ty);
+}
+
+/// Test if a type is an enum. This function looks through typedefs.
+///
+/// @parm t the type to consider.
+///
+/// @return the enum_decl if @p t is an @ref enum_decl or null
+/// otherwise.
+enum_type_decl_sptr
+is_compatible_with_enum_type(const decl_base_sptr& t)
+{return is_compatible_with_enum_type(is_type(t));}
+
 /// Test if a decl is an enum_type_decl
 ///
 /// @param d the decl to test for.
@@ -8267,27 +8385,22 @@ is_method_type(type_or_decl_base* t)
 /// If a class (or union) is a decl-only class, get its definition.
 /// Otherwise, just return the initial class.
 ///
-/// @param the_klass the class (or union) to consider.
+/// @param the_class the class (or union) to consider.
+///
+/// @return either the definition of the class, or the class itself.
+class_or_union*
+look_through_decl_only_class(class_or_union* the_class)
+{return is_class_or_union_type(look_through_decl_only(the_class));}
+
+/// If a class (or union) is a decl-only class, get its definition.
+/// Otherwise, just return the initial class.
+///
+/// @param the_class the class (or union) to consider.
 ///
 /// @return either the definition of the class, or the class itself.
 class_or_union_sptr
 look_through_decl_only_class(const class_or_union& the_class)
-{
-  class_or_union_sptr klass;
-  if (the_class.get_is_declaration_only())
-    klass = the_class.get_definition_of_declaration();
-
-  if (!klass)
-    return klass;
-
-  while (klass
-	 && klass->get_is_declaration_only()
-	 && klass->get_definition_of_declaration())
-    klass = klass->get_definition_of_declaration();
-
-  ABG_ASSERT(klass);
-  return klass;
-}
+{return is_class_or_union_type(look_through_decl_only(the_class));}
 
 /// If a class (or union) is a decl-only class, get its definition.
 /// Otherwise, just return the initial class.
@@ -8297,13 +8410,84 @@ look_through_decl_only_class(const class_or_union& the_class)
 /// @return either the definition of the class, or the class itself.
 class_or_union_sptr
 look_through_decl_only_class(class_or_union_sptr klass)
+{return is_class_or_union_type(look_through_decl_only(klass));}
+
+/// If an enum is a decl-only enum, get its definition.
+/// Otherwise, just return the initial enum.
+///
+/// @param the_enum the enum to consider.
+///
+/// @return either the definition of the enum, or the enum itself.
+enum_type_decl_sptr
+look_through_decl_only_enum(const enum_type_decl& the_enum)
+{return is_enum_type(look_through_decl_only(the_enum));}
+
+/// If an enum is a decl-only enum, get its definition.
+/// Otherwise, just return the initial enum.
+///
+/// @param enom the enum to consider.
+///
+/// @return either the definition of the enum, or the enum itself.
+enum_type_decl_sptr
+look_through_decl_only_enum(enum_type_decl_sptr enom)
+{return is_enum_type(look_through_decl_only(enom));}
+
+/// If a decl is decl-only get its definition.  Otherwise, just return nil.
+///
+/// @param d the decl to consider.
+///
+/// @return either the definition of the decl, or nil.
+decl_base_sptr
+look_through_decl_only(const decl_base& d)
+{
+  decl_base_sptr decl;
+  if (d.get_is_declaration_only())
+    decl = d.get_definition_of_declaration();
+
+  if (!decl)
+    return decl;
+
+  while (decl->get_is_declaration_only()
+	 && decl->get_definition_of_declaration())
+    decl = decl->get_definition_of_declaration();
+
+  ABG_ASSERT(decl);
+  return decl;
+}
+
+/// If a decl is decl-only enum, get its definition.  Otherwise, just
+/// return the initial decl.
+///
+/// @param d the decl to consider.
+///
+/// @return either the definition of the enum, or the decl itself.
+decl_base*
+look_through_decl_only(decl_base* d)
+{
+  if (!d)
+    return d;
+
+  decl_base* result = look_through_decl_only(*d).get();
+  if (!result)
+    result = d;
+
+  return result;
+}
+
+/// If a decl is decl-only get its definition.  Otherwise, just return nil.
+///
+/// @param d the decl to consider.
+///
+/// @return either the definition of the decl, or nil.
+decl_base_sptr
+look_through_decl_only(const decl_base_sptr& d)
 {
-  if (!klass)
-    return klass;
+  if (!d)
+    return d;
 
-  class_or_union_sptr result = look_through_decl_only_class(*klass);
+  decl_base_sptr result = look_through_decl_only(*d);
   if (!result)
-    result = klass;
+    result = d;
 
   return result;
 }
@@ -8784,7 +8968,7 @@ lookup_union_type_per_location(const string& loc, const corpus& corp)
   return lookup_union_type_per_location(env->intern(loc), corp);
 }
 
-/// Lookup a enum type from a translation unit.
+/// Lookup an enum type from a translation unit.
 ///
 /// This is done by looking the type up in the type map that is
 /// maintained in the translation unit.  So this is as fast as
@@ -8802,7 +8986,7 @@ lookup_enum_type(const interned_string& type_name, const translation_unit& tu)
 					    tu.get_types().enum_types());
 }
 
-/// Lookup a enum type from a translation unit.
+/// Lookup an enum type from a translation unit.
 ///
 /// This is done by looking the type up in the type map that is
 /// maintained in the translation unit.  So this is as fast as
@@ -10004,7 +10188,7 @@ lookup_class_type(const interned_string& qualified_name, const corpus& corp)
 ///
 /// @param corp the corpus to look into.
 ///
-/// @return the vector of class types that which name is @p qualified_name.
+/// @return the vector of class types named @p qualified_name.
 const type_base_wptrs_type *
 lookup_class_types(const interned_string& qualified_name, const corpus& corp)
 {
@@ -10103,7 +10287,7 @@ lookup_union_type(const string& type_name, const corpus& corp)
   return lookup_union_type(s, corp);
 }
 
-/// Look into a given corpus to find a enum type which has the same
+/// Look into a given corpus to find an enum type which has the same
 /// qualified name as a given enum type.
 ///
 /// If the per-corpus type map is non-empty (because the corpus allows
@@ -10166,6 +10350,37 @@ lookup_enum_type(const interned_string& qualified_name, const corpus& corp)
   return result;
 }
 
+/// Look into a given corpus to find the enum type*s* that have a
+/// given qualified name.
+///
+/// @param qualified_name the qualified name of the type to look for.
+///
+/// @param corp the corpus to look into.
+///
+/// @return the vector of enum types that which name is @p qualified_name.
+const type_base_wptrs_type *
+lookup_enum_types(const interned_string& qualified_name, const corpus& corp)
+{
+  const istring_type_base_wptrs_map_type& m = corp.get_types().enum_types();
+
+  return lookup_types_in_map(qualified_name, m);
+}
+
+/// Look into a given corpus to find the enum type*s* that have a
+/// given qualified name.
+///
+/// @param qualified_name the qualified name of the type to look for.
+///
+/// @param corp the corpus to look into.
+///
+/// @return the vector of enum types that which name is @p qualified_name.
+const type_base_wptrs_type*
+lookup_enum_types(const string& qualified_name, const corpus& corp)
+{
+  interned_string s = corp.get_environment()->intern(qualified_name);
+  return lookup_enum_types(s, corp);
+}
+
 /// Look up an @ref enum_type_decl from a given corpus, by its location.
 ///
 /// @param loc the location to consider.
@@ -10767,7 +10982,8 @@ maybe_update_types_lookup_map<class_decl>(const class_decl_sptr& class_type,
   bool update_qname_map = true;
   if (type->get_is_declaration_only())
     {
-      if (class_decl_sptr def = class_type->get_definition_of_declaration())
+      if (class_decl_sptr def =
+	  is_class_type(class_type->get_definition_of_declaration()))
 	type = def;
       else
 	update_qname_map = false;
@@ -11683,10 +11899,8 @@ types_defined_same_linux_kernel_corpus_public(const type_base& t1,
 
   // Look through declaration-only types.  That is, get the associated
   // definition type.
-  if (c1 && c1->get_is_declaration_only())
-    c1 = c1->get_definition_of_declaration().get();
-  if (c2 && c2->get_is_declaration_only())
-    c2 = c2->get_definition_of_declaration().get();
+  c1 = look_through_decl_only_class(c1);
+  c2 = look_through_decl_only_class(c2);
 
   if (c1 && c2)
     {
@@ -12079,6 +12293,23 @@ canonicalize(type_base_sptr t)
   return canonical;
 }
 
+
+/// Set the definition of this declaration-only @ref decl_base.
+///
+/// @param d the new definition to set.
+void
+decl_base::set_definition_of_declaration(const decl_base_sptr& d)
+{
+  ABG_ASSERT(get_is_declaration_only());
+  priv_->definition_of_declaration_ = d;
+  priv_->definition_of_declaration_ = d;
+  if (type_base *t = is_type(this))
+    if (type_base_sptr canonical_type = is_type(d)->get_canonical_type())
+      t->priv_->canonical_type = canonical_type;
+
+  priv_->naked_definition_of_declaration_ = const_cast<decl_base*>(d.get());
+}
+
 /// The constructor of @ref type_base.
 ///
 /// @param s the size of the type, in bits.
@@ -14978,7 +15209,6 @@ class enum_type_decl::priv
   priv();
 
 public:
-
   priv(type_base_sptr underlying_type,
        enumerators& enumerators)
     : underlying_type_(underlying_type),
@@ -17987,9 +18217,6 @@ function_decl::parameter::get_pretty_representation(bool internal,
 struct class_or_union::priv
 {
   typedef_decl_wptr		naming_typedef_;
-  decl_base_sptr		declaration_;
-  class_or_union_wptr		definition_of_declaration_;
-  class_or_union*		naked_definition_of_declaration_;
   member_types			member_types_;
   data_members			data_members_;
   data_members			non_static_data_members_;
@@ -18001,21 +18228,16 @@ struct class_or_union::priv
   string_mem_fn_ptr_map_type	signature_2_mem_fn_map_;
   member_function_templates	member_function_templates_;
   member_class_templates	member_class_templates_;
-  bool				is_declaration_only_;
 
   priv()
-    : naked_definition_of_declaration_(),
-      is_declaration_only_(false)
   {}
 
   priv(class_or_union::member_types& mbr_types,
        class_or_union::data_members& data_mbrs,
        class_or_union::member_functions& mbr_fns)
-    : naked_definition_of_declaration_(),
-      member_types_(mbr_types),
+    : member_types_(mbr_types),
       data_members_(data_mbrs),
-      member_functions_(mbr_fns),
-      is_declaration_only_(false)
+      member_functions_(mbr_fns)
   {
     for (data_members::const_iterator i = data_members_.begin();
 	 i != data_members_.end();
@@ -18024,11 +18246,6 @@ struct class_or_union::priv
 	non_static_data_members_.push_back(*i);
   }
 
-  priv(bool is_declaration_only)
-    : naked_definition_of_declaration_(),
-      is_declaration_only_(is_declaration_only)
-  {}
-
   /// Mark a class or union or union as being currently compared using
   /// the class_or_union== operator.
   ///
@@ -18244,8 +18461,10 @@ class_or_union::class_or_union(const environment* env, const string& name,
     decl_base(env, name, location(), name),
     type_base(env, 0, 0),
     scope_type_decl(env, name, 0, 0, location()),
-    priv_(new priv(is_declaration_only))
-{}
+    priv_(new priv)
+{
+  set_is_declaration_only(is_declaration_only);
+}
 
 /// This implements the ir_traversable_base::traverse pure virtual
 /// function.
@@ -18465,7 +18684,8 @@ size_t
 class_or_union::get_alignment_in_bits() const
 {
   if (get_is_declaration_only() && get_definition_of_declaration())
-    return get_definition_of_declaration()->get_alignment_in_bits();
+    return is_class_or_union_type
+      (get_definition_of_declaration())->get_alignment_in_bits();
 
    return type_base::get_alignment_in_bits();
 }
@@ -18480,7 +18700,8 @@ void
 class_or_union::set_alignment_in_bits(size_t a)
 {
   if (get_is_declaration_only() && get_definition_of_declaration())
-    get_definition_of_declaration()->set_alignment_in_bits(a);
+    is_class_or_union_type
+      (get_definition_of_declaration()) ->set_alignment_in_bits(a);
   else
     type_base::set_alignment_in_bits(a);
 }
@@ -18495,7 +18716,8 @@ void
 class_or_union::set_size_in_bits(size_t s)
 {
   if (get_is_declaration_only() && get_definition_of_declaration())
-    get_definition_of_declaration()->set_size_in_bits(s);
+    is_class_or_union_type
+      (get_definition_of_declaration())->set_size_in_bits(s);
   else
     type_base::set_size_in_bits(s);
 }
@@ -18510,42 +18732,12 @@ size_t
 class_or_union::get_size_in_bits() const
 {
   if (get_is_declaration_only() && get_definition_of_declaration())
-    return get_definition_of_declaration()->get_size_in_bits();
+    return is_class_or_union_type
+      (get_definition_of_declaration())->get_size_in_bits();
 
   return type_base::get_size_in_bits();
 }
 
-/// Test if a @ref class_or_union is a declaration-only @ref
-/// class_or_union.
-///
-/// @return true iff the current @ref class_or_union is a
-/// declaration-only @ref class_or_union.
-bool
-class_or_union::get_is_declaration_only() const
-{return priv_->is_declaration_only_;}
-
-/// Set a flag saying if the @ref class_or_union is a declaration-only
-/// @ref class_or_union.
-///
-/// @param f true if the @ref class_or_union is a decalaration-only
-/// @ref class_or_union.
-void
-class_or_union::set_is_declaration_only(bool f)
-{
-  bool update_types_lookup_map = !f && priv_->is_declaration_only_;
-
-  priv_->is_declaration_only_ = f;
-
-  if (update_types_lookup_map)
-    if (scope_decl* s = get_scope())
-      {
-	declarations::iterator i;
-	if (s->find_iterator_for_member(this, i))
-	  maybe_update_types_lookup_map(*i);
-	else
-	  ABG_ASSERT_NOT_REACHED;
-      }
-}
 
 /// Getter for the naming typedef of the current class.
 ///
@@ -18577,64 +18769,6 @@ class_or_union::set_naming_typedef(const typedef_decl_sptr& typedef_type)
   priv_->naming_typedef_ = typedef_type;
 }
 
-/// Set the definition of this declaration-only @ref class_or_union.
-///
-/// @param d the new definition to set.
-void
-class_or_union::set_definition_of_declaration(class_or_union_sptr d)
-{
-  ABG_ASSERT(get_is_declaration_only());
-  priv_->definition_of_declaration_ = d;
-  if (d->get_canonical_type())
-    type_base::priv_->canonical_type = d->get_canonical_type();
-
-  priv_->naked_definition_of_declaration_ = d.get();
-}
-
-/// If this @ref class_or_union_sptr is declaration-only, get its
-/// definition, if any.
-///
-/// @return the definition of this decl-only class.
-const class_or_union_sptr
-class_or_union::get_definition_of_declaration() const
-{return priv_->definition_of_declaration_.lock();}
-
-///  If this @ref class_or_union is declaration-only, get its
-///  definition, if any.
-///
-/// Note that this function doesn't return a smart pointer, but rather
-/// the underlying pointer managed by the smart pointer.  So it's as
-/// fast as possible.  This getter is to be used in code paths that
-/// are proven to be performance hot spots; especially, when comparing
-/// sensitive types like class or unions.  Those are compared
-/// extremely frequently and thus, their access to the definition of
-/// declaration must be fast.
-///
-/// @return the definition of the class.
-const class_or_union*
-class_or_union::get_naked_definition_of_declaration() const
-{return priv_->naked_definition_of_declaration_;}
-
-/// If this @ref class_or_union_sptr is a definitin, get its earlier
-/// declaration.
-///
-/// @return the earlier declaration of the class, if any.
-decl_base_sptr
-class_or_union::get_earlier_declaration() const
-{return priv_->declaration_;}
-
-/// set the earlier declaration of this @ref class_or_union definition.
-///
-/// @param declaration the earlier declaration to set.  Note that it's
-/// set only if it's a pure declaration.
-void
-class_or_union::set_earlier_declaration(decl_base_sptr declaration)
-{
-  class_or_union_sptr cl = dynamic_pointer_cast<class_or_union>(declaration);
-  if (cl && cl->get_is_declaration_only())
-    priv_->declaration_ = declaration;
-}
-
 /// Get the member types of this @ref class_or_union.
 ///
 /// @return a vector of the member types of this ref class_or_union.
@@ -19082,15 +19216,16 @@ class_or_union::operator==(const decl_base& other) const
   if (!canonical_type
       && get_is_declaration_only()
       && get_naked_definition_of_declaration())
-    canonical_type =
-      get_naked_definition_of_declaration()->get_naked_canonical_type();
+    canonical_type = is_class_or_union_type
+      (get_naked_definition_of_declaration())->get_naked_canonical_type();
 
   // Likewise for the other class.
   if (!other_canonical_type
       && op->get_is_declaration_only()
       && op->get_naked_definition_of_declaration())
     other_canonical_type =
-      op->get_naked_definition_of_declaration()->get_naked_canonical_type();
+      is_class_or_union_type
+      (op->get_naked_definition_of_declaration())->get_naked_canonical_type();
 
   if (canonical_type && other_canonical_type)
     return canonical_type == other_canonical_type;
@@ -19161,11 +19296,11 @@ equals(const class_or_union& l, const class_or_union& r, change_kind* k)
   if (l_is_decl_only || r_is_decl_only)
     {
       const class_or_union* def1 = l_is_decl_only
-	? l.get_naked_definition_of_declaration()
+	? is_class_or_union_type(l.get_naked_definition_of_declaration())
 	: &l;
 
       const class_or_union* def2 = r_is_decl_only
-	? r.get_naked_definition_of_declaration()
+	? is_class_or_union_type(r.get_naked_definition_of_declaration())
 	: &r;
 
       if (!def1 || !def2)
@@ -19784,30 +19919,6 @@ class_decl::on_canonical_type_set()
     sort_virtual_member_functions(i->second);
 }
 
-///  If this @ref class_decl is declaration-only, get its definition,
-///  if any.
-///
-/// @return the definition of the class.
-const class_decl_sptr
-class_decl::get_definition_of_declaration() const
-{return is_class_type(class_or_union::get_definition_of_declaration());}
-
-///  If this @ref class_decl is declaration-only, get its definition,
-///  if any.
-///
-/// Note that this function doesn't return a smart pointer, but rather
-/// the underlying pointer managed by the smart pointer.  So it's as
-/// fast as possible.  This getter is to be used in code paths that
-/// are proven to be performance hot spots; especially, when comparing
-/// sensitive types like class or unions.  Those are compared
-/// extremely frequently and thus, their access to the definition of
-/// declaration must be fast.
-///
-/// @return the definition of the class.
-const class_decl*
-class_decl::get_naked_definition_of_declaration() const
-{return is_class_type(class_or_union::get_naked_definition_of_declaration());}
-
 /// Set the "is-struct" flag of the class.
 ///
 /// @param f the new value of the flag.
@@ -20938,14 +21049,16 @@ class_decl::operator==(const decl_base& other) const
       && get_is_declaration_only()
       && get_naked_definition_of_declaration())
     canonical_type =
-      get_naked_definition_of_declaration()->get_naked_canonical_type();
+      is_class_type
+      (get_naked_definition_of_declaration())->get_naked_canonical_type();
 
   // Likewise for the other class.
   if (!other_canonical_type
       && op->get_is_declaration_only()
       && op->get_naked_definition_of_declaration())
     other_canonical_type =
-      op->get_naked_definition_of_declaration()->get_naked_canonical_type();
+      is_class_type
+      (op->get_naked_definition_of_declaration())->get_naked_canonical_type();
 
   if (canonical_type && other_canonical_type)
     return canonical_type == other_canonical_type;
@@ -23035,7 +23148,8 @@ hash_type(const type_base *t)
 	// The is a declaration-only class, so it has no canonical
 	// type; but then it's class definition has one.  Let's
 	// use that one.
-	return hash_type(cl->get_naked_definition_of_declaration());
+	return hash_type
+	  (is_class_type(cl->get_naked_definition_of_declaration()));
       else
 	{
 	  // The class really has no canonical type, let's use the
  
Giuliano Procida July 8, 2020, 10:39 a.m. UTC | #3
On Wed, 8 Jul 2020 at 10:22, Dodji Seketeli <dodji@seketeli.org> wrote:
>
> Hello Giuliano,
>
> Giuliano Procida <gprocida@google.com> a écrit:
>
> > Hi Dodji.
> >
> > Overall this looks like an excellent compromise. In terms of the
> > surgery on the type hierarchy, my main concerns would be that
> >
> > 1. things that are not class/union/enum but which fall under decl_base
> > continue to get the same treatment
>
> Agreed.
>
> > 2. old places where class/union had declaration-only manipulation and
> > new places where enums have the same both get the right down-casting
>
> Yes.
>
> > It looks like these are there. I have some further comments in-line,
> > nothing significant.
>
> Thanks.
>
> [...]
>
> >> +bool
> >> +has_decl_only_def_change(const decl_base_sptr& first,
> >> +                        const decl_base_sptr& second)
> >> +{
> >> +  if (!first || !second)
> >> +    return false;
> >> +
> >> +  decl_base_sptr f =
> >> +    look_through_decl_only(first);
> >> +  decl_base_sptr s =
> >> +    look_through_decl_only(second);
> >> +
> >> +  if (f->get_qualified_name() != s->get_qualified_name())
> >> +    return false;
> >> +
> >> +  return (f->get_is_declaration_only() != s->get_is_declaration_only());
> >
> > This return statement doesn't need the outer ( ).
>
> Fixed.
>
> [...]
>
> >>  /// Test if two @ref class_or_union_sptr are different just by the
> >>  /// fact that one is decl-only and the other one is defined.
> >>  ///
> >> @@ -956,6 +1026,31 @@ has_class_decl_only_def_change(const class_or_union_sptr& first,
> >>    return (f->get_is_declaration_only() != s->get_is_declaration_only());
> >
> > Same here on the return statement.
>
> Fixed.
>
> [...]
>
> >> +bool
> >> +has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
> >> +                             const enum_type_decl_sptr& second)
> >> +{
> >> +  if (!first || !second)
> >> +    return false;
> >> +
> >> +  enum_type_decl_sptr f = look_through_decl_only_enum(first);
> >> +  enum_type_decl_sptr s = look_through_decl_only_enum(second);
> >> +
> >> +  if (f->get_qualified_name() != s->get_qualified_name())
> >> +    return false;
> >> +
> >> +  return (f->get_is_declaration_only() != s->get_is_declaration_only());
> >
> > And again.
>
> Fixed.
>
> [...]
>
> >> @@ -3423,7 +3427,9 @@ struct decl_base::priv
> >>        is_artificial_(false),
> >>        has_anonymous_parent_(false),
> >>        context_(),
> >> -      visibility_(VISIBILITY_DEFAULT)
> >> +      visibility_(VISIBILITY_DEFAULT),
> >> +      naked_definition_of_declaration_(),
> >> +      is_declaration_only_()
> >
> > Would be clearer to say (false).
>
> Fixed.
>
> >>    {}
> >>
> >>    priv(interned_string name, const location& locus,
> >> @@ -3434,7 +3440,9 @@ struct decl_base::priv
> >>        name_(name),
> >>        qualified_name_(name),
> >>        linkage_name_(linkage_name),
> >> -      visibility_(vis)
> >> +      visibility_(vis),
> >> +      naked_definition_of_declaration_(),
> >> +      is_declaration_only_()
> >
> > Same here.
>
> Fixed.
>
> >>    {
> >>      is_anonymous_ = name_.empty();
> >>      has_anonymous_parent_ = false;
> >> @@ -3446,7 +3454,9 @@ struct decl_base::priv
> >>        has_anonymous_parent_(false),
> >>        location_(l),
> >>        context_(),
> >> -      visibility_(VISIBILITY_DEFAULT)
> >> +      visibility_(VISIBILITY_DEFAULT),
> >> +      naked_definition_of_declaration_(),
> >> +      is_declaration_only_()
> >
> > And again.
>
> Fixed.
>
> [...]
>
> >> +/// Set a flag saying if the @ref enum_type_decl is a declaration-only
> >> +/// @ref enum_type_decl.
> >> +///
> >> +/// @param f true if the @ref enum_type_decl is a decalaration-only
> >
> > Typo: decalaration / declaration.
>
> Fixed.
>
> [...]
>
> >> +/// Test if a type is a enum. This function looks through typedefs.
> >
> > Typo: a enum / an enum.
>
> Fixed.
>
> [...]
>
> >> +/// Test if a type is a enum. This function looks through typedefs.
> >
> > Same typo.
>
> Fixed.
>
> >> +///
> >> +/// @parm t the type to consider.
> >> +///
> >> +/// @return the enum_decl if @p t is a enum_decl or null otherwise.
> >
> > Technically a typo: a enum_decl / an enum_decl.
> > I'm not sure how it will render if you @ref the types.
>
> Fixed.
>
> >> +enum_type_decl_sptr
> >> +is_compatible_with_enum_type(const decl_base_sptr& t)
> >> +{return is_compatible_with_enum_type(is_type(t));}
> >> +
> >>  /// Test if a decl is an enum_type_decl
> >>  ///
> >>  /// @param d the decl to test for.
> >> @@ -8270,24 +8386,19 @@ is_method_type(type_or_decl_base* t)
> >>  /// @param the_klass the class (or union) to consider.
> >
> > Parameter is the_class.
>
> Fixed.
>
> >>  ///
> >>  /// @return either the definition of the class, or the class itself.
> >> +class_or_union*
> >> +look_through_decl_only_class(class_or_union* the_class)
> >> +{return is_class_or_union_type(look_through_decl_only(the_class));}
> >> +
> >> +/// If a class (or union) is a decl-only class, get its definition.
> >> +/// Otherwise, just return the initial class.
> >> +///
> >> +/// @param the_klass the class (or union) to consider.
> >
> > Same here.
>
> Fixed.
>
> [...]
>
>
> >> +decl_base_sptr
> >> +look_through_decl_only(const decl_base& d)
> >> +{
> >> +  decl_base_sptr decl;
> >> +  if (d.get_is_declaration_only())
> >> +    decl = d.get_definition_of_declaration();
> >> +
> >> +  if (!decl)
> >> +    return decl;
> >> +
> >> +  while (decl
> >> +        && decl->get_is_declaration_only()
> >> +        && decl->get_definition_of_declaration())
> >> +    decl = decl->get_definition_of_declaration();
> >> +
> >> +  ABG_ASSERT(decl);
> >> +  return decl;
> >
> > I had a suspicion that this code could be simplified a while ago.
> >
> > You should be able to simplify it to (something like):
> >
> > look_through_decl_only_class(class_or_union_sptr klass)
> > {
> >   if (!klass)
> >     return klass;
> >   while (klass->get_is_declaration_only()
> >          && klass->get_definition_of_declaration())
> >     klass = klass->get_definition_of_declaration();
> >   return klass;
> > }
> >
> > This is two things:
> > 1. The function is simpler if it takes a (shared) pointer argument.
> > You probably don't need the other overload.
>
> Actually, I prefer keeping the function that takes reference.  It's
> reused in the overload that takes a pointer.  Also, I have some
> test/play programs on the side that use types instantiated on the stack
> etc and that make use of these.  So they are useful in general, I think.
>
> > 2. You don't need to retest decl on every loop or assert it at the
> > end, just check once at the top as the loop condition preserves the
> > invariant.
>
> Yes, you are correct.  I have made this change in the updated patch.
> Thanks.
>
> [...]
>
> [...]
>
> >> +/// Look into a given corpus to find the enum type*s* that have a
> >> +/// given qualified name.
> >> +///
> >> +/// @param qualified_name the qualified name of the type to look for.
> >> +///
> >> +/// @param corp the corpus to look into.
> >> +///
> >> +/// @return the vector of enum types that which name is @p qualified_name.
> >
> > Suggest: "that which name is" -> "named" (or "which have the name")
> > (and again, below)
>
> Of course.  Fixed.
>
> [...]
>
> Please find below an updated patch that is present in my updated
> dodji/incomp-enum branch.
>
> Thanks.
>
>
> From 377b577cbbdc313e78b58631f5fb9bcc2c913a25 Mon Sep 17 00:00:00 2001
> From: Giuliano Procida <gprocida@google.com>
> Date: Wed, 10 Jun 2020 12:59:35 +0100
> Subject: [PATCH 1/4] Support incomplete enums in core and diff code.
>
> This is an initial implementation of the support for incomplete, also
> known as forward-declared, enum types. I've not made any attempt to
> refactor or share logic with the struct/union code.
>
>         * include/abg-comp-filter.h (has_decl_only_def_change) : Declare
>         New function.
>         * src/abg-comp-filter.cc (there_is_a_decl_only_enum): Define new
>         static function and ...
>         (type_size_changed): ... use it here.
>         (has_decl_only_def_change): Define new function and ...
>         (categorize_harm{less, ful}_diff_node): ... use it here.
>         * include/abg-fwd.h (enums_type, decl_base_wptr): Declare new
>         typedefs.
>         (look_through_decl_only_class): Declare new overload for
>         class_or_union*.
>         (is_compatible_with_enum_type, is_compatible_with_enum_type)
>         (look_through_decl_only, lookup_enum_types, lookup_enum_types):
>         Declare new functions.
>         * include/abg-ir.h (decl_base::{get_is_declaration_only,
>         set_is_declaration_only, set_definition_of_declaration,
>         get_definition_of_declaration,
>         get_naked_definition_of_declaration}): Declare new member
>         functions.  They were moved here from the class_or_union class.
>         (class_or_union::{get_earlier_declaration,
>         set_earlier_declaration, get_definition_of_declaration,
>         set_definition_of_declaration,
>         get_naked_definition_of_declaration, get_is_declaration_only,
>         set_is_declaration_only}): Remove these member functions.
>         * src/abg-ir.cc (decl_base::priv::{declaration_,
>         definition_of_declaration_, naked_definition_of_declaration_,
>         is_declaration_only_}): Define data members.  Moved here from
>         class_or_union.
>         (decl_base::priv::priv): Adjust to initialize the new data
>         members.
>         (decl_base::{get_earlier_declaration, set_earlier_declaration,
>         get_definition_of_declaration,
>         get_naked_definition_of_declaration, get_is_declaration_only,
>         set_is_declaration_only, set_definition_of_declaration}): Define
>         member functions.
>         (operator|): In the overload for (change_kind, change_kind),
>         adjust the return type of the call to
>         decl_base::get_definition_of_declaration.
>         (look_through_decl_only): Define new function.
>         (look_through_decl_only_class): Adjust.
>         (look_through_decl_only_enum): Likewise.
>         (maybe_update_types_lookup_map<class_decl>): Adjust return type of
>         call to decl_base::get_definition_of_declaration.
>         (types_defined_same_linux_kernel_corpus_public): Use
>         look_through_decl_only_class rather than open coding it.
>         (class_or_union::priv::{declaration_, definition_of_declaration_,
>         naked_definition_of_declaration_, is_declaration_only_}): Remove
>         these data members.  They are now carried by decl_base::priv.
>         (class_or_union::{g,s}et_alignment_in_bits): Adjust.
>         (class_or_union::{g,s}et_size_in_bits): Likewise.
>         (class_or_union::operator==): Likewise.
>         (equals): Adjust the overload for class_or_union.
>         (is_compatible_with_enum_type)
>         * src/abg-comparison.cc (try_to_diff<class_decl>): Adjust the
>         return type of decl_base::get_definition_of_declaration.
>         (leaf_diff_node_marker_visitor::visit_begin): Use
>         filtering::has_decl_only_def_change rather than
>         filtering::has_class_decl_only_def_change.  Decl-only changes to
>         enums (or any other type really) will thus not be recorded as leaf
>         changes.
>         * src/abg-dwarf-reader.cc (get_scope_for_die): Adjust return type
>         of decl_base::get_definition_of_declaration.
>         * src/abg-default-reporter.cc (default_reporter::report): Report
>         enum decl-only <-> definition changes.
>         * src/abg-hash.cc (class_or_union::hash::operator()): In the
>         overload for class_or_union& adjust the return type for
>         decl_base::get_definition_of_declaration.
>
> Signed-off-by: Giuliano Procida <gprocida@google.com>
> Signed-off-by: Dodji Seketeli <dodji@redhat.com>
> ---
>  include/abg-comp-filter.h   |  14 ++
>  include/abg-fwd.h           |  36 ++++
>  include/abg-ir.h            |  48 ++---
>  src/abg-comp-filter.cc      | 133 ++++++++++++-
>  src/abg-comparison.cc       |  10 +-
>  src/abg-default-reporter.cc |  19 ++
>  src/abg-dwarf-reader.cc     |   3 +-
>  src/abg-hash.cc             |   3 +-
>  src/abg-ir.cc               | 472 +++++++++++++++++++++++++++-----------------
>  9 files changed, 519 insertions(+), 219 deletions(-)
>
> diff --git a/include/abg-comp-filter.h b/include/abg-comp-filter.h
> index 8113003..556bad4 100644
> --- a/include/abg-comp-filter.h
> +++ b/include/abg-comp-filter.h
> @@ -65,13 +65,27 @@ bool
>  is_decl_only_class_with_size_change(const diff *diff);
>
>  bool
> +has_decl_only_def_change(const decl_base_sptr& first,
> +                        const decl_base_sptr& second);
> +
> +bool
> +has_decl_only_def_change(const diff *d);
> +
> +bool
>  has_class_decl_only_def_change(const class_or_union_sptr& first,
>                                const class_or_union_sptr& second);
>
>  bool
> +has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
> +                             const enum_type_decl_sptr& second);
> +
> +bool
>  has_class_decl_only_def_change(const diff *diff);
>
>  bool
> +has_enum_decl_only_def_change(const diff *diff);
> +
> +bool
>  has_basic_type_name_change(const diff *);
>
>  bool
> diff --git a/include/abg-fwd.h b/include/abg-fwd.h
> index f23f4a4..bfd9f88 100644
> --- a/include/abg-fwd.h
> +++ b/include/abg-fwd.h
> @@ -155,6 +155,12 @@ class enum_type_decl;
>  /// Convenience typedef for shared pointer to a @ref enum_type_decl.
>  typedef shared_ptr<enum_type_decl> enum_type_decl_sptr;
>
> +/// Convenience typedef for a vector of @ref enum_type_decl_sptr
> +typedef vector<enum_type_decl_sptr> enums_type;
> +
> +/// Convenience typedef for a weak pointer to a @ref decl_base.
> +typedef weak_ptr<decl_base> decl_base_wptr;
> +
>  class class_or_union;
>
>  typedef shared_ptr<class_or_union> class_or_union_sptr;
> @@ -424,6 +430,12 @@ typedef_decl*
>  is_typedef(type_base*);
>
>  enum_type_decl_sptr
> +is_compatible_with_enum_type(const type_base_sptr&);
> +
> +enum_type_decl_sptr
> +is_compatible_with_enum_type(const decl_base_sptr&);
> +
> +enum_type_decl_sptr
>  is_enum_type(const type_or_decl_base_sptr&);
>
>  const enum_type_decl*
> @@ -513,6 +525,24 @@ look_through_decl_only_class(const class_or_union&);
>  class_or_union_sptr
>  look_through_decl_only_class(class_or_union_sptr);
>
> +class_or_union*
> +look_through_decl_only_class(class_or_union*);
> +
> +enum_type_decl_sptr
> +look_through_decl_only_enum(const enum_type_decl&);
> +
> +enum_type_decl_sptr
> +look_through_decl_only_enum(enum_type_decl_sptr);
> +
> +decl_base_sptr
> +look_through_decl_only(const decl_base&);
> +
> +decl_base*
> +look_through_decl_only(decl_base*);
> +
> +decl_base_sptr
> +look_through_decl_only(const decl_base_sptr&);
> +
>  var_decl*
>  is_var_decl(const type_or_decl_base*);
>
> @@ -1085,6 +1115,12 @@ lookup_enum_type(const string&, const corpus&);
>  enum_type_decl_sptr
>  lookup_enum_type(const interned_string&, const corpus&);
>
> +const type_base_wptrs_type*
> +lookup_enum_types(const interned_string&, const corpus&);
> +
> +const type_base_wptrs_type*
> +lookup_enum_types(const string&, const corpus&);
> +
>  enum_type_decl_sptr
>  lookup_enum_type_per_location(const interned_string&, const corpus&);
>
> diff --git a/include/abg-ir.h b/include/abg-ir.h
> index c2b66c4..5b13dc9 100644
> --- a/include/abg-ir.h
> +++ b/include/abg-ir.h
> @@ -1553,6 +1553,27 @@ public:
>    void
>    set_visibility(visibility v);
>
> +  const decl_base_sptr
> +  get_earlier_declaration() const;
> +
> +  void
> +  set_earlier_declaration(const decl_base_sptr&);
> +
> +  const decl_base_sptr
> +  get_definition_of_declaration() const;
> +
> +  void
> +  set_definition_of_declaration(const decl_base_sptr&);
> +
> +  const decl_base*
> +  get_naked_definition_of_declaration() const;
> +
> +  bool
> +  get_is_declaration_only() const;
> +
> +  void
> +  set_is_declaration_only(bool f);
> +
>    friend type_base_sptr
>    canonicalize(type_base_sptr);
>
> @@ -3776,27 +3797,6 @@ public:
>    void
>    set_naming_typedef(const typedef_decl_sptr&);
>
> -  bool
> -  get_is_declaration_only() const;
> -
> -  void
> -  set_is_declaration_only(bool f);
> -
> -  void
> -  set_definition_of_declaration(class_or_union_sptr);
> -
> -  const class_or_union_sptr
> -  get_definition_of_declaration() const;
> -
> -  const class_or_union*
> -  get_naked_definition_of_declaration() const;
> -
> -  decl_base_sptr
> -  get_earlier_declaration() const;
> -
> -  void
> -  set_earlier_declaration(decl_base_sptr declaration);
> -
>   void
>    insert_member_type(type_base_sptr t,
>                      declarations::iterator before);
> @@ -4020,12 +4020,6 @@ public:
>    class_decl(const environment* env, const string& name, bool is_struct,
>              bool is_declaration_only = true);
>
> -  const class_decl_sptr
> -  get_definition_of_declaration() const;
> -
> -  const class_decl*
> -  get_naked_definition_of_declaration() const;
> -
>    virtual string
>    get_pretty_representation(bool internal = false,
>                             bool qualified_name = true) const;
> diff --git a/src/abg-comp-filter.cc b/src/abg-comp-filter.cc
> index 702d223..0b0fbe4 100644
> --- a/src/abg-comp-filter.cc
> +++ b/src/abg-comp-filter.cc
> @@ -118,6 +118,25 @@ there_is_a_decl_only_class(const class_decl_sptr& class1,
>    return false;
>  }
>
> +/// Test if there is a enum that is declaration-only among the two
> +/// enums in parameter.
> +///
> +/// @param enum1 the first enum to consider.
> +///
> +/// @param enum2 the second enum to consider.
> +///
> +/// @return true if either enums are declaration-only, false
> +/// otherwise.
> +static bool
> +there_is_a_decl_only_enum(const enum_type_decl_sptr& enum1,
> +                         const enum_type_decl_sptr& enum2)
> +{
> +  if ((enum1 && enum1->get_is_declaration_only())
> +      || (enum2 && enum2->get_is_declaration_only()))
> +    return true;
> +  return false;
> +}
> +
>  /// Test if the diff involves a declaration-only class.
>  ///
>  /// @param diff the class diff to consider.
> @@ -146,7 +165,9 @@ type_size_changed(const type_base_sptr f, const type_base_sptr s)
>        || f->get_size_in_bits() == 0
>        || s->get_size_in_bits() == 0
>        || there_is_a_decl_only_class(is_compatible_with_class_type(f),
> -                                   is_compatible_with_class_type(s)))
> +                                   is_compatible_with_class_type(s))
> +      || there_is_a_decl_only_enum(is_compatible_with_enum_type(f),
> +                                  is_compatible_with_enum_type(s)))
>      return false;
>
>    return f->get_size_in_bits() != s->get_size_in_bits();
> @@ -893,10 +914,8 @@ is_decl_only_class_with_size_change(const class_or_union_sptr& first,
>    if (!first || !second)
>      return false;
>
> -  class_or_union_sptr f =
> -    look_through_decl_only_class(first);
> -  class_or_union_sptr s =
> -    look_through_decl_only_class(second);
> +  class_or_union_sptr f = look_through_decl_only_class(first);
> +  class_or_union_sptr s = look_through_decl_only_class(second);
>
>    return is_decl_only_class_with_size_change(*f, *s);
>  }
> @@ -929,6 +948,57 @@ is_decl_only_class_with_size_change(const diff *diff)
>    return is_decl_only_class_with_size_change(f, s);
>  }
>
> +/// Test if two @ref decl_base_sptr are different just by the
> +/// fact that one is decl-only and the other one is defined.
> +///
> +/// @param first the first decl to consider.
> +///
> +/// @param second the second decl to consider.
> +///
> +/// @return true iff the two arguments are different just by the fact
> +/// that one is decl-only and the other one is defined.
> +bool
> +has_decl_only_def_change(const decl_base_sptr& first,
> +                        const decl_base_sptr& second)
> +{
> +  if (!first || !second)
> +    return false;
> +
> +  decl_base_sptr f =
> +    look_through_decl_only(first);
> +  decl_base_sptr s =
> +    look_through_decl_only(second);
> +
> +  if (f->get_qualified_name() != s->get_qualified_name())
> +    return false;
> +
> +  return f->get_is_declaration_only() != s->get_is_declaration_only();
> +}
> +
> +/// Test if a diff carries a change in which the two decls are
> +/// different by the fact that one is a decl-only and the other one is
> +/// defined.
> +///
> +/// @param diff the diff node to consider.
> +///
> +/// @return true if the diff carries a change in which the two decls
> +/// are different by the fact that one is a decl-only and the other
> +/// one is defined.
> +bool
> +has_decl_only_def_change(const diff *d)
> +{
> +  if (!d)
> +    return false;
> +
> +  decl_base_sptr f =
> +    look_through_decl_only(is_decl(d->first_subject()));
> +  decl_base_sptr s =
> +    look_through_decl_only(is_decl(d->second_subject()));
> +
> +  return has_decl_only_def_change(f, s);
> +}
> +
> +
>  /// Test if two @ref class_or_union_sptr are different just by the
>  /// fact that one is decl-only and the other one is defined.
>  ///
> @@ -953,7 +1023,32 @@ has_class_decl_only_def_change(const class_or_union_sptr& first,
>    if (f->get_qualified_name() != s->get_qualified_name())
>      return false;
>
> -  return (f->get_is_declaration_only() != s->get_is_declaration_only());
> +  return f->get_is_declaration_only() != s->get_is_declaration_only();
> +}
> +
> +/// Test if two @ref enum_sptr are different just by the
> +/// fact that one is decl-only and the other one is defined.
> +///
> +/// @param first the first enum to consider.
> +///
> +/// @param second the second enum to consider.
> +///
> +/// @return true iff the two arguments are different just by the fact
> +/// that one is decl-only and the other one is defined.
> +bool
> +has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
> +                             const enum_type_decl_sptr& second)
> +{
> +  if (!first || !second)
> +    return false;
> +
> +  enum_type_decl_sptr f = look_through_decl_only_enum(first);
> +  enum_type_decl_sptr s = look_through_decl_only_enum(second);
> +
> +  if (f->get_qualified_name() != s->get_qualified_name())
> +    return false;
> +
> +  return f->get_is_declaration_only() != s->get_is_declaration_only();
>  }
>
>  /// Test if a class_or_union_diff carries a change in which the two
> @@ -980,6 +1075,28 @@ has_class_decl_only_def_change(const diff *diff)
>    return has_class_decl_only_def_change(f, s);
>  }
>
> +/// Test if a enum_diff carries a change in which the two enums are
> +/// different by the fact that one is a decl-only and the other one is
> +/// defined.
> +///
> +/// @param diff the diff node to consider.
> +///
> +/// @return true if the enum_diff carries a change in which the two
> +/// enums are different by the fact that one is a decl-only and the
> +/// other one is defined.
> +bool
> +has_enum_decl_only_def_change(const diff *diff)
> +{
> +  const enum_diff *d = dynamic_cast<const enum_diff*>(diff);
> +  if (!d)
> +    return false;
> +
> +  enum_type_decl_sptr f = look_through_decl_only_enum(d->first_enum());
> +  enum_type_decl_sptr s = look_through_decl_only_enum(d->second_enum());
> +
> +  return has_enum_decl_only_def_change(f, s);
> +}
> +
>  /// Test if a diff node carries a basic type name change.
>  ///
>  /// @param d the diff node to consider.
> @@ -1517,7 +1634,8 @@ categorize_harmless_diff_node(diff *d, bool pre)
>        decl_base_sptr f = is_decl(d->first_subject()),
>         s = is_decl(d->second_subject());
>
> -      if (has_class_decl_only_def_change(d))
> +      if (has_class_decl_only_def_change(d)
> +         || has_enum_decl_only_def_change(d))
>         category |= TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY;
>
>        if (access_changed(f, s))
> @@ -1608,6 +1726,7 @@ categorize_harmful_diff_node(diff *d, bool pre)
>        //
>        // TODO: be more specific -- not all size changes are harmful.
>        if (!has_class_decl_only_def_change(d)
> +         && !has_enum_decl_only_def_change(d)
>           && (type_size_changed(f, s)
>               || data_member_offset_changed(f, s)
>               || non_static_data_member_type_size_changed(f, s)
> diff --git a/src/abg-comparison.cc b/src/abg-comparison.cc
> index d0f1d21..b6f065f 100644
> --- a/src/abg-comparison.cc
> +++ b/src/abg-comparison.cc
> @@ -2827,13 +2827,15 @@ try_to_diff<class_decl>(const type_or_decl_base_sptr first,
>
>        if (f->get_is_declaration_only())
>         {
> -         class_decl_sptr f2 = f->get_definition_of_declaration();
> +         class_decl_sptr f2 =
> +           is_class_type (f->get_definition_of_declaration());
>           if (f2)
>             f = f2;
>         }
>        if (s->get_is_declaration_only())
>         {
> -         class_decl_sptr s2 = s->get_definition_of_declaration();
> +         class_decl_sptr s2 =
> +           is_class_type(s->get_definition_of_declaration());
>           if (s2)
>             s = s2;
>         }
> @@ -10953,8 +10955,8 @@ struct leaf_diff_node_marker_visitor : public diff_node_visitor
>         // typedef change which underlying type is an anonymous
>         // struct/union.
>         && !is_anonymous_class_or_union_diff(d)
> -       // Don't show decl-only-ness changes of classes either.
> -       && !filtering::has_class_decl_only_def_change(d)
> +       // Don't show decl-only-ness changes either.
> +       && !filtering::has_decl_only_def_change(d)
>         // Sometime, we can encounter artifacts of bogus DWARF that
>         // yield a diff node for a decl-only class (and empty class
>         // with the is_declaration flag set) that carries a non-zero
> diff --git a/src/abg-default-reporter.cc b/src/abg-default-reporter.cc
> index 2acb695..cbf8c2b 100644
> --- a/src/abg-default-reporter.cc
> +++ b/src/abg-default-reporter.cc
> @@ -99,6 +99,25 @@ default_reporter::report(const enum_diff& d, ostream& out,
>    enum_type_decl_sptr first = d.first_enum(), second = d.second_enum();
>
>    const diff_context_sptr& ctxt = d.context();
> +
> +  // Report enum decl-only <-> definition changes.
> +  if (ctxt->get_allowed_category() & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY)
> +    if (filtering::has_enum_decl_only_def_change(first, second))
> +      {
> +       string was =
> +         first->get_is_declaration_only()
> +         ? " was a declaration-only enum type"
> +         : " was a defined enum type";
> +
> +       string is_now =
> +         second->get_is_declaration_only()
> +         ? " and is now a declaration-only enum type"
> +         : " and is now a defined enum type";
> +
> +       out << indent << "enum type " << name << was << is_now << "\n";
> +       return;
> +      }
> +
>    report_name_size_and_alignment_changes(first, second, ctxt,
>                                          out, indent);
>    maybe_report_diff_for_member(first, second, ctxt, out, indent);
> diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc
> index ba4e750..db43aa7 100644
> --- a/src/abg-dwarf-reader.cc
> +++ b/src/abg-dwarf-reader.cc
> @@ -12315,7 +12315,8 @@ get_scope_for_die(read_context& ctxt,
>    class_decl_sptr cl = dynamic_pointer_cast<class_decl>(d);
>    if (cl && cl->get_is_declaration_only())
>      {
> -      scope_decl_sptr scop (cl->get_definition_of_declaration());
> +      scope_decl_sptr scop  =
> +       dynamic_pointer_cast<scope_decl>(cl->get_definition_of_declaration());
>        if (scop)
>         s = scop;
>        else
> diff --git a/src/abg-hash.cc b/src/abg-hash.cc
> index c1cdc57..5d2861f 100644
> --- a/src/abg-hash.cc
> +++ b/src/abg-hash.cc
> @@ -635,7 +635,8 @@ class_or_union::hash::operator()(const class_or_union& t) const
>    if (t.get_is_declaration_only())
>      {
>        ABG_ASSERT(t.get_definition_of_declaration());
> -      size_t v = operator()(*t.get_definition_of_declaration());
> +      size_t v = operator()
> +       (*is_class_or_union_type(t.get_definition_of_declaration()));
>        return v;
>      }
>
> diff --git a/src/abg-ir.cc b/src/abg-ir.cc
> index a434ec6..15bf124 100644
> --- a/src/abg-ir.cc
> +++ b/src/abg-ir.cc
> @@ -3416,6 +3416,10 @@ struct decl_base::priv
>      interned_string    scoped_name_;
>    interned_string      linkage_name_;
>    visibility           visibility_;
> +  decl_base_sptr       declaration_;
> +  decl_base_wptr       definition_of_declaration_;
> +  decl_base*           naked_definition_of_declaration_;
> +  bool                 is_declaration_only_;
>
>    priv()
>      : in_pub_sym_tab_(false),
> @@ -3423,7 +3427,9 @@ struct decl_base::priv
>        is_artificial_(false),
>        has_anonymous_parent_(false),
>        context_(),
> -      visibility_(VISIBILITY_DEFAULT)
> +      visibility_(VISIBILITY_DEFAULT),
> +      naked_definition_of_declaration_(),
> +      is_declaration_only_(false)
>    {}
>
>    priv(interned_string name, const location& locus,
> @@ -3434,7 +3440,9 @@ struct decl_base::priv
>        name_(name),
>        qualified_name_(name),
>        linkage_name_(linkage_name),
> -      visibility_(vis)
> +      visibility_(vis),
> +      naked_definition_of_declaration_(),
> +      is_declaration_only_(false)
>    {
>      is_anonymous_ = name_.empty();
>      has_anonymous_parent_ = false;
> @@ -3446,7 +3454,9 @@ struct decl_base::priv
>        has_anonymous_parent_(false),
>        location_(l),
>        context_(),
> -      visibility_(VISIBILITY_DEFAULT)
> +      visibility_(VISIBILITY_DEFAULT),
> +      naked_definition_of_declaration_(),
> +      is_declaration_only_(false)
>    {}
>
>    ~priv()
> @@ -3881,6 +3891,80 @@ const interned_string&
>  decl_base::get_scoped_name() const
>  {return priv_->scoped_name_;}
>
> +/// If this @ref decl_base is a definition, get its earlier
> +/// declaration.
> +///
> +/// @return the earlier declaration of the class, if any.
> +const decl_base_sptr
> +decl_base::get_earlier_declaration() const
> +{return priv_->declaration_;}
> +
> +/// set the earlier declaration of this @ref decl_base definition.
> +///
> +/// @param d the earlier declaration to set.  Note that it's set only
> +/// if it's a pure declaration.
> +void
> +decl_base::set_earlier_declaration(const decl_base_sptr& d)
> +{
> +  if (d && d->get_is_declaration_only())
> +    priv_->declaration_ = d;
> +}
> +
> +
> +/// If this @ref decl_base is declaration-only, get its definition, if
> +/// any.
> +///
> +/// @return the definition of this decl-only @ref decl_base.
> +const decl_base_sptr
> +decl_base::get_definition_of_declaration() const
> +{return priv_->definition_of_declaration_.lock();}
> +
> +///  If this @ref decl_base is declaration-only, get its definition,
> +///  if any.
> +///
> +/// Note that this function doesn't return a smart pointer, but rather
> +/// the underlying pointer managed by the smart pointer.  So it's as
> +/// fast as possible.  This getter is to be used in code paths that
> +/// are proven to be performance hot spots; especially, when comparing
> +/// sensitive types like enums, classes or unions.  Those are compared
> +/// extremely frequently and thus, their access to the definition of
> +/// declaration must be fast.
> +///
> +/// @return the definition of the declaration.
> +const decl_base*
> +decl_base::get_naked_definition_of_declaration() const
> +{return priv_->naked_definition_of_declaration_;}
> +
> +/// Test if a @ref decl_base is a declaration-only decl.
> +///
> +/// @return true iff the current @ref decl_base is declaration-only.
> +bool
> +decl_base::get_is_declaration_only() const
> +{return priv_->is_declaration_only_;}
> +
> +/// Set a flag saying if the @ref enum_type_decl is a declaration-only
> +/// @ref enum_type_decl.
> +///
> +/// @param f true if the @ref enum_type_decl is a declaration-only
> +/// @ref enum_type_decl.
> +void
> +decl_base::set_is_declaration_only(bool f)
> +{
> +  bool update_types_lookup_map = !f && priv_->is_declaration_only_;
> +
> +  priv_->is_declaration_only_ = f;
> +
> +  if (update_types_lookup_map)
> +    if (scope_decl* s = get_scope())
> +      {
> +       scope_decl::declarations::iterator i;
> +       if (s->find_iterator_for_member(this, i))
> +         maybe_update_types_lookup_map(*i);
> +       else
> +         ABG_ASSERT_NOT_REACHED;
> +      }
> +}
> +
>  change_kind
>  operator|(change_kind l, change_kind r)
>  {
> @@ -4236,7 +4320,7 @@ operator!=(const type_base_sptr& l, const type_base_sptr& r)
>
>  /// Tests if a declaration has got a scope.
>  ///
> -/// @param d the decalaration to consider.
> +/// @param d the declaration to consider.
>  ///
>  /// @return true if the declaration has got a scope, false otherwise.
>  bool
> @@ -4245,7 +4329,7 @@ has_scope(const decl_base& d)
>
>  /// Tests if a declaration has got a scope.
>  ///
> -/// @param d the decalaration to consider.
> +/// @param d the declaration to consider.
>  ///
>  /// @return true if the declaration has got a scope, false otherwise.
>  bool
> @@ -6686,7 +6770,7 @@ get_location(const decl_base_sptr& decl)
>        if (class_or_union_sptr c = is_class_or_union_type(decl))
>         if (c->get_is_declaration_only() && c->get_definition_of_declaration())
>           {
> -           c = c->get_definition_of_declaration();
> +           c = is_class_or_union_type(c->get_definition_of_declaration());
>             loc = c->get_location();
>           }
>      }
> @@ -7925,6 +8009,40 @@ typedef_decl*
>  is_typedef(type_base* t)
>  {return dynamic_cast<typedef_decl*>(t);}
>
> +/// Test if a type is an enum. This function looks through typedefs.
> +///
> +/// @parm t the type to consider.
> +///
> +/// @return the enum_decl if @p t is an @ref enum_decl or null
> +/// otherwise.
> +enum_type_decl_sptr
> +is_compatible_with_enum_type(const type_base_sptr& t)
> +{
> +  if (!t)
> +    return enum_type_decl_sptr();
> +
> +  // Normally we should strip typedefs entirely, but this is
> +  // potentially costly, especially on binaries with huge changesets
> +  // like the Linux Kernel.  So we just get the leaf types for now.
> +  //
> +  // Maybe there should be an option by which users accepts to pay the
> +  // CPU usage toll in exchange for finer filtering?
> +
> +  // type_base_sptr ty = strip_typedef(t);
> +  type_base_sptr ty = peel_typedef_type(t);;
> +  return is_enum_type(ty);
> +}
> +
> +/// Test if a type is an enum. This function looks through typedefs.
> +///
> +/// @parm t the type to consider.
> +///
> +/// @return the enum_decl if @p t is an @ref enum_decl or null
> +/// otherwise.
> +enum_type_decl_sptr
> +is_compatible_with_enum_type(const decl_base_sptr& t)
> +{return is_compatible_with_enum_type(is_type(t));}
> +
>  /// Test if a decl is an enum_type_decl
>  ///
>  /// @param d the decl to test for.
> @@ -8267,27 +8385,22 @@ is_method_type(type_or_decl_base* t)
>  /// If a class (or union) is a decl-only class, get its definition.
>  /// Otherwise, just return the initial class.
>  ///
> -/// @param the_klass the class (or union) to consider.
> +/// @param the_class the class (or union) to consider.
> +///
> +/// @return either the definition of the class, or the class itself.
> +class_or_union*
> +look_through_decl_only_class(class_or_union* the_class)
> +{return is_class_or_union_type(look_through_decl_only(the_class));}
> +
> +/// If a class (or union) is a decl-only class, get its definition.
> +/// Otherwise, just return the initial class.
> +///
> +/// @param the_class the class (or union) to consider.
>  ///
>  /// @return either the definition of the class, or the class itself.
>  class_or_union_sptr
>  look_through_decl_only_class(const class_or_union& the_class)
> -{
> -  class_or_union_sptr klass;
> -  if (the_class.get_is_declaration_only())
> -    klass = the_class.get_definition_of_declaration();
> -
> -  if (!klass)
> -    return klass;
> -
> -  while (klass
> -        && klass->get_is_declaration_only()
> -        && klass->get_definition_of_declaration())
> -    klass = klass->get_definition_of_declaration();
> -
> -  ABG_ASSERT(klass);
> -  return klass;
> -}
> +{return is_class_or_union_type(look_through_decl_only(the_class));}
>
>  /// If a class (or union) is a decl-only class, get its definition.
>  /// Otherwise, just return the initial class.
> @@ -8297,13 +8410,84 @@ look_through_decl_only_class(const class_or_union& the_class)
>  /// @return either the definition of the class, or the class itself.
>  class_or_union_sptr
>  look_through_decl_only_class(class_or_union_sptr klass)
> +{return is_class_or_union_type(look_through_decl_only(klass));}
> +
> +/// If an enum is a decl-only enum, get its definition.
> +/// Otherwise, just return the initial enum.
> +///
> +/// @param the_enum the enum to consider.
> +///
> +/// @return either the definition of the enum, or the enum itself.
> +enum_type_decl_sptr
> +look_through_decl_only_enum(const enum_type_decl& the_enum)
> +{return is_enum_type(look_through_decl_only(the_enum));}
> +
> +/// If an enum is a decl-only enum, get its definition.
> +/// Otherwise, just return the initial enum.
> +///
> +/// @param enom the enum to consider.
> +///
> +/// @return either the definition of the enum, or the enum itself.
> +enum_type_decl_sptr
> +look_through_decl_only_enum(enum_type_decl_sptr enom)
> +{return is_enum_type(look_through_decl_only(enom));}
> +
> +/// If a decl is decl-only get its definition.  Otherwise, just return nil.
> +///
> +/// @param d the decl to consider.
> +///
> +/// @return either the definition of the decl, or nil.
> +decl_base_sptr
> +look_through_decl_only(const decl_base& d)
> +{
> +  decl_base_sptr decl;
> +  if (d.get_is_declaration_only())
> +    decl = d.get_definition_of_declaration();
> +
> +  if (!decl)
> +    return decl;
> +
> +  while (decl->get_is_declaration_only()
> +        && decl->get_definition_of_declaration())
> +    decl = decl->get_definition_of_declaration();
> +
> +  ABG_ASSERT(decl);

The assert is still here (and redundant - to be honest, the compiler
may be able to work this out for itself).

I didn't spot anything else, so all good.

Regards,
Giuliano.

> +  return decl;
> +}
> +
> +/// If a decl is decl-only enum, get its definition.  Otherwise, just
> +/// return the initial decl.
> +///
> +/// @param d the decl to consider.
> +///
> +/// @return either the definition of the enum, or the decl itself.
> +decl_base*
> +look_through_decl_only(decl_base* d)
> +{
> +  if (!d)
> +    return d;
> +
> +  decl_base* result = look_through_decl_only(*d).get();
> +  if (!result)
> +    result = d;
> +
> +  return result;
> +}
> +
> +/// If a decl is decl-only get its definition.  Otherwise, just return nil.
> +///
> +/// @param d the decl to consider.
> +///
> +/// @return either the definition of the decl, or nil.
> +decl_base_sptr
> +look_through_decl_only(const decl_base_sptr& d)
>  {
> -  if (!klass)
> -    return klass;
> +  if (!d)
> +    return d;
>
> -  class_or_union_sptr result = look_through_decl_only_class(*klass);
> +  decl_base_sptr result = look_through_decl_only(*d);
>    if (!result)
> -    result = klass;
> +    result = d;
>
>    return result;
>  }
> @@ -8784,7 +8968,7 @@ lookup_union_type_per_location(const string& loc, const corpus& corp)
>    return lookup_union_type_per_location(env->intern(loc), corp);
>  }
>
> -/// Lookup a enum type from a translation unit.
> +/// Lookup an enum type from a translation unit.
>  ///
>  /// This is done by looking the type up in the type map that is
>  /// maintained in the translation unit.  So this is as fast as
> @@ -8802,7 +8986,7 @@ lookup_enum_type(const interned_string& type_name, const translation_unit& tu)
>                                             tu.get_types().enum_types());
>  }
>
> -/// Lookup a enum type from a translation unit.
> +/// Lookup an enum type from a translation unit.
>  ///
>  /// This is done by looking the type up in the type map that is
>  /// maintained in the translation unit.  So this is as fast as
> @@ -10004,7 +10188,7 @@ lookup_class_type(const interned_string& qualified_name, const corpus& corp)
>  ///
>  /// @param corp the corpus to look into.
>  ///
> -/// @return the vector of class types that which name is @p qualified_name.
> +/// @return the vector of class types named @p qualified_name.
>  const type_base_wptrs_type *
>  lookup_class_types(const interned_string& qualified_name, const corpus& corp)
>  {
> @@ -10103,7 +10287,7 @@ lookup_union_type(const string& type_name, const corpus& corp)
>    return lookup_union_type(s, corp);
>  }
>
> -/// Look into a given corpus to find a enum type which has the same
> +/// Look into a given corpus to find an enum type which has the same
>  /// qualified name as a given enum type.
>  ///
>  /// If the per-corpus type map is non-empty (because the corpus allows
> @@ -10166,6 +10350,37 @@ lookup_enum_type(const interned_string& qualified_name, const corpus& corp)
>    return result;
>  }
>
> +/// Look into a given corpus to find the enum type*s* that have a
> +/// given qualified name.
> +///
> +/// @param qualified_name the qualified name of the type to look for.
> +///
> +/// @param corp the corpus to look into.
> +///
> +/// @return the vector of enum types that which name is @p qualified_name.
> +const type_base_wptrs_type *
> +lookup_enum_types(const interned_string& qualified_name, const corpus& corp)
> +{
> +  const istring_type_base_wptrs_map_type& m = corp.get_types().enum_types();
> +
> +  return lookup_types_in_map(qualified_name, m);
> +}
> +
> +/// Look into a given corpus to find the enum type*s* that have a
> +/// given qualified name.
> +///
> +/// @param qualified_name the qualified name of the type to look for.
> +///
> +/// @param corp the corpus to look into.
> +///
> +/// @return the vector of enum types that which name is @p qualified_name.
> +const type_base_wptrs_type*
> +lookup_enum_types(const string& qualified_name, const corpus& corp)
> +{
> +  interned_string s = corp.get_environment()->intern(qualified_name);
> +  return lookup_enum_types(s, corp);
> +}
> +
>  /// Look up an @ref enum_type_decl from a given corpus, by its location.
>  ///
>  /// @param loc the location to consider.
> @@ -10767,7 +10982,8 @@ maybe_update_types_lookup_map<class_decl>(const class_decl_sptr& class_type,
>    bool update_qname_map = true;
>    if (type->get_is_declaration_only())
>      {
> -      if (class_decl_sptr def = class_type->get_definition_of_declaration())
> +      if (class_decl_sptr def =
> +         is_class_type(class_type->get_definition_of_declaration()))
>         type = def;
>        else
>         update_qname_map = false;
> @@ -11683,10 +11899,8 @@ types_defined_same_linux_kernel_corpus_public(const type_base& t1,
>
>    // Look through declaration-only types.  That is, get the associated
>    // definition type.
> -  if (c1 && c1->get_is_declaration_only())
> -    c1 = c1->get_definition_of_declaration().get();
> -  if (c2 && c2->get_is_declaration_only())
> -    c2 = c2->get_definition_of_declaration().get();
> +  c1 = look_through_decl_only_class(c1);
> +  c2 = look_through_decl_only_class(c2);
>
>    if (c1 && c2)
>      {
> @@ -12079,6 +12293,23 @@ canonicalize(type_base_sptr t)
>    return canonical;
>  }
>
> +
> +/// Set the definition of this declaration-only @ref decl_base.
> +///
> +/// @param d the new definition to set.
> +void
> +decl_base::set_definition_of_declaration(const decl_base_sptr& d)
> +{
> +  ABG_ASSERT(get_is_declaration_only());
> +  priv_->definition_of_declaration_ = d;
> +  priv_->definition_of_declaration_ = d;
> +  if (type_base *t = is_type(this))
> +    if (type_base_sptr canonical_type = is_type(d)->get_canonical_type())
> +      t->priv_->canonical_type = canonical_type;
> +
> +  priv_->naked_definition_of_declaration_ = const_cast<decl_base*>(d.get());
> +}
> +
>  /// The constructor of @ref type_base.
>  ///
>  /// @param s the size of the type, in bits.
> @@ -14978,7 +15209,6 @@ class enum_type_decl::priv
>    priv();
>
>  public:
> -
>    priv(type_base_sptr underlying_type,
>         enumerators& enumerators)
>      : underlying_type_(underlying_type),
> @@ -17987,9 +18217,6 @@ function_decl::parameter::get_pretty_representation(bool internal,
>  struct class_or_union::priv
>  {
>    typedef_decl_wptr            naming_typedef_;
> -  decl_base_sptr               declaration_;
> -  class_or_union_wptr          definition_of_declaration_;
> -  class_or_union*              naked_definition_of_declaration_;
>    member_types                 member_types_;
>    data_members                 data_members_;
>    data_members                 non_static_data_members_;
> @@ -18001,21 +18228,16 @@ struct class_or_union::priv
>    string_mem_fn_ptr_map_type   signature_2_mem_fn_map_;
>    member_function_templates    member_function_templates_;
>    member_class_templates       member_class_templates_;
> -  bool                         is_declaration_only_;
>
>    priv()
> -    : naked_definition_of_declaration_(),
> -      is_declaration_only_(false)
>    {}
>
>    priv(class_or_union::member_types& mbr_types,
>         class_or_union::data_members& data_mbrs,
>         class_or_union::member_functions& mbr_fns)
> -    : naked_definition_of_declaration_(),
> -      member_types_(mbr_types),
> +    : member_types_(mbr_types),
>        data_members_(data_mbrs),
> -      member_functions_(mbr_fns),
> -      is_declaration_only_(false)
> +      member_functions_(mbr_fns)
>    {
>      for (data_members::const_iterator i = data_members_.begin();
>          i != data_members_.end();
> @@ -18024,11 +18246,6 @@ struct class_or_union::priv
>         non_static_data_members_.push_back(*i);
>    }
>
> -  priv(bool is_declaration_only)
> -    : naked_definition_of_declaration_(),
> -      is_declaration_only_(is_declaration_only)
> -  {}
> -
>    /// Mark a class or union or union as being currently compared using
>    /// the class_or_union== operator.
>    ///
> @@ -18244,8 +18461,10 @@ class_or_union::class_or_union(const environment* env, const string& name,
>      decl_base(env, name, location(), name),
>      type_base(env, 0, 0),
>      scope_type_decl(env, name, 0, 0, location()),
> -    priv_(new priv(is_declaration_only))
> -{}
> +    priv_(new priv)
> +{
> +  set_is_declaration_only(is_declaration_only);
> +}
>
>  /// This implements the ir_traversable_base::traverse pure virtual
>  /// function.
> @@ -18465,7 +18684,8 @@ size_t
>  class_or_union::get_alignment_in_bits() const
>  {
>    if (get_is_declaration_only() && get_definition_of_declaration())
> -    return get_definition_of_declaration()->get_alignment_in_bits();
> +    return is_class_or_union_type
> +      (get_definition_of_declaration())->get_alignment_in_bits();
>
>     return type_base::get_alignment_in_bits();
>  }
> @@ -18480,7 +18700,8 @@ void
>  class_or_union::set_alignment_in_bits(size_t a)
>  {
>    if (get_is_declaration_only() && get_definition_of_declaration())
> -    get_definition_of_declaration()->set_alignment_in_bits(a);
> +    is_class_or_union_type
> +      (get_definition_of_declaration()) ->set_alignment_in_bits(a);
>    else
>      type_base::set_alignment_in_bits(a);
>  }
> @@ -18495,7 +18716,8 @@ void
>  class_or_union::set_size_in_bits(size_t s)
>  {
>    if (get_is_declaration_only() && get_definition_of_declaration())
> -    get_definition_of_declaration()->set_size_in_bits(s);
> +    is_class_or_union_type
> +      (get_definition_of_declaration())->set_size_in_bits(s);
>    else
>      type_base::set_size_in_bits(s);
>  }
> @@ -18510,42 +18732,12 @@ size_t
>  class_or_union::get_size_in_bits() const
>  {
>    if (get_is_declaration_only() && get_definition_of_declaration())
> -    return get_definition_of_declaration()->get_size_in_bits();
> +    return is_class_or_union_type
> +      (get_definition_of_declaration())->get_size_in_bits();
>
>    return type_base::get_size_in_bits();
>  }
>
> -/// Test if a @ref class_or_union is a declaration-only @ref
> -/// class_or_union.
> -///
> -/// @return true iff the current @ref class_or_union is a
> -/// declaration-only @ref class_or_union.
> -bool
> -class_or_union::get_is_declaration_only() const
> -{return priv_->is_declaration_only_;}
> -
> -/// Set a flag saying if the @ref class_or_union is a declaration-only
> -/// @ref class_or_union.
> -///
> -/// @param f true if the @ref class_or_union is a decalaration-only
> -/// @ref class_or_union.
> -void
> -class_or_union::set_is_declaration_only(bool f)
> -{
> -  bool update_types_lookup_map = !f && priv_->is_declaration_only_;
> -
> -  priv_->is_declaration_only_ = f;
> -
> -  if (update_types_lookup_map)
> -    if (scope_decl* s = get_scope())
> -      {
> -       declarations::iterator i;
> -       if (s->find_iterator_for_member(this, i))
> -         maybe_update_types_lookup_map(*i);
> -       else
> -         ABG_ASSERT_NOT_REACHED;
> -      }
> -}
>
>  /// Getter for the naming typedef of the current class.
>  ///
> @@ -18577,64 +18769,6 @@ class_or_union::set_naming_typedef(const typedef_decl_sptr& typedef_type)
>    priv_->naming_typedef_ = typedef_type;
>  }
>
> -/// Set the definition of this declaration-only @ref class_or_union.
> -///
> -/// @param d the new definition to set.
> -void
> -class_or_union::set_definition_of_declaration(class_or_union_sptr d)
> -{
> -  ABG_ASSERT(get_is_declaration_only());
> -  priv_->definition_of_declaration_ = d;
> -  if (d->get_canonical_type())
> -    type_base::priv_->canonical_type = d->get_canonical_type();
> -
> -  priv_->naked_definition_of_declaration_ = d.get();
> -}
> -
> -/// If this @ref class_or_union_sptr is declaration-only, get its
> -/// definition, if any.
> -///
> -/// @return the definition of this decl-only class.
> -const class_or_union_sptr
> -class_or_union::get_definition_of_declaration() const
> -{return priv_->definition_of_declaration_.lock();}
> -
> -///  If this @ref class_or_union is declaration-only, get its
> -///  definition, if any.
> -///
> -/// Note that this function doesn't return a smart pointer, but rather
> -/// the underlying pointer managed by the smart pointer.  So it's as
> -/// fast as possible.  This getter is to be used in code paths that
> -/// are proven to be performance hot spots; especially, when comparing
> -/// sensitive types like class or unions.  Those are compared
> -/// extremely frequently and thus, their access to the definition of
> -/// declaration must be fast.
> -///
> -/// @return the definition of the class.
> -const class_or_union*
> -class_or_union::get_naked_definition_of_declaration() const
> -{return priv_->naked_definition_of_declaration_;}
> -
> -/// If this @ref class_or_union_sptr is a definitin, get its earlier
> -/// declaration.
> -///
> -/// @return the earlier declaration of the class, if any.
> -decl_base_sptr
> -class_or_union::get_earlier_declaration() const
> -{return priv_->declaration_;}
> -
> -/// set the earlier declaration of this @ref class_or_union definition.
> -///
> -/// @param declaration the earlier declaration to set.  Note that it's
> -/// set only if it's a pure declaration.
> -void
> -class_or_union::set_earlier_declaration(decl_base_sptr declaration)
> -{
> -  class_or_union_sptr cl = dynamic_pointer_cast<class_or_union>(declaration);
> -  if (cl && cl->get_is_declaration_only())
> -    priv_->declaration_ = declaration;
> -}
> -
>  /// Get the member types of this @ref class_or_union.
>  ///
>  /// @return a vector of the member types of this ref class_or_union.
> @@ -19082,15 +19216,16 @@ class_or_union::operator==(const decl_base& other) const
>    if (!canonical_type
>        && get_is_declaration_only()
>        && get_naked_definition_of_declaration())
> -    canonical_type =
> -      get_naked_definition_of_declaration()->get_naked_canonical_type();
> +    canonical_type = is_class_or_union_type
> +      (get_naked_definition_of_declaration())->get_naked_canonical_type();
>
>    // Likewise for the other class.
>    if (!other_canonical_type
>        && op->get_is_declaration_only()
>        && op->get_naked_definition_of_declaration())
>      other_canonical_type =
> -      op->get_naked_definition_of_declaration()->get_naked_canonical_type();
> +      is_class_or_union_type
> +      (op->get_naked_definition_of_declaration())->get_naked_canonical_type();
>
>    if (canonical_type && other_canonical_type)
>      return canonical_type == other_canonical_type;
> @@ -19161,11 +19296,11 @@ equals(const class_or_union& l, const class_or_union& r, change_kind* k)
>    if (l_is_decl_only || r_is_decl_only)
>      {
>        const class_or_union* def1 = l_is_decl_only
> -       ? l.get_naked_definition_of_declaration()
> +       ? is_class_or_union_type(l.get_naked_definition_of_declaration())
>         : &l;
>
>        const class_or_union* def2 = r_is_decl_only
> -       ? r.get_naked_definition_of_declaration()
> +       ? is_class_or_union_type(r.get_naked_definition_of_declaration())
>         : &r;
>
>        if (!def1 || !def2)
> @@ -19784,30 +19919,6 @@ class_decl::on_canonical_type_set()
>      sort_virtual_member_functions(i->second);
>  }
>
> -///  If this @ref class_decl is declaration-only, get its definition,
> -///  if any.
> -///
> -/// @return the definition of the class.
> -const class_decl_sptr
> -class_decl::get_definition_of_declaration() const
> -{return is_class_type(class_or_union::get_definition_of_declaration());}
> -
> -///  If this @ref class_decl is declaration-only, get its definition,
> -///  if any.
> -///
> -/// Note that this function doesn't return a smart pointer, but rather
> -/// the underlying pointer managed by the smart pointer.  So it's as
> -/// fast as possible.  This getter is to be used in code paths that
> -/// are proven to be performance hot spots; especially, when comparing
> -/// sensitive types like class or unions.  Those are compared
> -/// extremely frequently and thus, their access to the definition of
> -/// declaration must be fast.
> -///
> -/// @return the definition of the class.
> -const class_decl*
> -class_decl::get_naked_definition_of_declaration() const
> -{return is_class_type(class_or_union::get_naked_definition_of_declaration());}
> -
>  /// Set the "is-struct" flag of the class.
>  ///
>  /// @param f the new value of the flag.
> @@ -20938,14 +21049,16 @@ class_decl::operator==(const decl_base& other) const
>        && get_is_declaration_only()
>        && get_naked_definition_of_declaration())
>      canonical_type =
> -      get_naked_definition_of_declaration()->get_naked_canonical_type();
> +      is_class_type
> +      (get_naked_definition_of_declaration())->get_naked_canonical_type();
>
>    // Likewise for the other class.
>    if (!other_canonical_type
>        && op->get_is_declaration_only()
>        && op->get_naked_definition_of_declaration())
>      other_canonical_type =
> -      op->get_naked_definition_of_declaration()->get_naked_canonical_type();
> +      is_class_type
> +      (op->get_naked_definition_of_declaration())->get_naked_canonical_type();
>
>    if (canonical_type && other_canonical_type)
>      return canonical_type == other_canonical_type;
> @@ -23035,7 +23148,8 @@ hash_type(const type_base *t)
>         // The is a declaration-only class, so it has no canonical
>         // type; but then it's class definition has one.  Let's
>         // use that one.
> -       return hash_type(cl->get_naked_definition_of_declaration());
> +       return hash_type
> +         (is_class_type(cl->get_naked_definition_of_declaration()));
>        else
>         {
>           // The class really has no canonical type, let's use the
> --
> 1.8.3.1
>
>
> --
>                 Dodji
  
Dodji Seketeli July 8, 2020, 3:30 p.m. UTC | #4
Giuliano Procida <gprocida@google.com> a écrit:

[...]

>> +/// If a decl is decl-only get its definition.  Otherwise, just return nil.
>> +///
>> +/// @param d the decl to consider.
>> +///
>> +/// @return either the definition of the decl, or nil.
>> +decl_base_sptr
>> +look_through_decl_only(const decl_base& d)
>> +{
>> +  decl_base_sptr decl;
>> +  if (d.get_is_declaration_only())
>> +    decl = d.get_definition_of_declaration();
>> +
>> +  if (!decl)
>> +    return decl;
>> +
>> +  while (decl->get_is_declaration_only()
>> +        && decl->get_definition_of_declaration())
>> +    decl = decl->get_definition_of_declaration();
>> +
>> +  ABG_ASSERT(decl);
>
> The assert is still here (and redundant - to be honest, the compiler
> may be able to work this out for itself).

Pff, right.  I removed it now, thanks.

> I didn't spot anything else, so all good.

Thanks.  I've just applied the series to master then.

Cheers,
  

Patch

diff --git a/include/abg-comp-filter.h b/include/abg-comp-filter.h
index 81130033..3b845088 100644
--- a/include/abg-comp-filter.h
+++ b/include/abg-comp-filter.h
@@ -68,9 +68,16 @@  bool
 has_class_decl_only_def_change(const class_or_union_sptr& first,
 			       const class_or_union_sptr& second);
 
+bool
+has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
+			      const enum_type_decl_sptr& second);
+
 bool
 has_class_decl_only_def_change(const diff *diff);
 
+bool
+has_enum_decl_only_def_change(const diff *diff);
+
 bool
 has_basic_type_name_change(const diff *);
 
diff --git a/include/abg-fwd.h b/include/abg-fwd.h
index 999b071b..73d809ab 100644
--- a/include/abg-fwd.h
+++ b/include/abg-fwd.h
@@ -155,6 +155,12 @@  class enum_type_decl;
 /// Convenience typedef for shared pointer to a @ref enum_type_decl.
 typedef shared_ptr<enum_type_decl> enum_type_decl_sptr;
 
+/// Convenience typedef for a vector of @ref enum_type_decl_sptr
+typedef vector<enum_type_decl_sptr> enums_type;
+
+/// Convenience typedef for a weak pointer to a @ref enum_type_decl.
+typedef weak_ptr<enum_type_decl> enum_type_decl_wptr;
+
 class class_or_union;
 
 typedef shared_ptr<class_or_union> class_or_union_sptr;
@@ -423,6 +429,12 @@  is_typedef(const type_base*);
 typedef_decl*
 is_typedef(type_base*);
 
+enum_type_decl_sptr
+is_compatible_with_enum_type(const type_base_sptr&);
+
+enum_type_decl_sptr
+is_compatible_with_enum_type(const decl_base_sptr&);
+
 enum_type_decl_sptr
 is_enum_type(const type_or_decl_base_sptr&);
 
@@ -513,6 +525,12 @@  look_through_decl_only_class(const class_or_union&);
 class_or_union_sptr
 look_through_decl_only_class(class_or_union_sptr);
 
+enum_type_decl_sptr
+look_through_decl_only_enum(const enum_type_decl&);
+
+enum_type_decl_sptr
+look_through_decl_only_enum(enum_type_decl_sptr);
+
 var_decl*
 is_var_decl(const type_or_decl_base*);
 
@@ -1085,6 +1103,12 @@  lookup_enum_type(const string&, const corpus&);
 enum_type_decl_sptr
 lookup_enum_type(const interned_string&, const corpus&);
 
+const type_base_wptrs_type*
+lookup_enum_types(const interned_string&, const corpus&);
+
+const type_base_wptrs_type*
+lookup_enum_types(const string&, const corpus&);
+
 enum_type_decl_sptr
 lookup_enum_type_per_location(const interned_string&, const corpus&);
 
diff --git a/include/abg-ir.h b/include/abg-ir.h
index ebcc4b81..2114e27b 100644
--- a/include/abg-ir.h
+++ b/include/abg-ir.h
@@ -2497,6 +2497,21 @@  public:
   enumerators&
   get_enumerators();
 
+  bool
+  get_is_declaration_only() const;
+
+  void
+  set_is_declaration_only(bool f);
+
+  void
+  set_definition_of_declaration(enum_type_decl_sptr);
+
+  const enum_type_decl_sptr
+  get_definition_of_declaration() const;
+
+  const enum_type_decl*
+  get_naked_definition_of_declaration() const;
+
   virtual string
   get_pretty_representation(bool internal = false,
 			    bool qualified_name = true) const;
diff --git a/src/abg-comp-filter.cc b/src/abg-comp-filter.cc
index 81548427..70858f27 100644
--- a/src/abg-comp-filter.cc
+++ b/src/abg-comp-filter.cc
@@ -118,6 +118,25 @@  there_is_a_decl_only_class(const class_decl_sptr& class1,
   return false;
 }
 
+/// Test if there is a enum that is declaration-only among the two
+/// enums in parameter.
+///
+/// @param enum1 the first enum to consider.
+///
+/// @param enum2 the second enum to consider.
+///
+/// @return true if either enums are declaration-only, false
+/// otherwise.
+static bool
+there_is_a_decl_only_enum(const enum_type_decl_sptr& enum1,
+			  const enum_type_decl_sptr& enum2)
+{
+  if ((enum1 && enum1->get_is_declaration_only())
+      || (enum2 && enum2->get_is_declaration_only()))
+    return true;
+  return false;
+}
+
 /// Test if the diff involves a declaration-only class.
 ///
 /// @param diff the class diff to consider.
@@ -146,7 +165,9 @@  type_size_changed(const type_base_sptr f, const type_base_sptr s)
       || f->get_size_in_bits() == 0
       || s->get_size_in_bits() == 0
       || there_is_a_decl_only_class(is_compatible_with_class_type(f),
-				    is_compatible_with_class_type(s)))
+				    is_compatible_with_class_type(s))
+      || there_is_a_decl_only_enum(is_compatible_with_enum_type(f),
+				   is_compatible_with_enum_type(s)))
     return false;
 
   return f->get_size_in_bits() != s->get_size_in_bits();
@@ -956,6 +977,31 @@  has_class_decl_only_def_change(const class_or_union_sptr& first,
   return (f->get_is_declaration_only() != s->get_is_declaration_only());
 }
 
+/// Test if two @ref enum_sptr are different just by the
+/// fact that one is decl-only and the other one is defined.
+///
+/// @param first the first enum to consider.
+///
+/// @param second the second enum to consider.
+///
+/// @return true iff the two arguments are different just by the fact
+/// that one is decl-only and the other one is defined.
+bool
+has_enum_decl_only_def_change(const enum_type_decl_sptr& first,
+			      const enum_type_decl_sptr& second)
+{
+  if (!first || !second)
+    return false;
+
+  enum_type_decl_sptr f = look_through_decl_only_enum(first);
+  enum_type_decl_sptr s = look_through_decl_only_enum(second);
+
+  if (f->get_qualified_name() != s->get_qualified_name())
+    return false;
+
+  return (f->get_is_declaration_only() != s->get_is_declaration_only());
+}
+
 /// Test if a class_or_union_diff carries a change in which the two
 /// classes are different by the fact that one is a decl-only and the
 /// other one is defined.
@@ -980,6 +1026,28 @@  has_class_decl_only_def_change(const diff *diff)
   return has_class_decl_only_def_change(f, s);
 }
 
+/// Test if a enum_diff carries a change in which the two enums are
+/// different by the fact that one is a decl-only and the other one is
+/// defined.
+///
+/// @param diff the diff node to consider.
+///
+/// @return true if the enum_diff carries a change in which the two
+/// enums are different by the fact that one is a decl-only and the
+/// other one is defined.
+bool
+has_enum_decl_only_def_change(const diff *diff)
+{
+  const enum_diff *d = dynamic_cast<const enum_diff*>(diff);
+  if (!d)
+    return false;
+
+  enum_type_decl_sptr f = look_through_decl_only_enum(d->first_enum());
+  enum_type_decl_sptr s = look_through_decl_only_enum(d->second_enum());
+
+  return has_enum_decl_only_def_change(f, s);
+}
+
 /// Test if a diff node carries a basic type name change.
 ///
 /// @param d the diff node to consider.
@@ -1517,7 +1585,8 @@  categorize_harmless_diff_node(diff *d, bool pre)
       decl_base_sptr f = is_decl(d->first_subject()),
 	s = is_decl(d->second_subject());
 
-      if (has_class_decl_only_def_change(d))
+      if (has_class_decl_only_def_change(d)
+	  || has_enum_decl_only_def_change(d))
 	category |= TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY;
 
       if (access_changed(f, s))
@@ -1608,6 +1677,7 @@  categorize_harmful_diff_node(diff *d, bool pre)
       //
       // TODO: be more specific -- not all size changes are harmful.
       if (!has_class_decl_only_def_change(d)
+	  && !has_enum_decl_only_def_change(d)
 	  && (type_size_changed(f, s)
 	      || data_member_offset_changed(f, s)
 	      || non_static_data_member_type_size_changed(f, s)
diff --git a/src/abg-comparison.cc b/src/abg-comparison.cc
index f77799f2..21cab0e7 100644
--- a/src/abg-comparison.cc
+++ b/src/abg-comparison.cc
@@ -10962,7 +10962,9 @@  struct leaf_diff_node_marker_visitor : public diff_node_visitor
 	// with the is_declaration flag set) that carries a non-zero
 	// size!  And of course at some point that non-zero size
 	// changes.  We need to be able to detect that.
-	&& !filtering::is_decl_only_class_with_size_change(d))
+	&& !filtering::is_decl_only_class_with_size_change(d)
+	// Don't show decl-only-ness changes of enums.
+	&& !filtering::has_enum_decl_only_def_change(d))
       {
 	diff_context_sptr ctxt = d->context();
 	const corpus_diff *corpus_diff_node = ctxt->get_corpus_diff().get();
diff --git a/src/abg-default-reporter.cc b/src/abg-default-reporter.cc
index 2acb6954..cbf8c2bc 100644
--- a/src/abg-default-reporter.cc
+++ b/src/abg-default-reporter.cc
@@ -99,6 +99,25 @@  default_reporter::report(const enum_diff& d, ostream& out,
   enum_type_decl_sptr first = d.first_enum(), second = d.second_enum();
 
   const diff_context_sptr& ctxt = d.context();
+
+  // Report enum decl-only <-> definition changes.
+  if (ctxt->get_allowed_category() & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY)
+    if (filtering::has_enum_decl_only_def_change(first, second))
+      {
+	string was =
+	  first->get_is_declaration_only()
+	  ? " was a declaration-only enum type"
+	  : " was a defined enum type";
+
+	string is_now =
+	  second->get_is_declaration_only()
+	  ? " and is now a declaration-only enum type"
+	  : " and is now a defined enum type";
+
+	out << indent << "enum type " << name << was << is_now << "\n";
+	return;
+      }
+
   report_name_size_and_alignment_changes(first, second, ctxt,
 					 out, indent);
   maybe_report_diff_for_member(first, second, ctxt, out, indent);
diff --git a/src/abg-ir.cc b/src/abg-ir.cc
index 5cc39f59..9f4890e8 100644
--- a/src/abg-ir.cc
+++ b/src/abg-ir.cc
@@ -7924,6 +7924,38 @@  typedef_decl*
 is_typedef(type_base* t)
 {return dynamic_cast<typedef_decl*>(t);}
 
+/// Test if a type is a enum. This function looks through typedefs.
+///
+/// @parm t the type to consider.
+///
+/// @return the enum_decl if @p t is a enum_decl or null otherwise.
+enum_type_decl_sptr
+is_compatible_with_enum_type(const type_base_sptr& t)
+{
+  if (!t)
+    return enum_type_decl_sptr();
+
+  // Normally we should strip typedefs entirely, but this is
+  // potentially costly, especially on binaries with huge changesets
+  // like the Linux Kernel.  So we just get the leaf types for now.
+  //
+  // Maybe there should be an option by which users accepts to pay the
+  // CPU usage toll in exchange for finer filtering?
+
+  // type_base_sptr ty = strip_typedef(t);
+  type_base_sptr ty = peel_typedef_type(t);;
+  return is_enum_type(ty);
+}
+
+/// Test if a type is a enum. This function looks through typedefs.
+///
+/// @parm t the type to consider.
+///
+/// @return the enum_decl if @p t is a enum_decl or null otherwise.
+enum_type_decl_sptr
+is_compatible_with_enum_type(const decl_base_sptr& t)
+{return is_compatible_with_enum_type(is_type(t));}
+
 /// Test if a decl is an enum_type_decl
 ///
 /// @param d the decl to test for.
@@ -8307,6 +8339,50 @@  look_through_decl_only_class(class_or_union_sptr klass)
   return result;
 }
 
+/// If a enum is a decl-only enum, get its definition.
+/// Otherwise, just return the initial enum.
+///
+/// @param the_enum the enum to consider.
+///
+/// @return either the definition of the enum, or the enum itself.
+enum_type_decl_sptr
+look_through_decl_only_enum(const enum_type_decl& the_enum)
+{
+  enum_type_decl_sptr enom;
+  if (the_enum.get_is_declaration_only())
+    enom = the_enum.get_definition_of_declaration();
+
+  if (!enom)
+    return enom;
+
+  while (enom
+	 && enom->get_is_declaration_only()
+	 && enom->get_definition_of_declaration())
+    enom = enom->get_definition_of_declaration();
+
+  ABG_ASSERT(enom);
+  return enom;
+}
+
+/// If a enum is a decl-only enum, get its definition.
+/// Otherwise, just return the initial enum.
+///
+/// @param enom the enum to consider.
+///
+/// @return either the definition of the enum, or the enum itself.
+enum_type_decl_sptr
+look_through_decl_only_enum(enum_type_decl_sptr enom)
+{
+  if (!enom)
+    return enom;
+
+  enum_type_decl_sptr result = look_through_decl_only_enum(*enom);
+  if (!result)
+    result = enom;
+
+  return result;
+}
+
 /// Tests if a declaration is a variable declaration.
 ///
 /// @param decl the decl to test.
@@ -10165,6 +10241,37 @@  lookup_enum_type(const interned_string& qualified_name, const corpus& corp)
   return result;
 }
 
+/// Look into a given corpus to find the enum type*s* that have a
+/// given qualified name.
+///
+/// @param qualified_name the qualified name of the type to look for.
+///
+/// @param corp the corpus to look into.
+///
+/// @return the vector of enum types that which name is @p qualified_name.
+const type_base_wptrs_type *
+lookup_enum_types(const interned_string& qualified_name, const corpus& corp)
+{
+  const istring_type_base_wptrs_map_type& m = corp.get_types().enum_types();
+
+  return lookup_types_in_map(qualified_name, m);
+}
+
+/// Look into a given corpus to find the enum type*s* that have a
+/// given qualified name.
+///
+/// @param qualified_name the qualified name of the type to look for.
+///
+/// @param corp the corpus to look into.
+///
+/// @return the vector of enum types that which name is @p qualified_name.
+const type_base_wptrs_type*
+lookup_enum_types(const string& qualified_name, const corpus& corp)
+{
+  interned_string s = corp.get_environment()->intern(qualified_name);
+  return lookup_enum_types(s, corp);
+}
+
 /// Look up an @ref enum_type_decl from a given corpus, by its location.
 ///
 /// @param loc the location to consider.
@@ -14974,17 +15081,26 @@  class enum_type_decl::priv
 {
   type_base_sptr	underlying_type_;
   enumerators		enumerators_;
+  decl_base_sptr       	declaration_;
+  enum_type_decl_wptr	definition_of_declaration_;
+  enum_type_decl*	naked_definition_of_declaration_;
+  bool			is_declaration_only_;
 
   friend class enum_type_decl;
 
-  priv();
-
 public:
 
+  priv()
+    : naked_definition_of_declaration_(),
+      is_declaration_only_(true)
+  {}
+
   priv(type_base_sptr underlying_type,
        enumerators& enumerators)
     : underlying_type_(underlying_type),
-      enumerators_(enumerators)
+      enumerators_(enumerators),
+      naked_definition_of_declaration_(),
+      is_declaration_only_(false)
   {}
 }; // end class enum_type_decl::priv
 
@@ -15040,6 +15156,80 @@  enum_type_decl::enumerators&
 enum_type_decl::get_enumerators()
 {return priv_->enumerators_;}
 
+/// Set the definition of this declaration-only @ref enum_type_decl.
+///
+/// @param d the new definition to set.
+void
+enum_type_decl::set_definition_of_declaration(enum_type_decl_sptr d)
+{
+  ABG_ASSERT(get_is_declaration_only());
+  priv_->definition_of_declaration_ = d;
+  if (d->get_canonical_type())
+    type_base::priv_->canonical_type = d->get_canonical_type();
+
+  priv_->naked_definition_of_declaration_ = d.get();
+}
+
+/// If this @ref enum_type_decl_sptr is declaration-only, get its
+/// definition, if any.
+///
+/// @return the definition of this decl-only enum.
+const enum_type_decl_sptr
+enum_type_decl::get_definition_of_declaration() const
+{
+  if (priv_->definition_of_declaration_.expired())
+    return enum_type_decl_sptr();
+  return enum_type_decl_sptr(priv_->definition_of_declaration_);
+}
+
+///  If this @ref enum_type_decl is declaration-only, get its
+///  definition, if any.
+///
+/// Note that this function doesn't return a smart pointer, but rather
+/// the underlying pointer managed by the smart pointer.  So it's as
+/// fast as possible.  This getter is to be used in code paths that are
+/// proven to be performance hot spots; especially, when comparing
+/// sensitive types like enums.  Those are compared extremely frequently
+/// and thus, their access to the definition of declaration must be
+/// fast.
+///
+/// @return the definition of the enum.
+const enum_type_decl*
+enum_type_decl::get_naked_definition_of_declaration() const
+{return priv_->naked_definition_of_declaration_;}
+
+/// Test if a @ref enum_type_decl is a declaration-only @ref
+/// enum_type_decl.
+///
+/// @return true iff the current @ref enum_type_decl is a
+/// declaration-only @ref enum_type_decl.
+bool
+enum_type_decl::get_is_declaration_only() const
+{return priv_->is_declaration_only_;}
+
+/// Set a flag saying if the @ref enum_type_decl is a declaration-only
+/// @ref enum_type_decl.
+///
+/// @param f true if the @ref enum_type_decl is a decalaration-only
+/// @ref enum_type_decl.
+void
+enum_type_decl::set_is_declaration_only(bool f)
+{
+  bool update_types_lookup_map = !f && priv_->is_declaration_only_;
+
+  priv_->is_declaration_only_ = f;
+
+  if (update_types_lookup_map)
+    if (scope_decl* s = get_scope())
+      {
+	scope_decl::declarations::iterator i;
+	if (s->find_iterator_for_member(this, i))
+	  maybe_update_types_lookup_map(*i);
+	else
+	  ABG_ASSERT_NOT_REACHED;
+      }
+}
+
 /// Get the pretty representation of the current instance of @ref
 /// enum_type_decl.
 ///