text: // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  // -*- Mode: C++ -*-
  //
  // Copyright (C) 2013-2020 Red Hat, Inc.
  //
  // Author: Dodji Seketeli
  
  /// @file
  ///
  /// This contains the implementation of the comparison engine of
  /// libabigail.
  
  #include <ctype.h>
  #include <libgen.h>
  #include <algorithm>
  #include <sstream>
  
  #include "abg-comparison-priv.h"
  #include "abg-reporter-priv.h"
  
  namespace abigail
  {
  
  namespace comparison
  {
  
  ///
  ///
  ///@defgroup DiffNode Internal Representation of the comparison engine
  /// @{
  ///
  /// @brief How changes are represented in libabigail's comparison engine.
  ///
  ///@par diff nodes
  ///
  /// The internal representation of the comparison engine is basically
  /// a graph of @ref instances of @ref diff node.  We refer to these
  /// just as <em>diff nodes</em>.  A diff node represents a change
  /// between two ABI artifacts represented by instances of types of the
  /// abigail::ir namespace.  These two artifacts that are being
  /// compared are called the <em>subjects of the diff</em>.
  ///
  /// The types of that IR are in the abigail::comparison namespace.
  ///
  ///@par comparing diff nodes
  ///
  /// Comparing two instances of @ref diff nodes amounts to comparing
  /// the subject of the diff.  In other words, two @ref diff nodes are
  /// equal if and only if their subjects are equal.  Thus, two @ref
  /// diff nodes can have different memory addresses and yet be equal.
  ///
  ///@par diff reporting and context
  ///
  /// A diff node can be serialized to an output stream to express, in
  /// a human-readable textual form, the different changes that exist
  /// between its two subjects.  This is done by invoking the
  /// diff::report() method.  That reporting is controlled by several
  /// parameters that are conceptually part of the context of the diff.
  /// That context is materialized by an instance of the @ref
  /// diff_context type.
  ///
  /// Please note that the role of the instance(s) of @ref diff_context
  /// is boreader than just controlling the reporting of @ref diff
  /// nodes.  Basically, a @ref diff node itself is created following
  /// behaviours that are controlled by a particular instance of
  /// diff_context.  A diff node is created in a particular diff
  /// context, so to speak.
  ///
  /// @}
  ///
  
  ///
  ///@defgroup CanonicalDiff Canonical diff tree nodes
  /// @{
  ///
  /// @brief How equivalent diff nodes are quickly spotted.
  ///
  /// @par Equivalence of diff nodes.
  ///
  /// Each @ref diff node has a property named <em>Canonical Diff
  /// Node</em>.  If \c D is a diff node, the canonical diff node of @c
  /// D, noted @c C(D) is a particular diff node that is equal to @c D.
  /// Thus, a fast way to compare two @ref diff node is to perform a
  /// pointer comparison of their canonical diff nodes.
  ///
  /// A set of equivalent @ref diff nodes is a set of diff nodes that
  /// all have the same canonical node.  All the nodes of that set are
  /// equal.
  ///
  /// A canonical node is registereded for a given diff node by invoking
  /// the method diff_context::initialize_canonical_diff().
  ///
  /// Please note that the diff_context holds all the canonical diffs
  /// that got registered through it.  Thus, the life time of all of
  /// canonical diff objects is the same as the life time of the @ref
  /// diff_context they relate to.
  ///
  /// @}
  ///
  
  // -----------------------------------------
  // <private functions re-usable elsewhere>
  // -----------------------------------------
  /// Sort a map of enumerators by their value.
  ///
  /// @param enumerators_map the map to sort.
  ///
  /// @param sorted the resulting vector of sorted enumerators.
  void
  sort_enumerators(const string_enumerator_map& enumerators_map,
  		 enum_type_decl::enumerators& sorted)
  {
    for (string_enumerator_map::const_iterator i = enumerators_map.begin();
         i != enumerators_map.end();
         ++i)
      sorted.push_back(i->second);
    enumerator_value_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort a map of changed enumerators.
  ///
  /// @param enumerators_map the map to sort.
  ///
  ///@param output parameter.  The resulting sorted enumerators.
  void
  sort_changed_enumerators(const string_changed_enumerator_map& enumerators_map,
  			 changed_enumerators_type& sorted)
  {
    for (string_changed_enumerator_map::const_iterator i =
  	 enumerators_map.begin();
         i != enumerators_map.end();
         ++i)
      sorted.push_back(i->second);
  
    changed_enumerator_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort a map of data members by the offset of their initial value.
  ///
  /// @param data_members the map of changed data members to sort.
  ///
  /// @param sorted the resulting vector of sorted changed data members.
  void
  sort_data_members(const string_decl_base_sptr_map &data_members,
  		  vector<decl_base_sptr>& sorted)
  {
    sorted.reserve(data_members.size());
    for (string_decl_base_sptr_map::const_iterator i = data_members.begin();
         i != data_members.end();
         ++i)
      sorted.push_back(i->second);
  
    data_member_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort (in place) a vector of changed data members.
  ///
  /// @param to_sort the vector to sort.
  void
  sort_changed_data_members(changed_var_sptrs_type& to_sort)
  {
    data_member_comp comp;
    std::sort(to_sort.begin(), to_sort.end(), comp);
  }
  
  /// Sort an instance of @ref string_function_ptr_map map and stuff a
  /// resulting sorted vector of pointers to function_decl.
  ///
  /// @param map the map to sort.
  ///
  /// @param sorted the resulting sorted vector.
  void
  sort_string_function_ptr_map(const string_function_ptr_map& map,
  			     vector<function_decl*>& sorted)
  {
    sorted.reserve(map.size());
    for (string_function_ptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
  
    function_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort a map that's an instance of @ref
  /// string_member_function_sptr_map and fill a vector of member
  /// functions with the sorted result.
  ///
  /// @param map the map to sort.
  ///
  /// @param sorted the resulting sorted vector.
  void
  sort_string_member_function_sptr_map(const string_member_function_sptr_map& map,
  				     class_or_union::member_functions& sorted)
  {
    sorted.reserve(map.size());
    for (string_member_function_sptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
  
    function_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort the values of a @ref string_function_decl_diff_sptr_map map
  /// and store the result in a vector of @ref function_decl_diff_sptr
  /// objects.
  ///
  /// @param map the map whose values to store.
  ///
  /// @param sorted the vector of function_decl_diff_sptr to store the
  /// result of the sort into.
  void
  sort_string_function_decl_diff_sptr_map
  (const string_function_decl_diff_sptr_map& map,
   function_decl_diff_sptrs_type& sorted)
  {
    sorted.reserve(map.size());
    for (string_function_decl_diff_sptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
    function_decl_diff_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort of an instance of @ref string_var_diff_sptr_map map.
  ///
  /// @param map the input map to sort.
  ///
  /// @param sorted the ouptut sorted vector of @ref var_diff_sptr.
  /// It's populated with the sorted content.
  void
  sort_string_var_diff_sptr_map(const string_var_diff_sptr_map& map,
  			      var_diff_sptrs_type& sorted)
  {
    sorted.reserve(map.size());
    for (string_var_diff_sptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
  
    var_diff_sptr_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort a map of string -> pointer to @ref elf_symbol.
  ///
  /// The result is a vector of @ref elf_symbol_sptr sorted by the
  /// name of the symbol.
  ///
  /// @param map the map to sort.
  ///
  /// @param sorted out parameter; the sorted vector of @ref
  /// elf_symbol_sptr.
  void
  sort_string_elf_symbol_map(const string_elf_symbol_map& map,
  			   vector<elf_symbol_sptr>& sorted)
  {
    for (string_elf_symbol_map::const_iterator i = map.begin();
         i!= map.end();
         ++i)
      sorted.push_back(i->second);
  
    elf_symbol_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort a map of string -> pointer to @ref var_decl.
  ///
  /// The result is a vector of var_decl* sorted by the qualified name
  /// of the variables.
  ///
  /// @param map the map to sort.
  ///
  /// @param sorted out parameter; the sorted vector of @ref var_decl.
  void
  sort_string_var_ptr_map(const string_var_ptr_map& map,
  			vector<var_decl*>& sorted)
  {
    for (string_var_ptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
  
    var_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort the values of a string_var_diff_sptr_map and store the result
  /// in a vector of var_diff_sptr.
  ///
  /// @param map the map of changed data members to sort.
  ///
  /// @param sorted the resulting vector of var_diff_sptr.
  void
  sort_string_data_member_diff_sptr_map(const string_var_diff_sptr_map& map,
  				      var_diff_sptrs_type& sorted)
  {
    sorted.reserve(map.size());
    for (string_var_diff_sptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
    data_member_diff_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort the values of a unsigned_var_diff_sptr_map map and store the
  /// result into a vector of var_diff_sptr.
  ///
  /// @param map the map of changed data members to sort.
  ///
  /// @param sorted the resulting vector of sorted var_diff_sptr.
  void
  sort_unsigned_data_member_diff_sptr_map(const unsigned_var_diff_sptr_map map,
  					var_diff_sptrs_type& sorted)
  {
    sorted.reserve(map.size());
    for (unsigned_var_diff_sptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
    data_member_diff_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort an map of string -> virtual member function into a vector of
  /// virtual member functions.  The virtual member functions are sorted
  /// by increasing order of their virtual index.
  ///
  /// @param map the input map.
  ///
  /// @param sorted the resulting sorted vector of virtual function
  /// member.
  void
  sort_string_virtual_member_function_diff_sptr_map
  (const string_function_decl_diff_sptr_map& map,
   function_decl_diff_sptrs_type& sorted)
  {
    sorted.reserve(map.size());
    for (string_function_decl_diff_sptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
  
    virtual_member_function_diff_comp comp;
    sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort a map ofg string -> @ref diff_sptr into a vector of @ref
  /// diff_sptr.  The diff_sptr are sorted lexicographically wrt
  /// qualified names of their first subjects.
  ///
  /// @param map the map to sort.
  ///
  /// @param sorted the resulting sorted vector.
  void
  sort_string_diff_sptr_map(const string_diff_sptr_map& map,
  			  diff_sptrs_type& sorted)
  {
    sorted.reserve(map.size());
    for (string_diff_sptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
  
    diff_comp comp;
    sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort a map ofg string -> @ref diff* into a vector of @ref
  /// diff_ptr.  The diff_ptr are sorted lexicographically wrt
  /// qualified names of their first subjects.
  ///
  /// @param map the map to sort.
  ///
  /// @param sorted the resulting sorted vector.
  void
  sort_string_diff_ptr_map(const string_diff_ptr_map& map,
  			  diff_ptrs_type& sorted)
  {
    sorted.reserve(map.size());
    for (string_diff_ptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
  
    diff_comp comp;
    sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort a map of string -> base_diff_sptr into a sorted vector of
  /// base_diff_sptr.  The base_diff_sptr are sorted by increasing value
  /// of their offset in their containing type.
  ///
  /// @param map the input map to sort.
  ///
  /// @param sorted the resulting sorted vector.
  void
  sort_string_base_diff_sptr_map(const string_base_diff_sptr_map& map,
  			       base_diff_sptrs_type& sorted)
  {
    for (string_base_diff_sptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
    base_diff_comp comp;
    sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Lexicographically sort base specifications found
  /// in instances of string_base_sptr_map.
  void
  sort_string_base_sptr_map(const string_base_sptr_map& m,
  			  class_decl::base_specs& sorted)
  {
    for (string_base_sptr_map::const_iterator i = m.begin();
         i != m.end();
         ++i)
      sorted.push_back(i->second);
  
    base_spec_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort a map of @ref fn_parm_diff by the indexes of the function
  /// parameters.
  ///
  /// @param map the map to sort.
  ///
  /// @param sorted the resulting sorted vector of changed function
  /// parms.
  void
  sort_string_fn_parm_diff_sptr_map(const unsigned_fn_parm_diff_sptr_map& map,
  				  vector<fn_parm_diff_sptr>&		sorted)
  {
    sorted.reserve(map.size());
    for (unsigned_fn_parm_diff_sptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
  
    fn_parm_diff_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort a map of changed function parameters by the indexes of the
  /// function parameters.
  ///
  /// @param map the map to sort.
  ///
  /// @param sorted the resulting sorted vector of instances of @ref
  /// fn_parm_diff_sptr
  void
  sort_string_fn_parm_diff_sptr_map(const string_fn_parm_diff_sptr_map&	map,
  				  vector<fn_parm_diff_sptr>&		sorted)
  {
    sorted.reserve(map.size());
    for (string_fn_parm_diff_sptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
  
    fn_parm_diff_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort a map of string -> function parameters.
  ///
  /// @param map the map to sort.
  ///
  /// @param sorted the resulting sorted vector of
  /// @ref vector<function_decl::parameter_sptr>
  void
  sort_string_parm_map(const string_parm_map& map,
  		     vector<function_decl::parameter_sptr>& sorted)
  {
    for (string_parm_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
  
    parm_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort the set of ABI artifacts contained in a @ref
  /// artifact_sptr_set_type.
  ///
  /// @param set the set of ABI artifacts to sort.
  ///
  /// @param output parameter the vector containing the sorted ABI
  /// artifacts.
  void
  sort_artifacts_set(const artifact_sptr_set_type& set,
  		   vector<type_or_decl_base_sptr>& sorted)
  {
  
    for (artifact_sptr_set_type::const_iterator it = set.begin();
         it != set.end();
         ++it)
      sorted.push_back(*it);
  
    type_or_decl_base_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Sort a map of string to type_base_sptr entities.
  ///
  /// The entries are sorted based on the lexicographic order of the
  /// pretty representation of the type_sptr_sptr.  The sorted result is
  /// put in a vector of type_base_sptr.
  ///
  /// @param map the map to sort.
  ///
  /// @param sorted the resulting vector of type_base_sptr
  /// lexicographically sorted using their pretty representation.
  void
  sort_string_type_base_sptr_map(string_type_base_sptr_map& map,
  			       vector<type_base_sptr>& sorted)
  {
    for (string_type_base_sptr_map::const_iterator i = map.begin();
         i != map.end();
         ++i)
      sorted.push_back(i->second);
  
    type_or_decl_base_comp comp;
    std::sort(sorted.begin(), sorted.end(), comp);
  }
  
  /// Return the first underlying type that is not a qualified type.
  /// @param t the qualified type to consider.
  ///
  /// @return the first underlying type that is not a qualified type, or
  /// NULL if t is NULL.
  type_base_sptr
  get_leaf_type(qualified_type_def_sptr t)
  {
    if (!t)
      return type_base_sptr();
  
    type_base_sptr ut = t->get_underlying_type();
    qualified_type_def_sptr qut = dynamic_pointer_cast<qualified_type_def>(ut);
  
    if (!qut)
      return ut;
    return get_leaf_type(qut);
  }
  
  /// Tests if a given diff node is to represent the changes between two
  /// gobal decls.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return true iff @p d represents the changes between two global
  /// decls.
  bool
  is_diff_of_global_decls(const diff* d)
  {
    ABG_ASSERT(d != 0);
  
    if (d == 0)
      return false;
  
    type_or_decl_base_sptr first = d->first_subject();
    ABG_ASSERT(first);
  
    type_or_decl_base_sptr second = d->first_subject();
    ABG_ASSERT(second);
  
    if (decl_base_sptr decl = is_decl(first))
      if (is_at_global_scope(decl))
        if ((decl = is_decl(second)))
  	if (is_at_global_scope(decl))
  	  return true;
  
    return false;
  }
  
  // -----------------------------------------
  // </private functions re-usable elsewhere>
  // -----------------------------------------
  
  /// The overloaded or operator for @ref visiting_kind.
  visiting_kind
  operator|(visiting_kind l, visiting_kind r)
  {return static_cast<visiting_kind>(static_cast<unsigned>(l)
  				   | static_cast<unsigned>(r));}
  
  /// The overloaded and operator for @ref visiting_kind.
  visiting_kind
  operator&(visiting_kind l, visiting_kind r)
  {
    return static_cast<visiting_kind>(static_cast<unsigned>(l)
  				    & static_cast<unsigned>(r));
  }
  
  /// The overloaded 'bit inversion' operator for @ref visiting_kind.
  visiting_kind
  operator~(visiting_kind l)
  {return static_cast<visiting_kind>(~static_cast<unsigned>(l));}
  
  /// Test if a diff node is about differences between types.
  ///
  /// @param diff the diff node to test.
  ///
  /// @return a pointer to the actual type_diff_base* that @p diff
  /// extends, iff it is about differences between types.
  const type_diff_base*
  is_type_diff(const diff* diff)
  {return dynamic_cast<const type_diff_base*>(diff);}
  
  /// Test if a diff node is about differences between declarations.
  ///
  /// @param diff the diff node to test.
  ///
  /// @return a pointer to the actual decl_diff_base @p diff extends,
  /// iff it is about differences between declarations.
  const decl_diff_base*
  is_decl_diff(const diff* diff)
  {return dynamic_cast<const decl_diff_base*>(diff);}
  
  /// Test if a diff node is a @ref class_diff node.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return a non-nil pointer to a @ref class_diff iff @p diff is a
  /// @ref class_diff node.
  const class_diff*
  is_class_diff(const diff* diff)
  {return dynamic_cast<const class_diff*>(diff);}
  
  /// Test if a diff node is a @ref enum_diff node.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return a non-nil pointer to ad @ref enum_diff node iff @p diff is
  /// a @ref enum_diff node.
  const enum_diff*
  is_enum_diff(const diff *diff)
  {return dynamic_cast<const enum_diff*>(diff);}
  
  /// Test if a diff node is a @ref union_diff node.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return a non-nil pointer to a @ref union_diff iff @p diff is a
  /// @ref union_diff node.
  const union_diff*
  is_union_diff(const diff* diff)
  {return dynamic_cast<const union_diff*>(diff);}
  
  /// Test if a diff node is a @ref class_or_union_diff node.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return a non-nil pointer to the @ref class_or_union_diff denoted
  /// by @p d iff @p d is a @ref class_or_union_diff.
  const class_or_union_diff*
  is_class_or_union_diff(const diff* d)
  {return dynamic_cast<const class_or_union_diff*>(d);}
  
  /// Test if a diff node is a @ref class_or_union_diff between two
  /// anonymous classes or unions.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return a non-nil pointer to the @ref class_or_union_diff iff @p
  /// denoted by @p d iff @p is pointer to an anonymous class or union
  /// diff.
  const class_or_union_diff*
  is_anonymous_class_or_union_diff(const diff* d)
  {
    if (const class_or_union_diff *dif = is_class_or_union_diff(d))
      if (dif->first_class_or_union()->get_is_anonymous())
        return dif;
    return 0;
  }
  
  /// Test if a diff node is a @ref typedef_diff node.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return a non-nil pointer to a @ref typedef_diff iff @p diff is a
  /// @ref typedef_diff node.
  const typedef_diff*
  is_typedef_diff(const diff *diff)
  {return dynamic_cast<const typedef_diff*>(diff);}
  
  /// Test if a diff node is a @ref array_diff node.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return a non-nil pointer to a @ref array_diff iff @p diff is a
  /// @ref array_diff node.
  const array_diff*
  is_array_diff(const diff* diff)
  {return dynamic_cast<const array_diff*>(diff);}
  
  /// Test if a diff node is a @ref function_type_diff node.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return a non-nil pointer to a @ref function_type_diff iff @p diff is a
  /// @ref function_type_diff node.
  const function_type_diff*
  is_function_type_diff(const diff* diff)
  {return dynamic_cast<const function_type_diff*>(diff);}
  
  /// Test if a given diff node carries a function type change with
  /// local changes.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return a non-nil pointer to a @ref function_type_diff iff @p diff
  /// is a function_type_diff node that carries a local change.
  const function_type_diff*
  is_function_type_diff_with_local_changes(const diff* diff)
  {
    if (const function_type_diff* d = is_function_type_diff(diff))
      if (d->has_local_changes())
        return d;
  
    return 0;
  }
  
  /// Test if a diff node is about differences between variables.
  ///
  /// @param diff the diff node to test.
  ///
  /// @return a pointer to the actual var_diff that @p diff is a type
  /// of, iff it is about differences between variables.
  const var_diff*
  is_var_diff(const diff* diff)
  {
    const var_diff* d = dynamic_cast<const var_diff*>(diff);
    if (d)
      ABG_ASSERT(is_decl_diff(diff));
    return d;
  }
  
  /// Test if a diff node is about differences between functions.
  ///
  /// @param diff the diff node to test.
  ///
  /// @return a pointer to the actual var_diff that @p diff is a type
  /// of, iff it is about differences between variables.
  const function_decl_diff*
  is_function_decl_diff(const diff* diff)
  {
    const function_decl_diff *d = dynamic_cast<const function_decl_diff*>(diff);
    if (d)
      ABG_ASSERT(is_decl_diff(diff));
    return d;
  }
  
  /// Test if a diff node is about differences between two pointers.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return the @p diff converted into an instance of @ref
  /// pointer_diff iff @p diff is about differences between two
  /// pointers.
  const pointer_diff*
  is_pointer_diff(const diff* diff)
  {return dynamic_cast<const pointer_diff*>(diff);}
  
  /// Test if a diff node is about differences between two references.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return the @p diff converted into an instance of @ref
  /// reference_diff iff @p diff is about differences between two
  /// references.
  const reference_diff*
  is_reference_diff(const diff* diff)
  {return dynamic_cast<const reference_diff*>(diff);}
  
  /// Test if a diff node is about differences between two qualified
  /// types.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return @p diff converted into an instance of @ref
  /// qualified_type_diff iff @p diff is about differences between two
  /// qualified types.
  const qualified_type_diff*
  is_qualified_type_diff(const diff* diff)
  {return dynamic_cast<const qualified_type_diff*>(diff);}
  
  /// Test if a diff node is a reference or pointer diff node to a
  /// change that is neither basic type change nor distinct type change.
  ///
  /// Note that this function also works on diffs of typedefs of
  /// reference or pointer.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return true iff @p diff is a eference or pointer diff node to a
  /// change that is neither basic type change nor distinct type change.
  bool
  is_reference_or_ptr_diff_to_non_basic_nor_distinct_types(const diff* diff)
  {
    diff = peel_typedef_diff(diff);
    if (const reference_diff* d = is_reference_diff(diff))
      {
        diff = peel_reference_diff(d);
        if (is_diff_of_basic_type(diff) || is_distinct_diff(diff))
  	return false;
        return true;
      }
    else if (const pointer_diff *d = is_pointer_diff(diff))
      {
        diff = peel_pointer_diff(d);
        if (is_diff_of_basic_type(diff) || is_distinct_diff(diff))
  	return false;
        return true;
      }
  
    return false;
  }
  
  /// Test if a diff node is about differences between two function
  /// parameters.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return the @p diff converted into an instance of @ref
  /// reference_diff iff @p diff is about differences between two
  /// function parameters.
  const fn_parm_diff*
  is_fn_parm_diff(const diff* diff)
  {return dynamic_cast<const fn_parm_diff*>(diff);}
  
  /// Test if a diff node is about differences between two base class
  /// specifiers.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return the @p diff converted into an instance of @ref base_diff
  /// iff @p diff is about differences between two base class
  /// specifiers.
  const base_diff*
  is_base_diff(const diff* diff)
  {return dynamic_cast<const base_diff*>(diff);}
  
  /// Test if a diff node is about differences between two diff nodes of
  /// different kinds.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return the @p diff converted into an instance of @ref
  /// distintc_diff iff @p diff is about differences between two diff
  /// nodes of different kinds.
  const distinct_diff*
  is_distinct_diff(const diff *diff)
  {return dynamic_cast<const distinct_diff*>(diff);}
  
  /// Test if a diff node is a @ref corpus_diff node.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @return a non-nil pointer to a @ref corpus_diff iff @p diff is a
  /// @ref corpus_diff node.
  const corpus_diff*
  is_corpus_diff(const diff* diff)
  {return dynamic_cast<const corpus_diff*>(diff);}
  
  /// Test if a diff node is a child node of a function parameter diff node.
  ///
  /// @param diff the diff node to test.
  ///
  /// @return true iff @p diff is a child node of a function parameter
  /// diff node.
  bool
  is_child_node_of_function_parm_diff(const diff* diff)
  {return diff && is_fn_parm_diff(diff->parent_node());}
  
  /// Test if a diff node is a child node of a base diff node.
  ///
  /// @param diff the diff node to test.
  ///
  /// @return true iff @p diff is a child node of a base diff node.
  bool
  is_child_node_of_base_diff(const diff* diff)
  {return diff && is_base_diff(diff->parent_node());}
  
  /// The default traverse function.
  ///
  /// @return true.
  bool
  diff_traversable_base::traverse(diff_node_visitor&)
  {return true;}
  
  diff_context::diff_context()
    : priv_(new diff_context::priv)
  {
    // Setup all the diff output filters we have.
    filtering::filter_base_sptr f;
  
    f.reset(new filtering::harmless_harmful_filter);
    add_diff_filter(f);
  
    // f.reset(new filtering::harmless_filter);
    // add_diff_filter(f);
  
    // f.reset(new filtering::harmful_filter);
    // add_diff_filter(f);
  }
  
  /// Set the corpus diff relevant to this context.
  ///
  /// @param d the corpus_diff we are interested in.
  void
  diff_context::set_corpus_diff(const corpus_diff_sptr& d)
  {priv_->corpus_diff_ = d;}
  
  /// Get the corpus diff for the current context.
  ///
  /// @return the corpus diff of this context.
  const corpus_diff_sptr&
  diff_context::get_corpus_diff() const
  {return priv_->corpus_diff_;}
  
  /// Getter for the first corpus of the corpus diff of the current context.
  ///
  /// @return the first corpus of the corpus diff of the current
  /// context, if no corpus diff is associated to the context.
  corpus_sptr
  diff_context::get_first_corpus() const
  {
    if (priv_->corpus_diff_)
      return priv_->corpus_diff_->first_corpus();
    return corpus_sptr();
  }
  
  /// Getter for the second corpus of the corpus diff of the current
  /// context.
  ///
  /// @return the second corpus of the corpus diff of the current
  /// context, if no corpus diff is associated to the context.
  corpus_sptr
  diff_context::get_second_corpus() const
  {
    if (priv_->corpus_diff_)
      return priv_->corpus_diff_->second_corpus();
    return corpus_sptr();
  }
  
  /// Getter of the reporter to be used in this context.
  ///
  /// @return the reporter to be used in this context.
  reporter_base_sptr
  diff_context::get_reporter() const
  {
    if (!priv_->reporter_)
      {
        if (show_leaf_changes_only())
  	priv_->reporter_.reset(new leaf_reporter);
        else
  	priv_->reporter_.reset(new default_reporter);
      }
    ABG_ASSERT(priv_->reporter_);
    return priv_->reporter_;
  }
  
  /// Setter of the reporter to be used in this context.
  ///
  /// @param r the reporter to be used in this context.
  void
  diff_context::set_reporter(reporter_base_sptr& r)
  {priv_->reporter_ = r;}
  
  /// Tests if the current diff context already has a diff for two decls.
  ///
  /// @param first the first decl to consider.
  ///
  /// @param second the second decl to consider.
  ///
  /// @return a pointer to the diff for @p first @p second if found,
  /// null otherwise.
  diff_sptr
  diff_context::has_diff_for(const type_or_decl_base_sptr first,
  			   const type_or_decl_base_sptr second) const
  {
    types_or_decls_diff_map_type::const_iterator i =
      priv_->types_or_decls_diff_map.find(std::make_pair(first, second));
    if (i != priv_->types_or_decls_diff_map.end())
      return i->second;
    return diff_sptr();
  }
  
  /// Tests if the current diff context already has a diff for two types.
  ///
  /// @param first the first type to consider.
  ///
  /// @param second the second type to consider.
  ///
  /// @return a pointer to the diff for @p first @p second if found,
  /// null otherwise.
  diff_sptr
  diff_context::has_diff_for_types(const type_base_sptr first,
  				  const type_base_sptr second) const
  {return has_diff_for(first, second);}
  
  /// Tests if the current diff context already has a given diff.
  ///
  ///@param d the diff to consider.
  ///
  /// @return a pointer to the diff found for @p d
  const diff*
  diff_context::has_diff_for(const diff* d) const
  {return has_diff_for(d->first_subject(), d->second_subject()).get();}
  
  /// Tests if the current diff context already has a given diff.
  ///
  ///@param d the diff to consider.
  ///
  /// @return a pointer to the diff found for @p d
  diff_sptr
  diff_context::has_diff_for(const diff_sptr d) const
  {return has_diff_for(d->first_subject(), d->second_subject());}
  
  /// Getter for the bitmap that represents the set of categories that
  /// the user wants to see reported.
  ///
  /// @return a bitmap that represents the set of categories that the
  /// user wants to see reported.
  diff_category
  diff_context::get_allowed_category() const
  {return priv_->allowed_category_;}
  
  /// Setter for the bitmap that represents the set of categories that
  /// the user wants to see reported.
  ///
  /// @param c a bitmap that represents the set of categories that the
  /// user wants to see represented.
  void
  diff_context::set_allowed_category(diff_category c)
  {priv_->allowed_category_ = c;}
  
  /// Setter for the bitmap that represents the set of categories that
  /// the user wants to see reported
  ///
  /// This function perform a bitwise or between the new set of
  /// categories and the current ones, and then sets the current
  /// categories to the result of the or.
  ///
  /// @param c a bitmap that represents the set of categories that the
  /// user wants to see represented.
  void
  diff_context::switch_categories_on(diff_category c)
  {priv_->allowed_category_ = priv_->allowed_category_ | c;}
  
  /// Setter for the bitmap that represents the set of categories that
  /// the user wants to see reported
  ///
  /// This function actually unsets bits from the current categories.
  ///
  /// @param c a bitmap that represents the set of categories to unset
  /// from the current categories.
  void
  diff_context::switch_categories_off(diff_category c)
  {priv_->allowed_category_ = priv_->allowed_category_ & ~c;}
  
  /// Add a diff for two decls to the cache of the current diff_context.
  ///
  /// Doing this allows to later find the added diff from its two
  /// subject decls.
  ///
  /// @param first the first decl to consider.
  ///
  /// @param second the second decl to consider.
  ///
  /// @param the diff to add.
  void
  diff_context::add_diff(type_or_decl_base_sptr first,
  		       type_or_decl_base_sptr second,
  		       const diff_sptr d)
  {priv_->types_or_decls_diff_map[std::make_pair(first, second)] = d;}
  
  /// Add a diff tree node to the cache of the current diff_context
  ///
  /// @param d the diff tree node to add.
  void
  diff_context::add_diff(const diff* d)
  {
    if (d)
      {
        diff_sptr dif(const_cast<diff*>(d), noop_deleter());
        add_diff(d->first_subject(), d->second_subject(), dif);
      }
  }
  
  /// Add a diff tree node to the cache of the current diff_context
  ///
  /// @param d the diff tree node to add.
  void
  diff_context::add_diff(const diff_sptr d)
  {
    if (d)
        add_diff(d->first_subject(), d->second_subject(), d);
  }
  
  /// Getter for the @ref CanonicalDiff "canonical diff node" for the
  /// @ref diff represented by their two subjects.
  ///
  /// @param first the first subject of the diff.
  ///
  /// @param second the second subject of the diff.
  ///
  /// @return the canonical diff for the diff node represented by the
  /// two diff subjects @p first and @p second.  If no canonical diff
  /// node was registered for these subjects, then a nil node is
  /// returned.
  diff_sptr
  diff_context::get_canonical_diff_for(const type_or_decl_base_sptr first,
  				     const type_or_decl_base_sptr second) const
  {return has_diff_for(first, second);}
  
  /// Getter for the @ref CanonicalDiff "canonical diff node" for the
  /// @ref diff represented by the two subjects of a given diff node.
  ///
  /// @param d the diff node to get the canonical node for.
  ///
  /// @return the canonical diff for the diff node represented by the
  /// two diff subjects of @p d.  If no canonical diff node was
  /// registered for these subjects, then a nil node is returned.
  diff_sptr
  diff_context::get_canonical_diff_for(const diff_sptr d) const
  {return has_diff_for(d);}
  
  /// Setter for the @ref CanonicalDiff "canonical diff node" for the
  /// @ref diff represented by their two subjects.
  ///
  /// @param first the first subject of the diff.
  ///
  /// @param second the second subject of the diff.
  ///
  /// @param d the new canonical diff.
  void
  diff_context::set_canonical_diff_for(const type_or_decl_base_sptr first,
  				     const type_or_decl_base_sptr second,
  				     const diff_sptr d)
  {
    ABG_ASSERT(d);
    if (!has_diff_for(first, second))
      {
        add_diff(first, second, d);
        priv_->canonical_diffs.push_back(d);
      }
  }
  
  /// If there is is a @ref CanonicalDiff "canonical diff node"
  /// registered for two diff subjects, return it.  Otherwise, register
  /// a canonical diff node for these two diff subjects and return it.
  ///
  /// @param first the first subject of the diff.
  ///
  /// @param second the second subject of the diff.
  ///
  /// @param d the new canonical diff node.
  ///
  /// @return the canonical diff node.
  diff_sptr
  diff_context::set_or_get_canonical_diff_for(const type_or_decl_base_sptr first,
  					    const type_or_decl_base_sptr second,
  					    const diff_sptr canonical_diff)
  {
    ABG_ASSERT(canonical_diff);
  
    diff_sptr canonical = get_canonical_diff_for(first, second);
    if (!canonical)
      {
        canonical = canonical_diff;
        set_canonical_diff_for(first, second, canonical);
      }
    return canonical;
  }
  
  /// Set the canonical diff node property of a given diff node
  /// appropriately.
  ///
  /// For a given diff node that has no canonical diff node, retrieve
  /// the canonical diff node (by looking at its diff subjects and at
  /// the current context) and set the canonical diff node property of
  /// the diff node to that canonical diff node.  If no canonical diff
  /// node has been registered to the diff context for the subjects of
  /// the diff node then, register the canonical diff node as being the
  /// diff node itself; and set its canonical diff node property as
  /// such.  Otherwise, if the diff node already has a canonical diff
  /// node, do nothing.
  ///
  /// @param diff the diff node to initialize the canonical diff node
  /// property for.
  void
  diff_context::initialize_canonical_diff(const diff_sptr diff)
  {
    if (diff->get_canonical_diff() == 0)
      {
        diff_sptr canonical =
  	set_or_get_canonical_diff_for(diff->first_subject(),
  				      diff->second_subject(),
  				      diff);
        diff->set_canonical_diff(canonical.get());
      }
  }
  
  /// Add a diff node to the set of diff nodes that are kept alive for
  /// the life time of the current instance of diff_context.
  ///
  /// Note that diff added to the diff cache are kept alive as well, and
  /// don't need to be passed to this function to be kept alive.
  ///
  /// @param d the diff node to be kept alive during the life time of
  /// the current instance of @ref diff_context.
  void
  diff_context::keep_diff_alive(diff_sptr& d)
  {priv_->live_diffs_.insert(d);}
  
  /// Test if a diff node has been traversed.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return the first diff node against which @p d is redundant.
  diff*
  diff_context::diff_has_been_visited(const diff* d) const
  {
    const diff* canonical = d->get_canonical_diff();
    ABG_ASSERT(canonical);
  
    size_t ptr_value = reinterpret_cast<size_t>(canonical);
    pointer_map::iterator it = priv_->visited_diff_nodes_.find(ptr_value);
    if (it != priv_->visited_diff_nodes_.end())
      return reinterpret_cast<diff*>(it->second);
    else
      return 0;
  }
  
  /// Test if a diff node has been traversed.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return the first diff node against which @p d is redundant.
  diff_sptr
  diff_context::diff_has_been_visited(const diff_sptr d) const
  {
    diff_sptr diff(diff_has_been_visited(d.get()));
    return diff;
  }
  
  /// Mark a diff node as traversed by a traversing algorithm.
  ///
  /// Actually, it's the @ref CanonicalDiff "canonical diff" of this
  /// node that is marked as traversed.
  ///
  /// Subsequent invocations of diff_has_been_visited() on the diff node
  /// will yield true.
  void
  diff_context::mark_diff_as_visited(const diff* d)
  {
    if (diff_has_been_visited(d))
      return;
  
    const diff* canonical = d->get_canonical_diff();
    ABG_ASSERT(canonical);
  
     size_t canonical_ptr_value = reinterpret_cast<size_t>(canonical);
     size_t diff_ptr_value = reinterpret_cast<size_t>(d);
     priv_->visited_diff_nodes_[canonical_ptr_value] = diff_ptr_value;
  }
  
  /// Unmark all the diff nodes that were marked as being traversed.
  void
  diff_context::forget_visited_diffs()
  {priv_->visited_diff_nodes_.clear();}
  
  /// This sets a flag that, if it's true, then during the traversing of
  /// a diff nodes tree each node is visited at most once.
  ///
  /// @param f if true then during the traversing of a diff nodes tree
  /// each node is visited at most once.
  ///
  void
  diff_context::forbid_visiting_a_node_twice(bool f)
  {priv_->forbid_visiting_a_node_twice_ = f;}
  
  /// This function sets a flag os that if @ref
  ///  forbid_visiting_a_node_twice() returns true, then each time the
  ///  node visitor starts visiting a new interface, it resets the
  ///  memory the systems has about already visited node.
  ///
  ///  @param f the flag to set.
  void
  diff_context::forbid_visiting_a_node_twice_per_interface(bool f)
  {priv_->reset_visited_diffs_for_each_interface_ = f;}
  
  /// Return a flag that, if true, then during the traversing of a diff
  /// nodes tree each node is visited at most once.
  ///
  /// @return the boolean flag.
  bool
  diff_context::visiting_a_node_twice_is_forbidden() const
  {return priv_->forbid_visiting_a_node_twice_;}
  
  /// Return a flag that, if true, then during the traversing of a diff
  /// nodes tree each node is visited at most once, while visiting the
  /// diff tree underneath a given interface (public function or
  /// variable).  Each time a new interface is visited, the nodes
  /// visited while visiting previous interfaces can be visited again.
  ///
  /// @return the boolean flag.
  ///
  /// @return the boolean flag.
  bool
  diff_context::visiting_a_node_twice_is_forbidden_per_interface() const
  {
    return (priv_->forbid_visiting_a_node_twice_
  	  && priv_->reset_visited_diffs_for_each_interface_);
  }
  
  /// Getter for the diff tree nodes filters to apply to diff sub-trees.
  ///
  /// @return the vector of tree filters to apply to diff sub-trees.
  const filtering::filters&
  diff_context::diff_filters() const
  {return priv_->filters_;}
  
  /// Setter for the diff filters to apply to a given diff sub-tree.
  ///
  /// @param f the new diff filter to add to the vector of diff filters
  /// to apply to diff sub-trees.
  void
  diff_context::add_diff_filter(filtering::filter_base_sptr f)
  {priv_->filters_.push_back(f);}
  
  /// Apply the diff filters to a given diff sub-tree.
  ///
  /// If the current context is instructed to filter out some categories
  /// then this function walks the given sub-tree and categorizes its
  /// nodes by using the filters held by the context.
  ///
  /// @param diff the diff sub-tree to apply the filters to.
  void
  diff_context::maybe_apply_filters(diff_sptr diff)
  {
    if (!diff)
      return;
  
    if (get_allowed_category() == EVERYTHING_CATEGORY)
      return;
  
    if (!diff->has_changes())
      return;
  
    for (filtering::filters::const_iterator i = diff_filters().begin();
         i != diff_filters().end();
         ++i)
      {
        filtering::apply_filter(*i, diff);
        propagate_categories(diff);
      }
  
   }
  
  /// Apply the diff filters to the diff nodes of a @ref corpus_diff
  /// instance.
  ///
  /// If the current context is instructed to filter out some categories
  /// then this function walks the diff tree and categorizes its nodes
  /// by using the filters held by the context.
  ///
  /// @param diff the corpus diff to apply the filters to.
  void
  diff_context::maybe_apply_filters(corpus_diff_sptr diff)
  {
  
    if (!diff || !diff->has_changes())
      return;
  
    for (filtering::filters::const_iterator i = diff_filters().begin();
         i != diff_filters().end();
         ++i)
      {
        filtering::apply_filter(**i, diff);
        propagate_categories(diff);
      }
  }
  
  /// Getter for the vector of suppressions that specify which diff node
  /// reports should be dropped on the floor.
  ///
  /// @return the set of suppressions.
  suppressions_type&
  diff_context::suppressions() const
  {return priv_->suppressions_;}
  
  /// Add a new suppression specification that specifies which diff node
  /// reports should be dropped on the floor.
  ///
  /// @param suppr the new suppression specification to add to the
  /// existing set of suppressions specifications of the diff context.
  void
  diff_context::add_suppression(const suppression_sptr suppr)
  {priv_->suppressions_.push_back(suppr);}
  
  /// Add new suppression specifications that specify which diff node
  /// reports should be dropped on the floor.
  ///
  /// @param supprs the new suppression specifications to add to the
  /// existing set of suppression specifications of the diff context.
  void
  diff_context::add_suppressions(const suppressions_type& supprs)
  {
    priv_->suppressions_.insert(priv_->suppressions_.end(),
  			      supprs.begin(), supprs.end());
  }
  
  /// Set the flag that indicates if the diff using this context should
  /// show only leaf changes or not.
  ///
  /// @param f the new value of the flag that indicates if the diff
  /// using this context should show only leaf changes or not.
  void
  diff_context::show_leaf_changes_only(bool f)
  {
    // This function can be called only if the reporter hasn't yet been
    // created.  Once it's been created, we are supposed to live with
    // it.
    ABG_ASSERT(priv_->reporter_ == 0);
    priv_->leaf_changes_only_ = f;
  }
  
  /// Get the flag that indicates if the diff using this context should
  /// show only leaf changes or not.
  ///
  /// @return the value of the flag that indicates if the diff using
  /// this context should show only leaf changes or not.
  bool
  diff_context::show_leaf_changes_only() const
  {return priv_->leaf_changes_only_;}
  
  /// Get the flag that indicates if the diff reports using this context
  /// should show sizes and offsets in an hexadecimal base or not.  If
  /// not, then they are to be shown in a decimal base.
  ///
  /// @return true iff sizes and offsets are to be shown in an
  /// hexadecimal base.
  bool
  diff_context::show_hex_values() const
  {return priv_->hex_values_;}
  
  /// Set the flag that indicates if diff reports using this context
  /// should show sizes and offsets in an hexadecimal base or not.  If
  /// not, then they are to be shown in a decimal base.
  ///
  /// @param f if true then sizes and offsets are to be shown in an
  /// hexadecimal base.
  void
  diff_context::show_hex_values(bool f)
  {priv_->hex_values_ = f;}
  
  /// Get the flag that indicates if diff reports using this context
  /// should show sizes and offsets in bits, rather than bytes.
  ///
  /// @return true iff sizes and offsets are to be shown in bits.
  /// Otherwise they are to be shown in bytes.
  bool
  diff_context::show_offsets_sizes_in_bits() const
  {return priv_->show_offsets_sizes_in_bits_;}
  
  /// Set the flag that indicates if diff reports using this context
  /// should show sizes and offsets in bits, rather than bytes.
  ///
  /// @param f if true then sizes and offsets are to be shown in bits.
  /// Otherwise they are to be shown in bytes.
  void
  diff_context::show_offsets_sizes_in_bits(bool f)
  {priv_->show_offsets_sizes_in_bits_ = f;}
  
  /// Set a flag saying if offset changes should be reported in a
  /// relative way.  That is, if the report should say how of many bits
  /// a class/struct data member did move.
  ///
  /// @param f the new boolean value of the flag.
  void
  diff_context::show_relative_offset_changes(bool f)
  {priv_->show_relative_offset_changes_ = f;}
  
  /// Get the flag saying if offset changes should be reported in a
  /// relative way.  That is, if the report should say how of many bits
  /// a class/struct data member did move.
  ///
  /// @return the boolean value of the flag.
  bool
  diff_context::show_relative_offset_changes(void)
  {return priv_->show_relative_offset_changes_;}
  
  /// Set a flag saying if the comparison module should only show the
  /// diff stats.
  ///
  /// @param f the flag to set.
  void
  diff_context::show_stats_only(bool f)
  {priv_->show_stats_only_ = f;}
  
  /// Test if the comparison module should only show the diff stats.
  ///
  /// @return true if the comparison module should only show the diff
  /// stats, false otherwise.
  bool
  diff_context::show_stats_only() const
  {return priv_->show_stats_only_;}
  
  /// Setter for the property that says if the comparison module should
  /// show the soname changes in its report.
  ///
  /// @param f the new value of the property.
  void
  diff_context::show_soname_change(bool f)
  {priv_->show_soname_change_ = f;}
  
  /// Getter for the property that says if the comparison module should
  /// show the soname changes in its report.
  ///
  /// @return the value of the property.
  bool
  diff_context::show_soname_change() const
  {return priv_->show_soname_change_;}
  
  /// Setter for the property that says if the comparison module should
  /// show the architecture changes in its report.
  ///
  /// @param f the new value of the property.
  void
  diff_context::show_architecture_change(bool f)
  {priv_->show_architecture_change_ = f;}
  
  /// Getter for the property that says if the comparison module should
  /// show the architecture changes in its report.
  ///
  /// @return the value of the property.
  bool
  diff_context::show_architecture_change() const
  {return priv_->show_architecture_change_;}
  
  /// Set a flag saying to show the deleted functions.
  ///
  /// @param f true to show deleted functions.
  void
  diff_context::show_deleted_fns(bool f)
  {priv_->show_deleted_fns_ = f;}
  
  /// @return true if we want to show the deleted functions, false
  /// otherwise.
  bool
  diff_context::show_deleted_fns() const
  {return priv_->show_deleted_fns_;}
  
  /// Set a flag saying to show the changed functions.
  ///
  /// @param f true to show the changed functions.
  void
  diff_context::show_changed_fns(bool f)
  {priv_->show_changed_fns_ = f;}
  
  /// @return true if we want to show the changed functions, false otherwise.
  bool
  diff_context::show_changed_fns() const
  {return priv_->show_changed_fns_;}
  
  /// Set a flag saying to show the added functions.
  ///
  /// @param f true to show the added functions.
  void
  diff_context::show_added_fns(bool f)
  {priv_->show_added_fns_ = f;}
  
  /// @return true if we want to show the added functions, false
  /// otherwise.
  bool
  diff_context::show_added_fns() const
  {return priv_->show_added_fns_;}
  
  /// Set a flag saying to show the deleted variables.
  ///
  /// @param f true to show the deleted variables.
  void
  diff_context::show_deleted_vars(bool f)
  {priv_->show_deleted_vars_ = f;}
  
  /// @return true if we want to show the deleted variables, false
  /// otherwise.
  bool
  diff_context::show_deleted_vars() const
  {return priv_->show_deleted_vars_;}
  
  /// Set a flag saying to show the changed variables.
  ///
  /// @param f true to show the changed variables.
  void
  diff_context::show_changed_vars(bool f)
  {priv_->show_changed_vars_ = f;}
  
  /// @return true if we want to show the changed variables, false otherwise.
  bool
  diff_context::show_changed_vars() const
  {return priv_->show_changed_vars_;}
  
  /// Set a flag saying to show the added variables.
  ///
  /// @param f true to show the added variables.
  void
  diff_context::show_added_vars(bool f)
  {priv_->show_added_vars_ = f;}
  
  /// @return true if we want to show the added variables, false
  /// otherwise.
  bool
  diff_context::show_added_vars() const
  {return priv_->show_added_vars_;}
  
  bool
  diff_context::show_linkage_names() const
  {return priv_->show_linkage_names_;}
  
  void
  diff_context::show_linkage_names(bool f)
  {priv_->show_linkage_names_= f;}
  
  /// Set a flag saying to show location information.
  ///
  /// @param f true to show location information.
  void
  diff_context::show_locs(bool f)
  {priv_->show_locs_= f;}
  
  /// @return true if we want to show location information, false
  /// otherwise.
  bool
  diff_context::show_locs() const
  {return priv_->show_locs_;}
  
  /// A getter for the flag that says if we should report about
  /// functions or variables diff nodes that have *exclusively*
  /// redundant diff tree children nodes.
  ///
  /// @return the flag.
  bool
  diff_context::show_redundant_changes() const
  {return priv_->show_redundant_changes_;}
  
  /// A setter for the flag that says if we should report about
  /// functions or variables diff nodes that have *exclusively*
  /// redundant diff tree children nodes.
  ///
  /// @param f the flag to set.
  void
  diff_context::show_redundant_changes(bool f)
  {priv_->show_redundant_changes_ = f;}
  
  /// Getter for the flag that indicates if symbols not referenced by
  /// any debug info are to be compared and reported about.
  ///
  /// @return the boolean flag.
  bool
  diff_context::show_symbols_unreferenced_by_debug_info() const
  {return priv_->show_syms_unreferenced_by_di_;}
  
  /// Setter for the flag that indicates if symbols not referenced by
  /// any debug info are to be compared and reported about.
  ///
  /// @param f the new flag to set.
  void
  diff_context::show_symbols_unreferenced_by_debug_info(bool f)
  {priv_->show_syms_unreferenced_by_di_ = f;}
  
  /// Getter for the flag that indicates if symbols not referenced by
  /// any debug info and that got added are to be reported about.
  ///
  /// @return true iff symbols not referenced by any debug info and that
  /// got added are to be reported about.
  bool
  diff_context::show_added_symbols_unreferenced_by_debug_info() const
  {return priv_->show_added_syms_unreferenced_by_di_;}
  
  /// Setter for the flag that indicates if symbols not referenced by
  /// any debug info and that got added are to be reported about.
  ///
  /// @param f the new flag that says if symbols not referenced by any
  /// debug info and that got added are to be reported about.
  void
  diff_context::show_added_symbols_unreferenced_by_debug_info(bool f)
  {priv_->show_added_syms_unreferenced_by_di_ = f;}
  
  /// Setter for the flag that indicates if changes on types unreachable
  /// from global functions and variables are to be reported.
  ///
  /// @param f if true, then changes on types unreachable from global
  /// functions and variables are to be reported.
  void
  diff_context::show_unreachable_types(bool f)
  {priv_->show_unreachable_types_ = f;}
  
  /// Getter for the flag that indicates if changes on types unreachable
  /// from global functions and variables are to be reported.
  ///
  /// @return true iff changes on types unreachable from global
  /// functions and variables are to be reported.
  bool
  diff_context::show_unreachable_types()
  {return priv_->show_unreachable_types_;}
  
  /// Getter of the flag that indicates if the leaf reporter should
  /// display a summary of the interfaces impacted by a given leaf
  /// change or not.
  ///
  /// @return the flag that indicates if the leaf reporter should
  /// display a summary of the interfaces impacted by a given leaf
  /// change or not.
  bool
  diff_context::show_impacted_interfaces() const
  {return priv_->show_impacted_interfaces_;}
  
  /// Setter of the flag that indicates if the leaf reporter should
  /// display a summary of the interfaces impacted by a given leaf
  /// change or not.
  ///
  /// @param f the new value of the flag that indicates if the leaf
  /// reporter should display a summary of the interfaces impacted by a
  /// given leaf change or not.
  void
  diff_context::show_impacted_interfaces(bool f)
  {priv_->show_impacted_interfaces_ = f;}
  
  /// Setter for the default output stream used by code of the
  /// comparison engine.  By default the default output stream is a NULL
  /// pointer.
  ///
  /// @param o a pointer to the default output stream.
  void
  diff_context::default_output_stream(ostream* o)
  {priv_->default_output_stream_ = o;}
  
  /// Getter for the default output stream used by code of the
  /// comparison engine.  By default the default output stream is a NULL
  /// pointer.
  ///
  /// @return a pointer to the default output stream.
  ostream*
  diff_context::default_output_stream()
  {return priv_->default_output_stream_;}
  
  /// Setter for the errror output stream used by code of the comparison
  /// engine.  By default the error output stream is a NULL pointer.
  ///
  /// @param o a pointer to the error output stream.
  void
  diff_context::error_output_stream(ostream* o)
  {priv_->error_output_stream_ = o;}
  
  /// Getter for the errror output stream used by code of the comparison
  /// engine.  By default the error output stream is a NULL pointer.
  ///
  /// @return a pointer to the error output stream.
  ostream*
  diff_context::error_output_stream() const
  {return priv_->error_output_stream_;}
  
  /// Test if the comparison engine should dump the diff tree for the
  /// changed functions and variables it has.
  ///
  /// @return true if after the comparison, the engine should dump the
  /// diff tree for the changed functions and variables it has.
  bool
  diff_context::dump_diff_tree() const
  {return priv_->dump_diff_tree_;}
  
  /// Set if the comparison engine should dump the diff tree for the
  /// changed functions and variables it has.
  ///
  /// @param f true if after the comparison, the engine should dump the
  /// diff tree for the changed functions and variables it has.
  void
  diff_context::dump_diff_tree(bool f)
  {priv_->dump_diff_tree_ = f;}
  
  /// Emit a textual representation of a diff tree to the error output
  /// stream of the current context, for debugging purposes.
  ///
  /// @param d the diff tree to serialize to the error output associated
  /// to the current instance of @ref diff_context.
  void
  diff_context::do_dump_diff_tree(const diff_sptr d) const
  {
    if (error_output_stream())
      print_diff_tree(d, *error_output_stream());
  }
  
  /// Emit a textual representation of a @ref corpus_diff tree to the error
  /// output stream of the current context, for debugging purposes.
  ///
  /// @param d the @ref corpus_diff tree to serialize to the error
  /// output associated to the current instance of @ref diff_context.
  void
  diff_context::do_dump_diff_tree(const corpus_diff_sptr d) const
  {
    if (error_output_stream())
      print_diff_tree(d, *error_output_stream());
  }
  // </diff_context stuff>
  
  // <diff stuff>
  
  /// Constructor for the @ref diff type.
  ///
  /// This constructs a diff between two subjects that are actually
  /// declarations; the first and the second one.
  ///
  /// @param first_subject the first decl (subject) of the diff.
  ///
  /// @param second_subject the second decl (subject) of the diff.
  diff::diff(type_or_decl_base_sptr first_subject,
  	   type_or_decl_base_sptr second_subject)
    : priv_(new priv(first_subject, second_subject,
  		   diff_context_sptr(),
  		   NO_CHANGE_CATEGORY,
  		   /*reported_once=*/false,
  		   /*currently_reporting=*/false))
  {}
  
  /// Constructor for the @ref diff type.
  ///
  /// This constructs a diff between two subjects that are actually
  /// declarations; the first and the second one.
  ///
  /// @param first_subject the first decl (subject) of the diff.
  ///
  /// @param second_subject the second decl (subject) of the diff.
  ///
  /// @param ctxt the context of the diff.  Note that this context
  /// object must stay alive during the entire life time of the current
  /// instance of @ref diff.  Otherwise, memory corruption issues occur.
  diff::diff(type_or_decl_base_sptr	first_subject,
  	   type_or_decl_base_sptr	second_subject,
  	   diff_context_sptr	ctxt)
    : priv_(new priv(first_subject, second_subject,
  		   ctxt, NO_CHANGE_CATEGORY,
  		   /*reported_once=*/false,
  		   /*currently_reporting=*/false))
  {}
  
  /// Flag a given diff node as being traversed.
  ///
  /// For certain diff nodes like @ref class_diff, it's important to
  /// avoid traversing the node again while it's already being
  /// traversed; otherwise this leads to infinite loops.  So the
  /// diff::begin_traversing() and diff::end_traversing() methods flag a
  /// given node as being traversed (or not), so that
  /// diff::is_traversing() can tell if the node is being traversed.
  ///
  /// Note that traversing a node means visiting it *and* visiting its
  /// children nodes.
  ///
  /// The canonical node is marked as being traversed too.
  ///
  /// These functions are called by the traversing code.
  void
  diff::begin_traversing()
  {
    ABG_ASSERT(!is_traversing());
    if (priv_->canonical_diff_)
      priv_->canonical_diff_->priv_->traversing_ = true;
    priv_->traversing_ = true;
  }
  
  /// Tell if a given node is being traversed or not.
  ///
  /// Note that traversing a node means visiting it *and* visiting its
  /// children nodes.
  ///
  /// It's the canonical node which is looked at, actually.
  ///
  /// Please read the comments for the diff::begin_traversing() for mode
  /// context.
  ///
  /// @return true if the current instance of @diff is being traversed.
  bool
  diff::is_traversing() const
  {
    if (priv_->canonical_diff_)
      return priv_->canonical_diff_->priv_->traversing_;
    return priv_->traversing_;
  }
  
  /// Flag a given diff node as not being traversed anymore.
  ///
  /// Note that traversing a node means visiting it *and* visiting its
  /// children nodes.
  ///
  /// Please read the comments of the function diff::begin_traversing()
  /// for mode context.
  void
  diff::end_traversing()
  {
    ABG_ASSERT(is_traversing());
    if (priv_->canonical_diff_)
      priv_->canonical_diff_->priv_->traversing_ = false;
    priv_->traversing_ = false;
  }
  
  /// Finish the building of a given kind of a diff tree node.
  ///
  /// For instance, certain kinds of diff tree node have specific
  /// children nodes that are populated after the constructor of the
  /// diff tree node has been called.  In that case, calling overloads
  /// of this method ensures that these children nodes are properly
  /// gathered and setup.
  void
  diff::finish_diff_type()
  {
  }
  
  /// Getter of the first subject of the diff.
  ///
  /// @return the first subject of the diff.
  type_or_decl_base_sptr
  diff::first_subject() const
  {return dynamic_pointer_cast<type_or_decl_base>(priv_->first_subject_);}
  
  /// Getter of the second subject of the diff.
  ///
  /// @return the second subject of the diff.
  type_or_decl_base_sptr
  diff::second_subject() const
  {return dynamic_pointer_cast<type_or_decl_base>(priv_->second_subject_);}
  
  /// Getter for the children nodes of the current @ref diff node.
  ///
  /// @return a vector of the children nodes.
  const vector<diff*>&
  diff::children_nodes() const
  {return priv_->children_;}
  
  /// Getter for the parent node of the current @ref diff node.
  ///
  /// @return the parent node of the current @ref diff node.
  const diff*
  diff::parent_node() const
  {return priv_->parent_;}
  
  /// Getter for the canonical diff of the current instance of @ref
  /// diff.
  ///
  /// Note that the canonical diff node for the current instanc eof diff
  /// node must have been set by invoking
  /// class_diff::initialize_canonical_diff() on the current instance of
  /// diff node.
  ///
  /// @return the canonical diff node or null if none was set.
  diff*
  diff::get_canonical_diff() const
  {return priv_->canonical_diff_;}
  
  /// Setter for the canonical diff of the current instance of @ref
  /// diff.
  ///
  /// @param d the new canonical node to set.
  void
  diff::set_canonical_diff(diff * d)
  {priv_->canonical_diff_ = d;}
  
  /// Add a new child node to the vector of children nodes for the
  /// current @ref diff node.
  ///
  /// @param d the new child node to add to the children nodes.
  void
  diff::append_child_node(diff_sptr d)
  {
    ABG_ASSERT(d);
  
    // Ensure 'd' is kept alive for the life time of the context of this
    // diff.
    context()->keep_diff_alive(d);
  
    // Add the underlying pointer of 'd' to the vector of children.
    // Note that this vector holds no reference to 'd'. This is to avoid
    // reference cycles.  The reference to 'd' is held by the context of
    // this diff, thanks to the call to context()->keep_diff_alive(d)
    // above.
    priv_->children_.push_back(d.get());
  
    diff_less_than_functor comp;
    std::sort(priv_->children_.begin(),
  	    priv_->children_.end(),
  	    comp);
  
    d->priv_->parent_ = this;
  }
  
  /// Getter of the context of the current diff.
  ///
  /// @return the context of the current diff.
  const diff_context_sptr
  diff::context() const
  {return priv_->get_context();}
  
  /// Setter of the context of the current diff.
  ///
  /// @param c the new context to set.
  void
  diff::context(diff_context_sptr c)
  {priv_->ctxt_ = c;}
  
  /// Tests if we are currently in the middle of emitting a report for
  /// this diff.
  ///
  /// @return true if we are currently emitting a report for the
  /// current diff, false otherwise.
  bool
  diff::currently_reporting() const
  {
    if (priv_->canonical_diff_)
      return priv_->canonical_diff_->priv_->currently_reporting_;
    return priv_->currently_reporting_;
  }
  
  /// Sets a flag saying if we are currently in the middle of emitting
  /// a report for this diff.
  ///
  /// @param f true if we are currently emitting a report for the
  /// current diff, false otherwise.
  void
  diff::currently_reporting(bool f) const
  {
    if (priv_->canonical_diff_)
      priv_->canonical_diff_->priv_->currently_reporting_ = f;
    priv_->currently_reporting_ = f;
  }
  
  /// Tests if a report has already been emitted for the current diff.
  ///
  /// @return true if a report has already been emitted for the
  /// current diff, false otherwise.
  bool
  diff::reported_once() const
  {
    ABG_ASSERT(priv_->canonical_diff_);
    return priv_->canonical_diff_->priv_->reported_once_;
  }
  
  /// The generic traversing code that walks a given diff sub-tree.
  ///
  /// Note that there is a difference between traversing a diff node and
  /// visiting it.  Basically, traversing a diff node means visiting it
  /// and visiting its children nodes too.  So one can visit a node
  /// without traversing it.  But traversing a node without visiting it
  /// is not possible.
  ///
  /// Note that by default this traversing code visits a given class of
  /// equivalence of a diff node only once.  This behaviour can been
  /// changed by calling
  /// diff_context::visiting_a_node_twice_is_forbidden(), but this is
  /// very risky as it might create endless loops while visiting a diff
  /// tree graph that has changes that refer to themselves; that is,
  /// diff tree graphs with cycles.
  ///
  /// When a diff node is encountered, the
  /// diff_node_visitor::visit_begin() method is invoked on the diff
  /// node first.
  ///
  /// If the diff node has already been visited, then
  /// node_visitor::visit_end() is called on it and the node traversing
  /// is done; the children of the diff node are not visited in this
  /// case.
  ///
  /// If the diff node has *NOT* been visited yet, then the
  /// diff_node_visitor::visit() method is invoked with it's 'pre'
  /// argument set to true.  Then if the diff_node_visitor::visit()
  /// returns true, then the children nodes of the diff node are
  /// visited.  Otherwise, no children nodes of the diff node is
  /// visited and the diff_node_visitor::visit_end() is called.
  
  /// After the children nodes are visited (and only if they are
  /// visited) the diff_node_visitor::visit() method is invoked with
  /// it's 'pre' argument set to false.  And then the
  /// diff_node_visitor::visit_end() is called.
  ///
  /// @param v the entity that visits each node of the diff sub-tree.
  ///
  /// @return true to tell the caller that all of the sub-tree could be
  /// walked.  This instructs the caller to keep walking the rest of the
  /// tree.  Return false otherwise.
  bool
  diff::traverse(diff_node_visitor& v)
  {
    finish_diff_type();
  
    v.visit_begin(this);
  
    bool already_visited = false;
    if (context()->visiting_a_node_twice_is_forbidden()
        && context()->diff_has_been_visited(this))
      already_visited = true;
  
    bool mark_visited_nodes_as_traversed =
      !(v.get_visiting_kind() & DO_NOT_MARK_VISITED_NODES_AS_VISITED);
  
    if (!already_visited && !v.visit(this, /*pre=*/true))
      {
        v.visit_end(this);
        if (mark_visited_nodes_as_traversed)
  	context()->mark_diff_as_visited(this);
        return false;
      }
  
    if (!(v.get_visiting_kind() & SKIP_CHILDREN_VISITING_KIND)
        && !is_traversing()
        && !already_visited)
      {
        begin_traversing();
        for (vector<diff*>::const_iterator i = children_nodes().begin();
  	   i != children_nodes().end();
  	   ++i)
  	{
  	  if (!(*i)->traverse(v))
  	    {
  	      v.visit_end(this);
  	      if (mark_visited_nodes_as_traversed)
  		context()->mark_diff_as_visited(this);
  	      end_traversing();
  	      return false;
  	    }
  	}
        end_traversing();
      }
  
    if (!v.visit(this, /*pref=*/false))
      {
        v.visit_end(this);
        if (mark_visited_nodes_as_traversed)
  	context()->mark_diff_as_visited(this);
        return false;
      }
  
    v.visit_end(this);
    if (!already_visited && mark_visited_nodes_as_traversed)
      context()->mark_diff_as_visited(this);
  
    return true;
  }
  
  /// Sets a flag saying if a report has already been emitted for the
  /// current diff.
  ///
  /// @param f true if a report has already been emitted for the
  /// current diff, false otherwise.
  void
  diff::reported_once(bool f) const
  {
    ABG_ASSERT(priv_->canonical_diff_);
    priv_->canonical_diff_->priv_->reported_once_ = f;
    priv_->reported_once_ = f;
  }
  
  /// Getter for the local category of the current diff tree node.
  ///
  /// The local category represents the set of categories of a diff
  /// node, not taking in account the categories inherited from its
  /// children nodes.
  ///
  /// @return the local category of the current diff tree node.
  diff_category
  diff::get_local_category() const
  {return priv_->local_category_;}
  
  /// Getter of the category of the class of equivalence of the current
  /// diff tree node.
  ///
  /// That is, if the current diff tree node has a canonical node,
  /// return the category of that canonical node.  Otherwise, return the
  /// category of the current node.
  ///
  /// @return the category of the class of equivalence of the current
  /// tree node.
  diff_category
  diff::get_class_of_equiv_category() const
  {
    diff* canonical = get_canonical_diff();
    return canonical ? canonical->get_category() : get_category();
  }
  
  /// Getter for the category of the current diff tree node.
  ///
  /// This category represents the union of the local category and the
  /// categories inherited from the children diff nodes.
  ///
  /// @return the category of the current diff tree node.
  diff_category
  diff::get_category() const
  {return priv_->category_;}
  
  /// Adds the current diff tree node to an additional set of
  /// categories.  Note that the categories include thoses inherited
  /// from the children nodes of this diff node.
  ///
  /// @param c a bit-map representing the set of categories to add the
  /// current diff tree node to.
  ///
  /// @return the resulting bit-map representing the categories this
  /// current diff tree node belongs to, including those inherited from
  /// its children nodes.
  diff_category
  diff::add_to_category(diff_category c)
  {
    priv_->category_ = priv_->category_ | c;
    return priv_->category_;
  }
  
  /// Adds the current diff tree node to the categories resulting from
  /// the local changes of the current diff node.
  ///
  /// @param c a bit-map representing the set of categories to add the
  /// current diff tree node to.
  ///
  /// @return the resulting bit-map representing the categories this
  /// current diff tree node belongs to.
  diff_category
  diff::add_to_local_category(diff_category c)
  {
    priv_->local_category_ = priv_->local_category_ | c;
    return priv_->local_category_;
  }
  
  /// Adds the current diff tree node to the categories resulting from
  /// the local and inherited changes of the current diff node.
  ///
  /// @param c a bit-map representing the set of categories to add the
  /// current diff tree node to.
  void
  diff::add_to_local_and_inherited_categories(diff_category c)
  {
    add_to_local_category(c);
    add_to_category(c);
  }
  
  /// Remove the current diff tree node from an a existing sef of
  /// categories.  The categories include those inherited from the
  /// children nodes of the current diff node.
  ///
  /// @param c a bit-map representing the set of categories to add the
  /// current diff tree node to.
  ///
  /// @return the resulting bit-map representing the categories this
  /// current diff tree onde belongs to, including the categories
  /// inherited from the children nodes of the current diff node.
  diff_category
  diff::remove_from_category(diff_category c)
  {
    priv_->category_ = priv_->category_ & ~c;
    return priv_->category_;
  }
  
  /// Remove the current diff tree node from the categories resulting
  /// from the local changes.
  ///
  /// @param c a bit-map representing the set of categories to add the
  /// current diff tree node to.
  ///
  /// @return the resulting bit-map representing the categories this
  /// current diff tree onde belongs to.
  diff_category
  diff::remove_from_local_category(diff_category c)
  {
    priv_->local_category_ = priv_->local_category_ & ~c;
    return priv_->local_category_;
  }
  
  /// Set the category of the current @ref diff node.  This category
  /// includes the categories inherited from the children nodes of the
  /// current diff node.
  ///
  /// @param c the new category for the current diff node.
  void
  diff::set_category(diff_category c)
  {priv_->category_ = c;}
  
  /// Set the local category of the current @ref diff node.
  ///
  /// @param c the new category for the current diff node.
  void
  diff::set_local_category(diff_category c)
  {priv_->local_category_ = c;}
  
  /// Test if this diff tree node is to be filtered out for reporting
  /// purposes.
  ///
  /// The function tests if the categories of the diff tree node are
  /// "forbidden" by the context or not.
  ///
  /// @return true iff the current diff node should NOT be reported.
  bool
  diff::is_filtered_out() const
  {
    if (diff * canonical = get_canonical_diff())
      if (canonical->get_category() & SUPPRESSED_CATEGORY
  	|| canonical->get_category() & PRIVATE_TYPE_CATEGORY)
        // The canonical type was suppressed either by a user-provided
        // suppression specification or by a "private-type" suppression
        // specification..  This means all the class of equivalence of
        // that canonical type was suppressed.  So this node should be
        // suppressed too.
        return true;
    return priv_->is_filtered_out(get_category());
  }
  
  /// Test if this diff tree node is to be filtered out for reporting
  /// purposes, but by considering only the categories that were *NOT*
  /// inherited from its children nodes.
  ///
  /// The function tests if the local categories of the diff tree node
  /// are "forbidden" by the context or not.
  ///
  /// @return true iff the current diff node should NOT be reported,
  /// with respect to its local categories.
  bool
  diff::is_filtered_out_wrt_non_inherited_categories() const
  {return priv_->is_filtered_out(get_local_category());}
  
  /// Test if the current diff node has been suppressed by a
  /// user-provided suppression specification.
  ///
  /// @return true if the current diff node has been suppressed by a
  /// user-provided suppression list.
  bool
  diff::is_suppressed() const
  {
    bool is_private = false;
    return is_suppressed(is_private);
  }
  
  /// Test if the current diff node has been suppressed by a
  /// user-provided suppression specification or by an auto-generated
  /// "private type" suppression specification.
  ///
  /// Note that private type suppressions are auto-generated from the
  /// path to where public headers are, as given by the user.
  ///
  /// @param is_private_type out parameter if the current diff node was
  /// suppressed because it's a private type then this parameter is set
  /// to true.
  ///
  /// @return true if the current diff node has been suppressed by a
  /// user-provided suppression list.
  bool
  diff::is_suppressed(bool &is_private_type) const
  {
    const suppressions_type& suppressions = context()->suppressions();
    for (suppressions_type::const_iterator i = suppressions.begin();
         i != suppressions.end();
         ++i)
      {
        if ((*i)->suppresses_diff(this))
  	{
  	  if (is_private_type_suppr_spec(*i))
  	    is_private_type = true;
  	  return true;
  	}
      }
    return false;
  }
  
  /// Test if this diff tree node should be reported.
  ///
  /// @return true iff the current node should be reported.
  bool
  diff::to_be_reported() const
  {
    if (has_changes() && !is_filtered_out())
      return true;
    return false;
  }
  
  /// Test if this diff tree node should be reported when considering
  /// the categories that were *NOT* inherited from its children nodes.
  ///
  /// @return true iff the current node should be reported.
  bool
  diff::has_local_changes_to_be_reported() const
  {
    if (has_local_changes()
        && !is_filtered_out_wrt_non_inherited_categories())
      return true;
    return false;
  }
  
  /// Get a pretty representation of the current @ref diff node.
  ///
  /// This is suitable for e.g. emitting debugging traces for the diff
  /// tree nodes.
  ///
  /// @return the pretty representation of the diff node.
  const string&
  diff::get_pretty_representation() const
  {
    if (priv_->pretty_representation_.empty())
      priv_->pretty_representation_ = "empty_diff";
    return priv_->pretty_representation_;
  }
  
  /// Default implementation of the hierachy chaining virtual function.
  ///
  /// There are several types of diff nodes that have logical children
  /// nodes; for instance, a typedef_diff has the diff of the underlying
  /// type as a child node.  A var_diff has the diff of the types of the
  /// variables as a child node, etc.
  ///
  /// But because the @ref diff base has a generic representation for
  /// children nodes of the all the types of @ref diff nodes (regardless
  /// of the specific most-derived type of diff node) that one can get
  /// using the method diff::children_nodes(), one need to populate that
  /// vector of children node.
  ///
  /// Populating that vector of children node is done by this function;
  /// it must be overloaded by each most-derived type of diff node that
  /// extends the @ref diff type.
  void
  diff::chain_into_hierarchy()
  {}
  
  // </diff stuff>
  
  // <type_diff_base stuff>
  
  type_diff_base::type_diff_base(type_base_sptr	first_subject,
  			       type_base_sptr	second_subject,
  			       diff_context_sptr	ctxt)
    : diff(first_subject, second_subject, ctxt),
      priv_(new priv)
  {}
  
  type_diff_base::~type_diff_base()
  {}
  // </type_diff_base stuff>
  
  // <decl_diff_base stuff>
  
  /// Constructor of @ref decl_diff_base.
  ///
  /// @param first_subject the first subject of the diff.
  ///
  /// @param second_subject the second subject of the diff.
  ///
  /// @param ctxt the context of the diff.  This object must stay alive
  /// at least during the life time of the current instance of @ref
  /// decl_diff_base, otherwise, memory corruption issues occur.
  decl_diff_base::decl_diff_base(decl_base_sptr	first_subject,
  			       decl_base_sptr	second_subject,
  			       diff_context_sptr	ctxt)
    : diff(first_subject, second_subject, ctxt),
      priv_(new priv)
  {}
  
  decl_diff_base::~decl_diff_base()
  {}
  
  // </decl_diff_base stuff>
  
  // <distinct_diff stuff>
  
  /// @return a pretty representation for the @ref distinct_diff node.
  const string&
  distinct_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "distinct_diff[";
        if (first_subject())
  	o << first_subject()->get_pretty_representation();
        else
  	o << "null";
        o << ", ";
        if (second_subject())
  	o << second_subject()->get_pretty_representation() ;
        else
  	o << "null";
        o << "]" ;
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @distinct_diff.
  ///
  /// The children nodes can then later be retrieved using
  /// diff::children_nodes().
  void
  distinct_diff::chain_into_hierarchy()
  {
    ABG_ASSERT(entities_are_of_distinct_kinds(first(), second()));
  
    if (diff_sptr d = compatible_child_diff())
      append_child_node(d);
  }
  
  /// Constructor for @ref distinct_diff.
  ///
  /// Note that the two entities considered for the diff (and passed in
  /// parameter) must be of different kinds.
  ///
  /// @param first the first entity to consider for the diff.
  ///
  /// @param second the second entity to consider for the diff.
  ///
  /// @param ctxt the context of the diff.  Note that this context
  /// object must stay alive at least during the life time of the
  /// current instance of @ref distinct_diff.  Otherwise memory
  /// corruption issues occur.
  distinct_diff::distinct_diff(type_or_decl_base_sptr first,
  			     type_or_decl_base_sptr second,
  			     diff_context_sptr ctxt)
    : diff(first, second, ctxt),
      priv_(new priv)
  {ABG_ASSERT(entities_are_of_distinct_kinds(first, second));}
  
  /// Finish building the current instance of @ref distinct_diff.
  void
  distinct_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
  
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// Getter for the first subject of the diff.
  ///
  /// @return the first subject of the diff.
  const type_or_decl_base_sptr
  distinct_diff::first() const
  {return first_subject();}
  
  /// Getter for the second subject of the diff.
  ///
  /// @return the second subject of the diff.
  const type_or_decl_base_sptr
  distinct_diff::second() const
  {return second_subject();}
  
  /// Getter for the child diff of this distinct_diff instance.
  ///
  /// When a distinct_diff has two subjects that are different but
  /// compatible, then the distinct_diff instance has a child diff node
  /// (named the compatible child diff) that is the diff between the two
  /// subjects stripped from their typedefs.  Otherwise, the compatible
  /// child diff is nul.
  ///
  /// Note that two diff subjects (that compare different) are
  /// considered compatible if stripping typedefs out of them makes them
  /// comparing equal.
  ///
  /// @return the compatible child diff node, if any.  Otherwise, null.
  const diff_sptr
  distinct_diff::compatible_child_diff() const
  {
    if (!priv_->compatible_child_diff)
      {
        type_base_sptr fs = strip_typedef(is_type(first())),
  	ss = strip_typedef(is_type(second()));
  
        if (fs && ss
  	  && !entities_are_of_distinct_kinds(get_type_declaration(fs),
  					     get_type_declaration(ss)))
  	priv_->compatible_child_diff = compute_diff(get_type_declaration(fs),
  						    get_type_declaration(ss),
  						    context());
      }
    return priv_->compatible_child_diff;
  }
  
  /// Test if the two arguments are of different kind, or that are both
  /// NULL.
  ///
  /// @param first the first argument to test for similarity in kind.
  ///
  /// @param second the second argument to test for similarity in kind.
  ///
  /// @return true iff the two arguments are of different kind.
  bool
  distinct_diff::entities_are_of_distinct_kinds(type_or_decl_base_sptr first,
  					      type_or_decl_base_sptr second)
  {
    if (!!first != !!second)
      return true;
    if (!first && !second)
      // We do consider diffs of two empty decls as a diff of distinct
      // kinds, for now.
      return true;
    if (first == second)
      return false;
  
    const type_or_decl_base &f = *first, &s = *second;
    return typeid(f) != typeid(s);
  }
  
  /// @return true if the two subjects of the diff are different, false
  /// otherwise.
  bool
  distinct_diff::has_changes() const
  {return first() != second();}
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  distinct_diff::has_local_changes() const
  {
    // Changes on a distinct_diff are all local.
    if (has_changes())
      return LOCAL_TYPE_CHANGE_KIND;
    return NO_CHANGE_KIND;
  }
  
  /// Emit a report about the current diff instance.
  ///
  /// @param out the output stream to send the diff report to.
  ///
  /// @param indent the indentation string to use in the report.
  void
  distinct_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Try to diff entities that are of distinct kinds.
  ///
  /// @param first the first entity to consider for the diff.
  ///
  /// @param second the second entity to consider for the diff.
  ///
  /// @param ctxt the context of the diff.
  ///
  /// @return a non-null diff if a diff object could be built, null
  /// otherwise.
  distinct_diff_sptr
  compute_diff_for_distinct_kinds(const type_or_decl_base_sptr first,
  				const type_or_decl_base_sptr second,
  				diff_context_sptr ctxt)
  {
    if (!distinct_diff::entities_are_of_distinct_kinds(first, second))
      return distinct_diff_sptr();
  
    distinct_diff_sptr result(new distinct_diff(first, second, ctxt));
  
    ctxt->initialize_canonical_diff(result);
  
    return result;
  }
  
  /// </distinct_diff stuff>
  
  /// Try to compute a diff on two instances of DiffType representation.
  ///
  /// The function template performs the diff if and only if the decl
  /// representations are of a DiffType.
  ///
  /// @tparm DiffType the type of instances to diff.
  ///
  /// @param first the first representation of decl to consider in the
  /// diff computation.
  ///
  /// @param second the second representation of decl to consider in the
  /// diff computation.
  ///
  /// @param ctxt the diff context to use.
  ///
  ///@return the diff of the two types @p first and @p second if and
  ///only if they represent the parametrized type DiffType.  Otherwise,
  ///returns a NULL pointer value.
  template<typename DiffType>
  diff_sptr
  try_to_diff(const type_or_decl_base_sptr first,
  	    const type_or_decl_base_sptr second,
  	    diff_context_sptr ctxt)
  {
    if (shared_ptr<DiffType> f =
        dynamic_pointer_cast<DiffType>(first))
      {
        shared_ptr<DiffType> s =
  	dynamic_pointer_cast<DiffType>(second);
        if (!s)
  	return diff_sptr();
        return compute_diff(f, s, ctxt);
      }
    return diff_sptr();
  }
  
  
  /// This is a specialization of @ref try_to_diff() template to diff
  /// instances of @ref class_decl.
  ///
  /// @param first the first representation of decl to consider in the
  /// diff computation.
  ///
  /// @param second the second representation of decl to consider in the
  /// diff computation.
  ///
  /// @param ctxt the diff context to use.
  template<>
  diff_sptr
  try_to_diff<class_decl>(const type_or_decl_base_sptr first,
  			const type_or_decl_base_sptr second,
  			diff_context_sptr ctxt)
  {
    if (class_decl_sptr f =
        dynamic_pointer_cast<class_decl>(first))
      {
        class_decl_sptr s = dynamic_pointer_cast<class_decl>(second);
        if (!s)
  	return diff_sptr();
  
        if (f->get_is_declaration_only())
  	{
  	  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 =
  	    is_class_type(s->get_definition_of_declaration());
  	  if (s2)
  	    s = s2;
  	}
        return compute_diff(f, s, ctxt);
      }
    return diff_sptr();
  }
  
  /// Try to diff entities that are of distinct kinds.
  ///
  /// @param first the first entity to consider for the diff.
  ///
  /// @param second the second entity to consider for the diff.
  ///
  /// @param ctxt the context of the diff.
  ///
  /// @return a non-null diff if a diff object could be built, null
  /// otherwise.
  static diff_sptr
  try_to_diff_distinct_kinds(const type_or_decl_base_sptr first,
  			   const type_or_decl_base_sptr second,
  			   diff_context_sptr ctxt)
  {return compute_diff_for_distinct_kinds(first, second, ctxt);}
  
  /// Compute the difference between two types.
  ///
  /// The function considers every possible types known to libabigail
  /// and runs the appropriate diff function on them.
  ///
  /// Whenever a new kind of type decl is supported by abigail, if we
  /// want to be able to diff two instances of it, we need to update
  /// this function to support it.
  ///
  /// @param first the first type decl to consider for the diff
  ///
  /// @param second the second type decl to consider for the diff.
  ///
  /// @param ctxt the diff context to use.
  ///
  /// @return the resulting diff.  It's a pointer to a descendent of
  /// abigail::comparison::diff.
  static diff_sptr
  compute_diff_for_types(const type_or_decl_base_sptr& first,
  		       const type_or_decl_base_sptr& second,
  		       const diff_context_sptr& ctxt)
  {
    type_or_decl_base_sptr f = first;
    type_or_decl_base_sptr s = second;
  
    // Look through no-op qualified types.
    f = look_through_no_op_qualified_type(is_type(f));
    s = look_through_no_op_qualified_type(is_type(s));
  
    diff_sptr d;
  
    ((d = try_to_diff<type_decl>(f, s, ctxt))
     ||(d = try_to_diff<enum_type_decl>(f, s, ctxt))
     ||(d = try_to_diff<union_decl>(f, s,ctxt))
     ||(d = try_to_diff<class_decl>(f, s,ctxt))
     ||(d = try_to_diff<pointer_type_def>(f, s, ctxt))
     ||(d = try_to_diff<reference_type_def>(f, s, ctxt))
     ||(d = try_to_diff<array_type_def>(f, s, ctxt))
     ||(d = try_to_diff<qualified_type_def>(f, s, ctxt))
     ||(d = try_to_diff<typedef_decl>(f, s, ctxt))
     ||(d = try_to_diff<function_type>(f, s, ctxt))
     ||(d = try_to_diff_distinct_kinds(f, s, ctxt)));
  
    ABG_ASSERT(d);
  
    return d;
  }
  
  diff_category
  operator|(diff_category c1, diff_category c2)
  {return static_cast<diff_category>(static_cast<unsigned>(c1)
  				   | static_cast<unsigned>(c2));}
  
  diff_category&
  operator|=(diff_category& c1, diff_category c2)
  {
    c1 = c1 | c2;
    return c1;
  }
  
  diff_category&
  operator&=(diff_category& c1, diff_category c2)
  {
    c1 = c1 & c2;
    return c1;
  }
  
  diff_category
  operator^(diff_category c1, diff_category c2)
  {return static_cast<diff_category>(static_cast<unsigned>(c1)
  				   ^ static_cast<unsigned>(c2));}
  
  diff_category
  operator&(diff_category c1, diff_category c2)
  {return static_cast<diff_category>(static_cast<unsigned>(c1)
  				   & static_cast<unsigned>(c2));}
  
  diff_category
  operator~(diff_category c)
  {return static_cast<diff_category>(~static_cast<unsigned>(c));}
  
  
  /// Getter of a bitmap made of the set of change categories that are
  /// considered harmless.
  ///
  /// @return the bitmap made of the set of change categories that are
  /// considered harmless.
  diff_category
  get_default_harmless_categories_bitmap()
  {
    return (abigail::comparison::ACCESS_CHANGE_CATEGORY
  	  | abigail::comparison::COMPATIBLE_TYPE_CHANGE_CATEGORY
  	  | abigail::comparison::HARMLESS_DECL_NAME_CHANGE_CATEGORY
  	  | abigail::comparison::NON_VIRT_MEM_FUN_CHANGE_CATEGORY
  	  | abigail::comparison::STATIC_DATA_MEMBER_CHANGE_CATEGORY
  	  | abigail::comparison::HARMLESS_ENUM_CHANGE_CATEGORY
  	  | abigail::comparison::HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY
  	  | abigail::comparison::HARMLESS_UNION_CHANGE_CATEGORY
  	  | abigail::comparison::HARMLESS_DATA_MEMBER_CHANGE_CATEGORY
  	  | abigail::comparison::TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY
  	  | abigail::comparison::FN_PARM_TYPE_TOP_CV_CHANGE_CATEGORY
  	  | abigail::comparison::FN_PARM_TYPE_CV_CHANGE_CATEGORY
  	  | abigail::comparison::FN_RETURN_TYPE_CV_CHANGE_CATEGORY
  	  | abigail::comparison::VAR_TYPE_CV_CHANGE_CATEGORY
  	  | abigail::comparison::VOID_PTR_TO_PTR_CHANGE_CATEGORY
  	  | abigail::comparison::BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY);
  }
  
  /// Getter of a bitmap made of the set of change categories that are
  /// considered harmful.
  ///
  /// @return the bitmap made of the set of change categories that are
  /// considered harmful.
  diff_category
  get_default_harmful_categories_bitmap()
  {
    return (abigail::comparison::SIZE_OR_OFFSET_CHANGE_CATEGORY
  	  | abigail::comparison::VIRTUAL_MEMBER_CHANGE_CATEGORY);
  }
  
  /// Serialize an instance of @ref diff_category to an output stream.
  ///
  /// @param o the output stream to serialize @p c to.
  ///
  /// @param c the instance of diff_category to serialize.
  ///
  /// @return the output stream to serialize @p c to.
  ostream&
  operator<<(ostream& o, diff_category c)
  {
    bool emitted_a_category = false;
  
    if (c == NO_CHANGE_CATEGORY)
      {
        o << "NO_CHANGE_CATEGORY";
        emitted_a_category = true;
      }
  
    if (c & ACCESS_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "ACCESS_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & COMPATIBLE_TYPE_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "COMPATIBLE_TYPE_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & HARMLESS_DECL_NAME_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "HARMLESS_DECL_NAME_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & NON_VIRT_MEM_FUN_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "NON_VIRT_MEM_FUN_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & STATIC_DATA_MEMBER_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "STATIC_DATA_MEMBER_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & HARMLESS_ENUM_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "HARMLESS_ENUM_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
      if (c & HARMLESS_DATA_MEMBER_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "HARMLESS_DATA_MEMBER_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & HARMLESS_UNION_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "HARMLESS_UNION_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & SUPPRESSED_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "SUPPRESSED_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & PRIVATE_TYPE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "PRIVATE_TYPE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & SIZE_OR_OFFSET_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "SIZE_OR_OFFSET_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & VIRTUAL_MEMBER_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "VIRTUAL_MEMBER_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & REDUNDANT_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "REDUNDANT_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "TYPE_DECL_ONLY_DEF_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & FN_PARM_TYPE_TOP_CV_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "FN_PARM_TYPE_TOP_CV_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & FN_PARM_TYPE_CV_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "FN_PARM_TYPE_CV_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & FN_RETURN_TYPE_CV_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "FN_RETURN_TYPE_CV_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & VAR_TYPE_CV_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "VAR_TYPE_CV_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & VOID_PTR_TO_PTR_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "VOID_PTR_TO_PTR_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    if (c & BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY)
      {
        if (emitted_a_category)
  	o << "|";
        o << "BENIGN_INFINITE_ARRAY_CHANGE_CATEGORY";
        emitted_a_category |= true;
      }
  
    return o;
  }
  
  /// Compute the difference between two decls.
  ///
  /// The function consider every possible decls known to libabigail and
  /// runs the appropriate diff function on them.
  ///
  /// Whenever a new kind of non-type decl is supported by abigail, if
  /// we want to be able to diff two instances of it, we need to update
  /// this function to support it.
  ///
  /// @param first the first decl to consider for the diff
  ///
  /// @param second the second decl to consider for the diff.
  ///
  /// @param ctxt the diff context to use.
  ///
  /// @return the resulting diff.
  static diff_sptr
  compute_diff_for_decls(const decl_base_sptr first,
  		       const decl_base_sptr second,
  		       diff_context_sptr ctxt)
  {
  
    diff_sptr d;
  
    ((d = try_to_diff<function_decl>(first, second, ctxt))
     || (d = try_to_diff<var_decl>(first, second, ctxt))
     || (d = try_to_diff_distinct_kinds(first, second, ctxt)));
  
     ABG_ASSERT(d);
  
    return d;
  }
  
  /// Compute the difference between two decls.  The decls can represent
  /// either type declarations, or non-type declaration.
  ///
  /// Note that the two decls must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the first decl to consider.
  ///
  /// @param second the second decl to consider.
  ///
  /// @param ctxt the diff context to use.
  ///
  /// @return the resulting diff, or NULL if the diff could not be
  /// computed.
  diff_sptr
  compute_diff(const decl_base_sptr	first,
  	     const decl_base_sptr	second,
  	     diff_context_sptr		ctxt)
  {
    if (!first || !second)
      return diff_sptr();
  
    ABG_ASSERT(first->get_environment() == second->get_environment());
  
    diff_sptr d;
    if (is_type(first) && is_type(second))
      d = compute_diff_for_types(first, second, ctxt);
    else
      d = compute_diff_for_decls(first, second, ctxt);
    ABG_ASSERT(d);
    return d;
  }
  
  /// Compute the difference between two types.
  ///
  /// Note that the two types must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the first type to consider.
  ///
  /// @param second the second type to consider.
  ///
  /// @param ctxt the diff context to use.
  ///
  /// @return the resulting diff, or NULL if the diff couldn't be
  /// computed.
  diff_sptr
  compute_diff(const type_base_sptr	first,
  	     const type_base_sptr	second,
  	     diff_context_sptr		ctxt)
  {
    decl_base_sptr f = get_type_declaration(first),
      s = get_type_declaration(second);
  
    if (first && second)
      ABG_ASSERT(first->get_environment() == second->get_environment());
  
    diff_sptr d = compute_diff_for_types(f,s, ctxt);
    ABG_ASSERT(d);
    return d;
  }
  
  /// Get a copy of the pretty representation of a diff node.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return the pretty representation string.
  string
  get_pretty_representation(diff* d)
  {
    if (!d)
      return "";
    string prefix= "diff of ";
    return prefix + get_pretty_representation(d->first_subject());
  }
  
  // <var_diff stuff>
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref var_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  var_diff::chain_into_hierarchy()
  {append_child_node(type_diff());}
  
  /// @return the pretty representation for this current instance of
  /// @ref var_diff.
  const string&
  var_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "var_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  /// Constructor for @ref var_diff.
  ///
  /// @param first the first instance of @ref var_decl to consider in
  /// the diff.
  ///
  /// @param second the second instance of @ref var_decl to consider in
  /// the diff.
  ///
  /// @param type_diff the diff between types of the instances of
  /// var_decl.
  ///
  /// @param ctxt the diff context to use.
  var_diff::var_diff(var_decl_sptr	first,
  		   var_decl_sptr	second,
  		   diff_sptr		type_diff,
  		   diff_context_sptr	ctxt)
    : decl_diff_base(first, second, ctxt),
      priv_(new priv)
  {priv_->type_diff_ = type_diff;}
  
  /// Finish building the current instance of @ref var_diff.
  void
  var_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// Getter for the first @ref var_decl of the diff.
  ///
  /// @return the first @ref var_decl of the diff.
  var_decl_sptr
  var_diff::first_var() const
  {return dynamic_pointer_cast<var_decl>(first_subject());}
  
  /// Getter for the second @ref var_decl of the diff.
  ///
  /// @return the second @ref var_decl of the diff.
  var_decl_sptr
  var_diff::second_var() const
  {return dynamic_pointer_cast<var_decl>(second_subject());}
  
  /// Getter for the diff of the types of the instances of @ref
  /// var_decl.
  ///
  /// @return the diff of the types of the instances of @ref var_decl.
  diff_sptr
  var_diff::type_diff() const
  {
    if (diff_sptr result = priv_->type_diff_.lock())
      return result;
    else
      {
        result = compute_diff(first_var()->get_type(),
  			    second_var()->get_type(),
  			    context());
        context()->keep_diff_alive(result);
        priv_->type_diff_ = result;
        return result;
      }
  }
  
  /// Return true iff the diff node has a change.
  ///
  /// @return true iff the diff node has a change.
  bool
  var_diff::has_changes() const
  {return *first_var() != *second_var();}
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  var_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_var(), *second_var(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// Report the diff in a serialized form.
  ///
  /// @param out the stream to serialize the diff to.
  ///
  /// @param indent the prefix to use for the indentation of this
  /// serialization.
  void
  var_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Compute the diff between two instances of @ref var_decl.
  ///
  /// Note that the two decls must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the first @ref var_decl to consider for the diff.
  ///
  /// @param second the second @ref var_decl to consider for the diff.
  ///
  /// @param ctxt the diff context to use.
  ///
  /// @return the resulting diff between the two @ref var_decl.
  var_diff_sptr
  compute_diff(const var_decl_sptr	first,
  	     const var_decl_sptr	second,
  	     diff_context_sptr		ctxt)
  {
    if (first && second)
      ABG_ASSERT(first->get_environment() == second->get_environment());
  
    var_diff_sptr d(new var_diff(first, second, diff_sptr(), ctxt));
    ctxt->initialize_canonical_diff(d);
  
    return d;
  }
  
  // </var_diff stuff>
  
  // <pointer_type_def stuff>
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref pointer_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  pointer_diff::chain_into_hierarchy()
  {append_child_node(underlying_type_diff());}
  
  /// Constructor for a pointer_diff.
  ///
  /// @param first the first pointer to consider for the diff.
  ///
  /// @param second the secon pointer to consider for the diff.
  ///
  /// @param ctxt the diff context to use.
  pointer_diff::pointer_diff(pointer_type_def_sptr	first,
  			   pointer_type_def_sptr	second,
  			   diff_sptr			underlying,
  			   diff_context_sptr		ctxt)
    : type_diff_base(first, second, ctxt),
      priv_(new priv(underlying))
  {}
  
  /// Finish building the current instance of @ref pointer_diff.
  void
  pointer_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// Getter for the first subject of a pointer diff
  ///
  /// @return the first pointer considered in this pointer diff.
  const pointer_type_def_sptr
  pointer_diff::first_pointer() const
  {return dynamic_pointer_cast<pointer_type_def>(first_subject());}
  
  /// Getter for the second subject of a pointer diff
  ///
  /// @return the second pointer considered in this pointer diff.
  const pointer_type_def_sptr
  pointer_diff::second_pointer() const
  {return dynamic_pointer_cast<pointer_type_def>(second_subject());}
  
  /// @return the pretty represenation for the current instance of @ref
  /// pointer_diff.
  const string&
  pointer_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "pointer_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Return true iff the current diff node carries a change.
  ///
  /// @return true iff the current diff node carries a change.
  bool
  pointer_diff::has_changes() const
  {return first_pointer() != second_pointer();}
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  pointer_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_pointer(), *second_pointer(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// Getter for the diff between the pointed-to types of the pointers
  /// of this diff.
  ///
  /// @return the diff between the pointed-to types.
  diff_sptr
  pointer_diff::underlying_type_diff() const
  {return priv_->underlying_type_diff_;}
  
  /// Setter for the diff between the pointed-to types of the pointers
  /// of this diff.
  ///
  /// @param d the new diff between the pointed-to types of the pointers
  /// of this diff.
  void
  pointer_diff::underlying_type_diff(const diff_sptr d)
  {priv_->underlying_type_diff_ = d;}
  
  /// Report the diff in a serialized form.
  ///
  /// @param out the stream to serialize the diff to.
  ///
  /// @param indent the prefix to use for the indentation of this
  /// serialization.
  void
  pointer_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Compute the diff between between two pointers.
  ///
  /// Note that the two types must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the pointer to consider for the diff.
  ///
  /// @param second the pointer to consider for the diff.
  ///
  /// @return the resulting diff between the two pointers.
  ///
  /// @param ctxt the diff context to use.
  pointer_diff_sptr
  compute_diff(pointer_type_def_sptr	first,
  	     pointer_type_def_sptr	second,
  	     diff_context_sptr		ctxt)
  {
    if (first && second)
      ABG_ASSERT(first->get_environment() == second->get_environment());
  
    diff_sptr d = compute_diff_for_types(first->get_pointed_to_type(),
  				       second->get_pointed_to_type(),
  				       ctxt);
    pointer_diff_sptr result(new pointer_diff(first, second, d, ctxt));
    ctxt->initialize_canonical_diff(result);
  
    return result;
  }
  
  // </pointer_type_def>
  
  // <array_type_def>
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref array_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  array_diff::chain_into_hierarchy()
  {append_child_node(element_type_diff());}
  
  /// Constructor for array_diff
  ///
  /// @param first the first array_type of the diff.
  ///
  /// @param second the second array_type of the diff.
  ///
  /// @param element_type_diff the diff between the two array element
  /// types.
  ///
  /// @param ctxt the diff context to use.
  array_diff::array_diff(const array_type_def_sptr	first,
  		       const array_type_def_sptr	second,
  		       diff_sptr			element_type_diff,
  		       diff_context_sptr		ctxt)
    : type_diff_base(first, second, ctxt),
      priv_(new priv(element_type_diff))
  {}
  
  /// Finish building the current instance of @ref array_diff.
  void
  array_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// Getter for the first array of the diff.
  ///
  /// @return the first array of the diff.
  const array_type_def_sptr
  array_diff::first_array() const
  {return dynamic_pointer_cast<array_type_def>(first_subject());}
  
  /// Getter for the second array of the diff.
  ///
  /// @return for the second array of the diff.
  const array_type_def_sptr
  array_diff::second_array() const
  {return dynamic_pointer_cast<array_type_def>(second_subject());}
  
  /// Getter for the diff between the two types of array elements.
  ///
  /// @return the diff between the two types of array elements.
  const diff_sptr&
  array_diff::element_type_diff() const
  {return priv_->element_type_diff_;}
  
  /// Setter for the diff between the two array element types.
  ///
  /// @param d the new diff betweend the two array element types.
  void
  array_diff::element_type_diff(diff_sptr d)
  {priv_->element_type_diff_ = d;}
  
  /// @return the pretty representation for the current instance of @ref
  /// array_diff.
  const string&
  array_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "array_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Return true iff the current diff node carries a change.
  ///
  /// @return true iff the current diff node carries a change.
  bool
  array_diff::has_changes() const
  {
    bool l = false;
  
    //  the array element types match check for differing dimensions
    //  etc...
    array_type_def_sptr
      f = dynamic_pointer_cast<array_type_def>(first_subject()),
      s = dynamic_pointer_cast<array_type_def>(second_subject());
  
    if (f->get_name() != s->get_name())
      l |= true;
    if (f->get_size_in_bits() != s->get_size_in_bits())
      l |= true;
    if (f->get_alignment_in_bits() != s->get_alignment_in_bits())
      l |= true;
  
    l |=  element_type_diff()
      ? element_type_diff()->has_changes()
      : false;
  
    return l;
  }
  
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  array_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_array(), *second_array(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// Report the diff in a serialized form.
  ///
  /// @param out the output stream to serialize the dif to.
  ///
  /// @param indent the string to use for indenting the report.
  void
  array_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Compute the diff between two arrays.
  ///
  /// Note that the two types must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the first array to consider for the diff.
  ///
  /// @param second the second array to consider for the diff.
  ///
  /// @param ctxt the diff context to use.
  array_diff_sptr
  compute_diff(array_type_def_sptr	first,
  	     array_type_def_sptr	second,
  	     diff_context_sptr		ctxt)
  {
    if (first && second)
      ABG_ASSERT(first->get_environment() == second->get_environment());
  
    diff_sptr d = compute_diff_for_types(first->get_element_type(),
  				       second->get_element_type(),
  				       ctxt);
    array_diff_sptr result(new array_diff(first, second, d, ctxt));
    ctxt->initialize_canonical_diff(result);
    return result;
  }
  // </array_type_def>
  
  // <reference_type_def>
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref reference_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  reference_diff::chain_into_hierarchy()
  {append_child_node(underlying_type_diff());}
  
  /// Constructor for reference_diff
  ///
  /// @param first the first reference_type of the diff.
  ///
  /// @param second the second reference_type of the diff.
  ///
  /// @param ctxt the diff context to use.
  reference_diff::reference_diff(const reference_type_def_sptr	first,
  			       const reference_type_def_sptr	second,
  			       diff_sptr			underlying,
  			       diff_context_sptr		ctxt)
    : type_diff_base(first, second, ctxt),
  	priv_(new priv(underlying))
  {}
  
  /// Finish building the current instance of @ref reference_diff.
  void
  reference_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// Getter for the first reference of the diff.
  ///
  /// @return the first reference of the diff.
  reference_type_def_sptr
  reference_diff::first_reference() const
  {return dynamic_pointer_cast<reference_type_def>(first_subject());}
  
  /// Getter for the second reference of the diff.
  ///
  /// @return for the second reference of the diff.
  reference_type_def_sptr
  reference_diff::second_reference() const
  {return dynamic_pointer_cast<reference_type_def>(second_subject());}
  
  
  /// Getter for the diff between the two referred-to types.
  ///
  /// @return the diff between the two referred-to types.
  const diff_sptr&
  reference_diff::underlying_type_diff() const
  {return priv_->underlying_type_diff_;}
  
  /// Setter for the diff between the two referred-to types.
  ///
  /// @param d the new diff betweend the two referred-to types.
  diff_sptr&
  reference_diff::underlying_type_diff(diff_sptr d)
  {
    priv_->underlying_type_diff_ = d;
    return priv_->underlying_type_diff_;
  }
  
  /// @return the pretty representation for the current instance of @ref
  /// reference_diff.
  const string&
  reference_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "reference_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Return true iff the current diff node carries a change.
  ///
  /// @return true iff the current diff node carries a change.
  bool
  reference_diff::has_changes() const
  {
    return first_reference() != second_reference();
  }
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  reference_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_reference(), *second_reference(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// Report the diff in a serialized form.
  ///
  /// @param out the output stream to serialize the dif to.
  ///
  /// @param indent the string to use for indenting the report.
  void
  reference_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Compute the diff between two references.
  ///
  /// Note that the two types must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the first reference to consider for the diff.
  ///
  /// @param second the second reference to consider for the diff.
  ///
  /// @param ctxt the diff context to use.
  reference_diff_sptr
  compute_diff(reference_type_def_sptr	first,
  	     reference_type_def_sptr	second,
  	     diff_context_sptr		ctxt)
  {
    if (first && second)
      ABG_ASSERT(first->get_environment() == second->get_environment());
  
    diff_sptr d = compute_diff_for_types(first->get_pointed_to_type(),
  				       second->get_pointed_to_type(),
  				       ctxt);
    reference_diff_sptr result(new reference_diff(first, second, d, ctxt));
    ctxt->initialize_canonical_diff(result);
    return result;
  }
  // </reference_type_def>
  
  // <qualified_type_diff stuff>
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref qualified_type_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  qualified_type_diff::chain_into_hierarchy()
  {append_child_node(leaf_underlying_type_diff());}
  
  /// Constructor for qualified_type_diff.
  ///
  /// @param first the first qualified type of the diff.
  ///
  /// @param second the second qualified type of the diff.
  ///
  /// @param ctxt the diff context to use.
  qualified_type_diff::qualified_type_diff(qualified_type_def_sptr	first,
  					 qualified_type_def_sptr	second,
  					 diff_sptr			under,
  					 diff_context_sptr		ctxt)
    : type_diff_base(first, second, ctxt),
      priv_(new priv(under))
  {}
  
  /// Finish building the current instance of @ref qualified_type_diff.
  void
  qualified_type_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// Getter for the first qualified type of the diff.
  ///
  /// @return the first qualified type of the diff.
  const qualified_type_def_sptr
  qualified_type_diff::first_qualified_type() const
  {return dynamic_pointer_cast<qualified_type_def>(first_subject());}
  
  /// Getter for the second qualified type of the diff.
  ///
  /// @return the second qualified type of the diff.
  const qualified_type_def_sptr
  qualified_type_diff::second_qualified_type() const
  {return dynamic_pointer_cast<qualified_type_def>(second_subject());}
  
  /// Getter for the diff between the underlying types of the two
  /// qualified types.
  ///
  /// @return the diff between the underlying types of the two qualified
  /// types.
  diff_sptr
  qualified_type_diff::underlying_type_diff() const
  {return priv_->underlying_type_diff;}
  
  /// Getter for the diff between the most underlying non-qualified
  /// types of two qualified types.
  ///
  /// @return the diff between the most underlying non-qualified types
  /// of two qualified types.
  diff_sptr
  qualified_type_diff::leaf_underlying_type_diff() const
  {
    if (!priv_->leaf_underlying_type_diff)
      priv_->leaf_underlying_type_diff
        = compute_diff_for_types(get_leaf_type(first_qualified_type()),
  			       get_leaf_type(second_qualified_type()),
  			       context());
  
    return priv_->leaf_underlying_type_diff;
  }
  
  /// Setter for the diff between the underlying types of the two
  /// qualified types.
  ///
  /// @return the diff between the underlying types of the two qualified
  /// types.
  void
  qualified_type_diff::underlying_type_diff(const diff_sptr d)
  {priv_->underlying_type_diff = d;}
  
  /// @return the pretty representation of the current instance of @ref
  /// qualified_type_diff.
  const string&
  qualified_type_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "qualified_type_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Return true iff the current diff node carries a change.
  ///
  /// @return true iff the current diff node carries a change.
  bool
  qualified_type_diff::has_changes() const
  {return first_qualified_type() != second_qualified_type();}
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  qualified_type_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_qualified_type(), *second_qualified_type(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// Report the diff in a serialized form.
  ///
  /// @param out the output stream to serialize to.
  ///
  /// @param indent the string to use to indent the lines of the report.
  void
  qualified_type_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Compute the diff between two qualified types.
  ///
  /// Note that the two types must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the first qualified type to consider for the diff.
  ///
  /// @param second the second qualified type to consider for the diff.
  ///
  /// @param ctxt the diff context to use.
  qualified_type_diff_sptr
  compute_diff(const qualified_type_def_sptr	first,
  	     const qualified_type_def_sptr	second,
  	     diff_context_sptr			ctxt)
  {
    if (first && second)
      ABG_ASSERT(first->get_environment() == second->get_environment());
  
    diff_sptr d = compute_diff_for_types(first->get_underlying_type(),
  				       second->get_underlying_type(),
  				       ctxt);
    qualified_type_diff_sptr result(new qualified_type_diff(first, second,
  							  d, ctxt));
    ctxt->initialize_canonical_diff(result);
    return result;
  }
  
  // </qualified_type_diff stuff>
  
  // <enum_diff stuff>
  
  /// Clear the lookup tables useful for reporting an enum_diff.
  ///
  /// This function must be updated each time a lookup table is added or
  /// removed from the class_diff::priv.
  void
  enum_diff::clear_lookup_tables()
  {
    priv_->deleted_enumerators_.clear();
    priv_->inserted_enumerators_.clear();
    priv_->changed_enumerators_.clear();
  }
  
  /// Tests if the lookup tables are empty.
  ///
  /// @return true if the lookup tables are empty, false otherwise.
  bool
  enum_diff::lookup_tables_empty() const
  {
    return (priv_->deleted_enumerators_.empty()
  	  && priv_->inserted_enumerators_.empty()
  	  && priv_->changed_enumerators_.empty());
  }
  
  /// If the lookup tables are not yet built, walk the differences and
  /// fill the lookup tables.
  void
  enum_diff::ensure_lookup_tables_populated()
  {
    if (!lookup_tables_empty())
      return;
  
    {
      edit_script e = priv_->enumerators_changes_;
  
      for (vector<deletion>::const_iterator it = e.deletions().begin();
  	 it != e.deletions().end();
  	 ++it)
        {
  	unsigned i = it->index();
  	const enum_type_decl::enumerator& n =
  	  first_enum()->get_enumerators()[i];
  	const string& name = n.get_name();
  	ABG_ASSERT(priv_->deleted_enumerators_.find(n.get_name())
  	       == priv_->deleted_enumerators_.end());
  	priv_->deleted_enumerators_[name] = n;
        }
  
      for (vector<insertion>::const_iterator it = e.insertions().begin();
  	 it != e.insertions().end();
  	 ++it)
        {
  	for (vector<unsigned>::const_iterator iit =
  	       it->inserted_indexes().begin();
  	     iit != it->inserted_indexes().end();
  	     ++iit)
  	  {
  	    unsigned i = *iit;
  	    const enum_type_decl::enumerator& n =
  	      second_enum()->get_enumerators()[i];
  	    const string& name = n.get_name();
  	    ABG_ASSERT(priv_->inserted_enumerators_.find(n.get_name())
  		   == priv_->inserted_enumerators_.end());
  	    string_enumerator_map::const_iterator j =
  	      priv_->deleted_enumerators_.find(name);
  	    if (j == priv_->deleted_enumerators_.end())
  	      priv_->inserted_enumerators_[name] = n;
  	    else
  	      {
  		if (j->second != n)
  		  priv_->changed_enumerators_[j->first] =
  		    std::make_pair(j->second, n);
  		priv_->deleted_enumerators_.erase(j);
  	      }
  	  }
        }
    }
  }
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref enum_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  enum_diff::chain_into_hierarchy()
  {append_child_node(underlying_type_diff());}
  
  /// Constructor for enum_diff.
  ///
  /// @param first the first enum type of the diff.
  ///
  /// @param second the second enum type of the diff.
  ///
  /// @param underlying_type_diff the diff of the two underlying types
  /// of the two enum types.
  ///
  /// @param ctxt the diff context to use.
  enum_diff::enum_diff(const enum_type_decl_sptr	first,
  		     const enum_type_decl_sptr	second,
  		     const diff_sptr		underlying_type_diff,
  		     const diff_context_sptr	ctxt)
    : type_diff_base(first, second, ctxt),
      priv_(new priv(underlying_type_diff))
  {}
  
  /// Finish building the current instance of @ref enum_diff.
  void
  enum_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// @return the first enum of the diff.
  const enum_type_decl_sptr
  enum_diff::first_enum() const
  {return dynamic_pointer_cast<enum_type_decl>(first_subject());}
  
  /// @return the second enum of the diff.
  const enum_type_decl_sptr
  enum_diff::second_enum() const
  {return dynamic_pointer_cast<enum_type_decl>(second_subject());}
  
  /// @return the diff of the two underlying enum types.
  diff_sptr
  enum_diff::underlying_type_diff() const
  {return priv_->underlying_type_diff_;}
  
  /// @return a map of the enumerators that were deleted.
  const string_enumerator_map&
  enum_diff::deleted_enumerators() const
  {return priv_->deleted_enumerators_;}
  
  /// @return a map of the enumerators that were inserted
  const string_enumerator_map&
  enum_diff::inserted_enumerators() const
  {return priv_->inserted_enumerators_;}
  
  /// @return a map of the enumerators that were changed
  const string_changed_enumerator_map&
  enum_diff::changed_enumerators() const
  {return priv_->changed_enumerators_;}
  
  /// @return the pretty representation of the current instance of @ref
  /// enum_diff.
  const string&
  enum_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "enum_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Return true iff the current diff node carries a change.
  ///
  /// @return true iff the current diff node carries a change.
  bool
  enum_diff::has_changes() const
  {return first_enum() != second_enum();}
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  enum_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_enum(), *second_enum(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// Report the differences between the two enums.
  ///
  /// @param out the output stream to send the report to.
  ///
  /// @param indent the string to use for indentation.
  void
  enum_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Compute the set of changes between two instances of @ref
  /// enum_type_decl.
  ///
  /// Note that the two types must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first a pointer to the first enum_type_decl to consider.
  ///
  /// @param second a pointer to the second enum_type_decl to consider.
  ///
  /// @return the resulting diff of the two enums @p first and @p
  /// second.
  ///
  /// @param ctxt the diff context to use.
  enum_diff_sptr
  compute_diff(const enum_type_decl_sptr first,
  	     const enum_type_decl_sptr second,
  	     diff_context_sptr ctxt)
  {
    if (first && second)
      ABG_ASSERT(first->get_environment() == second->get_environment());
  
    diff_sptr ud = compute_diff_for_types(first->get_underlying_type(),
  					second->get_underlying_type(),
  					ctxt);
    enum_diff_sptr d(new enum_diff(first, second, ud, ctxt));
  
    compute_diff(first->get_enumerators().begin(),
  	       first->get_enumerators().end(),
  	       second->get_enumerators().begin(),
  	       second->get_enumerators().end(),
  	       d->priv_->enumerators_changes_);
  
    d->ensure_lookup_tables_populated();
  
    ctxt->initialize_canonical_diff(d);
  
    return d;
  }
  // </enum_diff stuff>
  
  // <class_or_union_diff stuff>
  
  /// Test if the current diff node carries a member type change for a
  /// member type which name is the same as the name of a given type
  /// declaration.
  ///
  /// @param d the type declaration which name should be equal to the
  /// name of the member type that might have changed.
  ///
  /// @return the member type that has changed, iff there were a member
  /// type (which name is the same as the name of @p d) that changed.
  /// Note that the member type that is returned is the new value of the
  /// member type that changed.
  type_or_decl_base_sptr
  class_or_union_diff::priv::member_type_has_changed(decl_base_sptr d) const
  {
    string qname = d->get_qualified_name();
    string_diff_sptr_map::const_iterator it =
      changed_member_types_.find(qname);
  
    return ((it == changed_member_types_.end())
  	  ? type_or_decl_base_sptr()
  	  : it->second->second_subject());
  }
  
  /// Test if the current diff node carries a data member change for a
  /// data member which name is the same as the name of a given type
  /// declaration.
  ///
  /// @param d the type declaration which name should be equal to the
  /// name of the data member that might have changed.
  ///
  /// @return the data member that has changed, iff there were a data
  /// member type (which name is the same as the name of @p d) that
  /// changed.  Note that the data member that is returned is the new
  /// value of the data member that changed.
  decl_base_sptr
  class_or_union_diff::priv::subtype_changed_dm(decl_base_sptr d) const
  {
    string qname = d->get_qualified_name();
    string_var_diff_sptr_map::const_iterator it =
      subtype_changed_dm_.find(qname);
  
    if (it == subtype_changed_dm_.end())
      return decl_base_sptr();
    return it->second->second_var();
  }
  
  /// Test if the current diff node carries a member class template
  /// change for a member class template which name is the same as the
  /// name of a given type declaration.
  ///
  /// @param d the type declaration which name should be equal to the
  /// name of the member class template that might have changed.
  ///
  /// @return the member class template that has changed, iff there were
  /// a member class template (which name is the same as the name of @p
  /// d) that changed.  Note that the member class template that is
  /// returned is the new value of the member class template that
  /// changed.
  decl_base_sptr
  class_or_union_diff::priv::member_class_tmpl_has_changed(decl_base_sptr d) const
  {
    string qname = d->get_qualified_name();
    string_diff_sptr_map::const_iterator it =
      changed_member_class_tmpls_.find(qname);
  
    return ((it == changed_member_class_tmpls_.end())
  	  ? decl_base_sptr()
  	  : dynamic_pointer_cast<decl_base>(it->second->second_subject()));
  }
  
  /// Get the number of non static data members that were deleted.
  ///
  /// @return the number of non static data members that were deleted.
  size_t
  class_or_union_diff::priv::get_deleted_non_static_data_members_number() const
  {
    size_t result = 0;
  
    for (string_decl_base_sptr_map::const_iterator i =
  	 deleted_data_members_.begin();
         i != deleted_data_members_.end();
         ++i)
      if (is_member_decl(i->second)
  	&& !get_member_is_static(i->second))
        ++result;
  
    return result;
  }
  
  /// Get the number of non static data members that were inserted.
  ///
  /// @return the number of non static data members that were inserted.
  size_t
  class_or_union_diff::priv::get_inserted_non_static_data_members_number() const
  {
    size_t result = 0;
  
    for (string_decl_base_sptr_map::const_iterator i =
  	 inserted_data_members_.begin();
         i != inserted_data_members_.end();
         ++i)
      if (is_member_decl(i->second)
  	&& !get_member_is_static(i->second))
        ++result;
  
    return result;
  }
  
  /// Get the number of data member sub-type changes carried by the
  /// current diff node that were filtered out.
  ///
  /// @param local_only if true, it means that only (filtered) local
  /// changes are considered.
  ///
  /// @return the number of data member sub-type changes carried by the
  /// current diff node that were filtered out.
  size_t
  class_or_union_diff::priv::count_filtered_subtype_changed_dm(bool local_only)
  {
    size_t num_filtered= 0;
    for (var_diff_sptrs_type::const_iterator i =
  	 sorted_subtype_changed_dm_.begin();
         i != sorted_subtype_changed_dm_.end();
         ++i)
      {
        if (local_only)
  	{
  	  if ((*i)->has_changes()
  	      && !(*i)->has_local_changes_to_be_reported())
  	    ++num_filtered;
  	}
        else
  	{
  	  if ((*i)->is_filtered_out())
  	    ++num_filtered;
  	}
      }
    return num_filtered;
  }
  
  /// Get the number of data member changes carried by the current diff
  /// node that were filtered out.
  ///
  /// @param local_only if true, it means that only (filtered) local
  /// changes are considered.
  ///
  /// @return the number of data member changes carried by the current
  /// diff node that were filtered out.
  size_t
  class_or_union_diff::priv::count_filtered_changed_dm(bool local_only)
  {
    size_t num_filtered= 0;
  
    for (unsigned_var_diff_sptr_map::const_iterator i = changed_dm_.begin();
         i != changed_dm_.end();
         ++i)
      {
        diff_sptr diff = i->second;
        if (local_only)
  	{
  	  if ((diff->has_changes() && !diff->has_local_changes_to_be_reported())
  	      || diff->is_filtered_out())
  	    ++num_filtered;
  	}
        else
  	{
  	  if (diff->is_filtered_out())
  	    ++num_filtered;
  	}
      }
    return num_filtered;
  }
  
  /// Skip the processing of the current member function if its
  /// virtual-ness is disallowed by the user.
  ///
  /// This is to be used in the member functions below that are used to
  /// count the number of filtered inserted, deleted and changed member
  /// functions.
  #define SKIP_MEM_FN_IF_VIRTUALITY_DISALLOWED				\
    do {									\
      if (get_member_function_is_virtual(f)					\
  	|| get_member_function_is_virtual(s))				\
        {								\
  	if (!(allowed_category | VIRTUAL_MEMBER_CHANGE_CATEGORY))	\
  	  continue;							\
        }								\
      else								\
        {								\
  	if (!(allowed_category | NON_VIRT_MEM_FUN_CHANGE_CATEGORY))	\
  	  continue;							\
        }								\
    } while (false)
  
  /// Get the number of member functions changes carried by the current
  /// diff node that were filtered out.
  ///
  /// @return the number of member functions changes carried by the
  /// current diff node that were filtered out.
  size_t
  class_or_union_diff::priv::count_filtered_changed_mem_fns
  (const diff_context_sptr& ctxt)
  {
    size_t count = 0;
    diff_category allowed_category = ctxt->get_allowed_category();
  
    for (function_decl_diff_sptrs_type::const_iterator i =
  	 sorted_changed_member_functions_.begin();
         i != sorted_changed_member_functions_.end();
         ++i)
      {
        method_decl_sptr f =
  	dynamic_pointer_cast<method_decl>
  	((*i)->first_function_decl());
        ABG_ASSERT(f);
  
        method_decl_sptr s =
  	dynamic_pointer_cast<method_decl>
  	((*i)->second_function_decl());
        ABG_ASSERT(s);
  
        SKIP_MEM_FN_IF_VIRTUALITY_DISALLOWED;
  
        diff_sptr diff = *i;
        ctxt->maybe_apply_filters(diff);
  
        if (diff->is_filtered_out())
  	++count;
      }
  
    return count;
  }
  
  /// Get the number of member functions insertions carried by the current
  /// diff node that were filtered out.
  ///
  /// @return the number of member functions insertions carried by the
  /// current diff node that were filtered out.
  size_t
  class_or_union_diff::priv::count_filtered_inserted_mem_fns
  (const diff_context_sptr& ctxt)
  {
      size_t count = 0;
    diff_category allowed_category = ctxt->get_allowed_category();
  
    for (string_member_function_sptr_map::const_iterator i =
  	 inserted_member_functions_.begin();
         i != inserted_member_functions_.end();
         ++i)
      {
        method_decl_sptr f = i->second,
  	s = i->second;
  
        SKIP_MEM_FN_IF_VIRTUALITY_DISALLOWED;
  
        diff_sptr diff = compute_diff_for_decls(f, s, ctxt);
        ctxt->maybe_apply_filters(diff);
  
        if (diff->get_category() != NO_CHANGE_CATEGORY
  	  && diff->is_filtered_out())
  	++count;
      }
  
    return count;
  }
  
  /// Get the number of member functions deletions carried by the current
  /// diff node that were filtered out.
  ///
  /// @return the number of member functions deletions carried by the
  /// current diff node that were filtered out.
  size_t
  class_or_union_diff::priv::count_filtered_deleted_mem_fns
  (const diff_context_sptr& ctxt)
  {
    size_t count = 0;
    diff_category allowed_category = ctxt->get_allowed_category();
  
    for (string_member_function_sptr_map::const_iterator i =
  	 deleted_member_functions_.begin();
         i != deleted_member_functions_.end();
         ++i)
      {
        method_decl_sptr f = i->second,
  	s = i->second;
  
        SKIP_MEM_FN_IF_VIRTUALITY_DISALLOWED;
  
        diff_sptr diff = compute_diff_for_decls(f, s, ctxt);
        ctxt->maybe_apply_filters(diff);
  
        if (diff->get_category() != NO_CHANGE_CATEGORY
  	  && diff->is_filtered_out())
  	++count;
      }
  
    return count;
  }
  
  /// Clear the lookup tables useful for reporting.
  ///
  /// This function must be updated each time a lookup table is added or
  /// removed from the class_or_union_diff::priv.
  void
  class_or_union_diff::clear_lookup_tables()
  {
    priv_->deleted_member_types_.clear();
    priv_->inserted_member_types_.clear();
    priv_->changed_member_types_.clear();
    priv_->deleted_data_members_.clear();
    priv_->inserted_data_members_.clear();
    priv_->subtype_changed_dm_.clear();
    priv_->deleted_member_functions_.clear();
    priv_->inserted_member_functions_.clear();
    priv_->changed_member_functions_.clear();
    priv_->deleted_member_class_tmpls_.clear();
    priv_->inserted_member_class_tmpls_.clear();
    priv_->changed_member_class_tmpls_.clear();
  }
  
  /// Tests if the lookup tables are empty.
  ///
  /// @return true if the lookup tables are empty, false otherwise.
  bool
  class_or_union_diff::lookup_tables_empty(void) const
  {
    return (priv_->deleted_member_types_.empty()
  	  && priv_->inserted_member_types_.empty()
  	  && priv_->changed_member_types_.empty()
  	  && priv_->deleted_data_members_.empty()
  	  && priv_->inserted_data_members_.empty()
  	  && priv_->subtype_changed_dm_.empty()
  	  && priv_->inserted_member_functions_.empty()
  	  && priv_->deleted_member_functions_.empty()
  	  && priv_->changed_member_functions_.empty()
  	  && priv_->deleted_member_class_tmpls_.empty()
  	  && priv_->inserted_member_class_tmpls_.empty()
  	  && priv_->changed_member_class_tmpls_.empty());
  }
  
  /// If the lookup tables are not yet built, walk the differences and
  /// fill them.
  void
  class_or_union_diff::ensure_lookup_tables_populated(void) const
  {
    {
      edit_script& e = priv_->member_types_changes_;
  
      for (vector<deletion>::const_iterator it = e.deletions().begin();
  	 it != e.deletions().end();
  	 ++it)
        {
  	unsigned i = it->index();
  	decl_base_sptr d =
  	  get_type_declaration(first_class_or_union()->get_member_types()[i]);
  	class_or_union_sptr record_type = is_class_or_union_type(d);
  	if (record_type && record_type->get_is_declaration_only())
  	  continue;
  	string name = d->get_name();
  	priv_->deleted_member_types_[name] = d;
        }
  
      for (vector<insertion>::const_iterator it = e.insertions().begin();
  	 it != e.insertions().end();
  	 ++it)
        {
  	for (vector<unsigned>::const_iterator iit =
  	       it->inserted_indexes().begin();
  	     iit != it->inserted_indexes().end();
  	     ++iit)
  	  {
  	    unsigned i = *iit;
  	    decl_base_sptr d =
  	      get_type_declaration(second_class_or_union()->get_member_types()[i]);
  	    class_or_union_sptr record_type = is_class_or_union_type(d);
  	    if (record_type && record_type->get_is_declaration_only())
  	      continue;
  	    string name = d->get_name();
  	    string_decl_base_sptr_map::const_iterator j =
  	      priv_->deleted_member_types_.find(name);
  	    if (j != priv_->deleted_member_types_.end())
  	      {
  		if (*j->second != *d)
  		  priv_->changed_member_types_[name] =
  		    compute_diff(j->second, d, context());
  
  		priv_->deleted_member_types_.erase(j);
  	      }
  	    else
  	      priv_->inserted_member_types_[name] = d;
  	  }
        }
    }
  
    {
      edit_script& e = priv_->data_members_changes_;
  
      for (vector<deletion>::const_iterator it = e.deletions().begin();
  	 it != e.deletions().end();
  	 ++it)
        {
  	unsigned i = it->index();
  	var_decl_sptr data_member =
  	  is_var_decl(first_class_or_union()->get_non_static_data_members()[i]);
  	string name = data_member->get_anon_dm_reliable_name();
  
  	ABG_ASSERT(priv_->deleted_data_members_.find(name)
  		   == priv_->deleted_data_members_.end());
  	priv_->deleted_data_members_[name] = data_member;
        }
  
      for (vector<insertion>::const_iterator it = e.insertions().begin();
  	 it != e.insertions().end();
  	 ++it)
        {
  	for (vector<unsigned>::const_iterator iit =
  	       it->inserted_indexes().begin();
  	     iit != it->inserted_indexes().end();
  	     ++iit)
  	  {
  	    unsigned i = *iit;
  	    decl_base_sptr d =
  	      second_class_or_union()->get_non_static_data_members()[i];
  	    var_decl_sptr added_dm = is_var_decl(d);
  	    string name = added_dm->get_anon_dm_reliable_name();
  	    ABG_ASSERT(priv_->inserted_data_members_.find(name)
  		       == priv_->inserted_data_members_.end());
  
  	    bool ignore_added_anonymous_data_member = false;
  	    if (is_anonymous_data_member(added_dm))
  	      {
  		//
  		// Handle insertion of anonymous data member to
  		// replace existing data members.
  		//
  		// For instance consider this:
  		//   struct S
  		//   {
  		//     int a;
  		//     int b;
  		//     int c;
  		//   };// end struct S
  		//
  		//   Where the data members 'a' and 'b' are replaced
  		//   by an anonymous data member without changing the
  		//   effective bit layout of the structure:
  		//
  		//   struct S
  		//   {
  		//     struct
  		//     {
  		//       union
  		//       {
  		//         int a;
  		//         char a_1;
  		//       };
  		//       union
  		//       {
  		//         int b;
  		//         char b_1;
  		//       };
  		//     };
  		//     int c;
  		//   }; // end struct S
  		//
  		var_decl_sptr replaced_dm, replacing_dm;
  		bool added_anon_dm_changes_dm = false;
  		// The vector of data members replaced by anonymous
  		// data members.
  		vector<var_decl_sptr> dms_replaced_by_anon_dm;
  
  		//
  		// Let's start collecting the set of data members
  		// which have been replaced by anonymous types in a
  		// harmless way.  These are going to be collected into
  		// dms_replaced_by_anon_dm and, ultimately, into
  		// priv_->dms_replaced_by_adms_
  		//
  		for (string_decl_base_sptr_map::const_iterator it =
  		       priv_->deleted_data_members_.begin();
  		     it != priv_->deleted_data_members_.end();
  		     ++it)
  		  {
  		    // We don't support this pattern for anonymous
  		    // data members themselves being replaced.  If
  		    // that occurs then we'll just report it verbatim.
  		    if (is_anonymous_data_member(it->second))
  		      continue;
  
  		    string deleted_dm_name = it->second->get_name();
  		    if ((replacing_dm =
  			 find_data_member_from_anonymous_data_member(added_dm,
  								     deleted_dm_name)))
  		      {
  			// So it looks like replacing_dm might have
  			// replaced the data member which name is
  			// 'deleted_dm_name'.  Let's look deeper to be
  			// sure.
  			//
  			// Note that replacing_dm is part (member) of
  			// an anonymous data member that might replace
  			// replaced_dm.
  
  			// So let's get that replaced data member.
  			replaced_dm = is_var_decl(it->second);
  			size_t replaced_dm_offset =
  			  get_data_member_offset(replaced_dm),
  			replacing_dm_offset =
  			  get_absolute_data_member_offset(replacing_dm);
  
  			if (replaced_dm_offset != replacing_dm_offset)
  			  {
  			    // So the replacing data member and the
  			    // replaced data member don't have the
  			    // same offset.  This is not the pattern we
  			    // are looking for.  Rather, it looks like
  			    // the anonymous data member has *changed*
  			    // the data member.
  			    added_anon_dm_changes_dm = true;
  			    break;
  			  }
  
  			if (replaced_dm->get_type()->get_size_in_bits()
  			    == replaced_dm->get_type()->get_size_in_bits())
  			  dms_replaced_by_anon_dm.push_back(replaced_dm);
  			else
  			  {
  			    added_anon_dm_changes_dm = true;
  			    break;
  			  }
  		      }
  		  }
  
  		// Now walk dms_replaced_by_anon_dm to fill up
  		// priv_->dms_replaced_by_adms_ with the set of data
  		// members replaced by anonymous data members.
  		if (!added_anon_dm_changes_dm
  		    && !dms_replaced_by_anon_dm.empty())
  		  {
  		    // See if the added data member isn't too big.
  		    type_base_sptr added_dm_type = added_dm->get_type();
  		    ABG_ASSERT(added_dm_type);
  		    var_decl_sptr new_next_dm =
  		      get_next_data_member(second_class_or_union(),
  					   added_dm);
  		    var_decl_sptr old_next_dm =
  		      first_class_or_union()->find_data_member(new_next_dm);
  
  		    if (!old_next_dm
  			|| (old_next_dm
  			    && (get_absolute_data_member_offset(old_next_dm)
  				== get_absolute_data_member_offset(new_next_dm))))
  		      {
  			// None of the data members that are replaced
  			// by the added union should be considered as
  			// having been deleted.
  			ignore_added_anonymous_data_member = true;
  			for (vector<var_decl_sptr>::const_iterator i =
  			       dms_replaced_by_anon_dm.begin();
  			     i != dms_replaced_by_anon_dm.end();
  			     ++i)
  			  {
  			    string n = (*i)->get_name();
  			    priv_->dms_replaced_by_adms_[n] =
  			      added_dm;
  			    priv_->deleted_data_members_.erase(n);
  			  }
  		      }
  		  }
  	      }
  
  	    if (!ignore_added_anonymous_data_member)
  	      {
  		// Detect changed data members.
  		//
  		// A changed data member (that we shall name D) is a data
  		// member that satisfies the conditions below:
  		//
  		// 1/ It must have been added.
  		//
  		// 2/ It must have been deleted as well.
  		//
  		// 3/ It there must be a non-empty difference between the
  		// deleted D and the added D.
  		string_decl_base_sptr_map::const_iterator j =
  		  priv_->deleted_data_members_.find(name);
  		if (j != priv_->deleted_data_members_.end())
  		  {
  		    if (*j->second != *d)
  		      {
  			var_decl_sptr old_dm = is_var_decl(j->second);
  			priv_->subtype_changed_dm_[name]=
  			  compute_diff(old_dm, added_dm, context());
  		      }
  		    priv_->deleted_data_members_.erase(j);
  		  }
  		else
  		  priv_->inserted_data_members_[name] = d;
  	      }
  	  }
        }
  
      // Now detect when a data member is deleted from offset N and
      // another one is added to offset N.  In that case, we want to be
      // able to say that the data member at offset N changed.
      for (string_decl_base_sptr_map::const_iterator i =
  	   priv_->deleted_data_members_.begin();
  	 i != priv_->deleted_data_members_.end();
  	 ++i)
        {
  	unsigned offset = get_data_member_offset(i->second);
  	priv_->deleted_dm_by_offset_[offset] = i->second;
        }
  
      for (string_decl_base_sptr_map::const_iterator i =
  	   priv_->inserted_data_members_.begin();
  	 i != priv_->inserted_data_members_.end();
  	 ++i)
        {
  	unsigned offset = get_data_member_offset(i->second);
  	priv_->inserted_dm_by_offset_[offset] = i->second;
        }
  
      for (unsigned_decl_base_sptr_map::const_iterator i =
  	   priv_->inserted_dm_by_offset_.begin();
  	 i != priv_->inserted_dm_by_offset_.end();
  	 ++i)
        {
  	unsigned_decl_base_sptr_map::const_iterator j =
  	  priv_->deleted_dm_by_offset_.find(i->first);
  	if (j != priv_->deleted_dm_by_offset_.end())
  	  {
  	    var_decl_sptr old_dm = is_var_decl(j->second);
  	    var_decl_sptr new_dm = is_var_decl(i->second);
  	    priv_->changed_dm_[i->first] =
  	      compute_diff(old_dm, new_dm, context());
  	  }
        }
  
      for (unsigned_var_diff_sptr_map::const_iterator i =
  	   priv_->changed_dm_.begin();
  	 i != priv_->changed_dm_.end();
  	 ++i)
        {
  	priv_->deleted_dm_by_offset_.erase(i->first);
  	priv_->inserted_dm_by_offset_.erase(i->first);
  	priv_->deleted_data_members_.erase
  	  (i->second->first_var()->get_anon_dm_reliable_name());
  	priv_->inserted_data_members_.erase
  	  (i->second->second_var()->get_anon_dm_reliable_name());
        }
    }
    sort_string_data_member_diff_sptr_map(priv_->subtype_changed_dm_,
  					priv_->sorted_subtype_changed_dm_);
    sort_unsigned_data_member_diff_sptr_map(priv_->changed_dm_,
  					  priv_->sorted_changed_dm_);
  
    {
      edit_script& e = priv_->member_class_tmpls_changes_;
  
      for (vector<deletion>::const_iterator it = e.deletions().begin();
  	 it != e.deletions().end();
  	 ++it)
        {
  	unsigned i = it->index();
  	decl_base_sptr d =
  	  first_class_or_union()->get_member_class_templates()[i]->
  	  as_class_tdecl();
  	string name = d->get_name();
  	ABG_ASSERT(priv_->deleted_member_class_tmpls_.find(name)
  	       == priv_->deleted_member_class_tmpls_.end());
  	priv_->deleted_member_class_tmpls_[name] = d;
        }
  
      for (vector<insertion>::const_iterator it = e.insertions().begin();
  	 it != e.insertions().end();
  	 ++it)
        {
  	for (vector<unsigned>::const_iterator iit =
  	       it->inserted_indexes().begin();
  	     iit != it->inserted_indexes().end();
  	     ++iit)
  	  {
  	    unsigned i = *iit;
  	    decl_base_sptr d =
  	      second_class_or_union()->get_member_class_templates()[i]->
  	      as_class_tdecl();
  	    string name = d->get_name();
  	    ABG_ASSERT(priv_->inserted_member_class_tmpls_.find(name)
  		   == priv_->inserted_member_class_tmpls_.end());
  	    string_decl_base_sptr_map::const_iterator j =
  	      priv_->deleted_member_class_tmpls_.find(name);
  	    if (j != priv_->deleted_member_class_tmpls_.end())
  	      {
  		if (*j->second != *d)
  		  priv_->changed_member_types_[name]=
  		    compute_diff(j->second, d, context());
  		priv_->deleted_member_class_tmpls_.erase(j);
  	      }
  	    else
  	      priv_->inserted_member_class_tmpls_[name] = d;
  	  }
        }
    }
    sort_string_diff_sptr_map(priv_->changed_member_types_,
  			    priv_->sorted_changed_member_types_);
  }
  
  /// Allocate the memory for the priv_ pimpl data member of the @ref
  /// class_or_union_diff class.
  void
  class_or_union_diff::allocate_priv_data()
  {
    if (!priv_)
      priv_.reset(new priv);
  }
  
  /// Constructor for the @ref class_or_union_diff class.
  ///
  /// @param first_scope the first @ref class_or_union of the diff node.
  ///
  /// @param second_scope the second @ref class_or_union of the diff node.
  ///
  /// @param ctxt the context of the diff.
  class_or_union_diff::class_or_union_diff(class_or_union_sptr first_scope,
  					 class_or_union_sptr second_scope,
  					 diff_context_sptr ctxt)
    : type_diff_base(first_scope, second_scope, ctxt)
      //priv_(new priv)
  {}
  
  /// Finish building the current instance of @ref class_or_union_diff.
  void
  class_or_union_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// Getter of the private data of the @ref class_or_union_diff type.
  ///
  /// Note that due to an optimization, the private data of @ref
  /// class_or_union_diff can be shared among several instances of
  /// class_or_union_diff, so you should never try to access
  /// class_or_union_diff::priv directly.
  ///
  /// When class_or_union_diff::priv is shared, this function returns
  /// the correct shared one.
  ///
  /// @return the (possibly) shared private data of the current instance
  /// of @ref class_or_union_diff.
  const class_or_union_diff::priv_sptr&
  class_or_union_diff::get_priv() const
  {
    if (priv_)
      return priv_;
  
    // If the current class_or_union_diff::priv member is empty, then look for
    // the shared one, from the canonical type.
    class_or_union_diff *canonical =
      dynamic_cast<class_or_union_diff*>(get_canonical_diff());
    ABG_ASSERT(canonical);
    ABG_ASSERT(canonical->priv_);
  
    return canonical->priv_;
  }
  
  /// Destructor of class_or_union_diff.
  class_or_union_diff::~class_or_union_diff()
  {
  }
  
  /// @return the first @ref class_or_union involved in the diff.
  class_or_union_sptr
  class_or_union_diff::first_class_or_union() const
  {return is_class_or_union_type(first_subject());}
  
  /// @return the second @ref class_or_union involved in the diff.
  class_or_union_sptr
  class_or_union_diff::second_class_or_union() const
  {return is_class_or_union_type(second_subject());}
  
  /// @return the edit script of the member types of the two @ref
  /// class_or_union.
  const edit_script&
  class_or_union_diff::member_types_changes() const
  {return get_priv()->member_types_changes_;}
  
  /// @return the edit script of the member types of the two @ref
  /// class_or_union.
  edit_script&
  class_or_union_diff::member_types_changes()
  {return get_priv()->member_types_changes_;}
  
  /// @return the edit script of the data members of the two @ref
  /// class_or_union.
  const edit_script&
  class_or_union_diff::data_members_changes() const
  {return get_priv()->data_members_changes_;}
  
  /// @return the edit script of the data members of the two @ref
  /// class_or_union.
  edit_script&
  class_or_union_diff::data_members_changes()
  {return get_priv()->data_members_changes_;}
  
  /// Getter for the data members that got inserted.
  ///
  /// @return a map of data members that got inserted.
  const string_decl_base_sptr_map&
  class_or_union_diff::inserted_data_members() const
  {return get_priv()->inserted_data_members_;}
  
  /// Getter for the data members that got deleted.
  ///
  /// @return a map of data members that got deleted.
  const string_decl_base_sptr_map&
  class_or_union_diff::deleted_data_members() const
  {return get_priv()->deleted_data_members_;}
  
  /// @return the edit script of the member functions of the two @ref
  /// class_or_union.
  const edit_script&
  class_or_union_diff::member_fns_changes() const
  {return get_priv()->member_fns_changes_;}
  
  /// Getter for the virtual members functions that have had a change in
  /// a sub-type, without having a change in their symbol name.
  ///
  /// @return a sorted vector of virtual member functions that have a
  /// sub-type change.
  const function_decl_diff_sptrs_type&
  class_or_union_diff::changed_member_fns() const
  {return get_priv()->sorted_changed_member_functions_;}
  
  /// @return the edit script of the member functions of the two
  /// classes.
  edit_script&
  class_or_union_diff::member_fns_changes()
  {return get_priv()->member_fns_changes_;}
  
  /// @return a map of member functions that got deleted.
  const string_member_function_sptr_map&
  class_or_union_diff::deleted_member_fns() const
  {return get_priv()->deleted_member_functions_;}
  
  /// @return a map of member functions that got inserted.
  const string_member_function_sptr_map&
  class_or_union_diff::inserted_member_fns() const
  {return get_priv()->inserted_member_functions_;}
  
  /// Getter of the sorted vector of data members that got replaced by
  /// another data member.
  ///
  /// @return sorted vector of changed data member.
  const var_diff_sptrs_type&
  class_or_union_diff::sorted_changed_data_members() const
  {return get_priv()->sorted_changed_dm_;}
  
  /// Count the number of /filtered/ data members that got replaced by
  /// another data member.
  ///
  /// @return the number of changed data member that got filtered out.
  size_t
  class_or_union_diff::count_filtered_changed_data_members(bool local) const
  {return get_priv()->count_filtered_changed_dm(local);}
  
  /// Getter of the sorted vector of data members with a (sub-)type change.
  ///
  /// @return sorted vector of changed data member.
  const var_diff_sptrs_type&
  class_or_union_diff::sorted_subtype_changed_data_members() const
  {return get_priv()->sorted_subtype_changed_dm_;}
  
  /// Count the number of /filtered/ data members with a sub-type change.
  ///
  /// @return the number of changed data member that got filtered out.
  size_t
  class_or_union_diff::count_filtered_subtype_changed_data_members(bool local) const
  {return get_priv()->count_filtered_subtype_changed_dm(local);}
  
  /// Get the map of data members that got replaced by anonymous data
  /// members.
  ///
  /// The key of a map entry is the name of the replaced data member and
  /// the value is the anonymous data member that replaces it.
  ///
  /// @return the map of data members replaced by anonymous data
  /// members.
  const string_decl_base_sptr_map&
  class_or_union_diff::data_members_replaced_by_adms() const
  {return get_priv()->dms_replaced_by_adms_;}
  
  /// Get an ordered vector of of data members that got replaced by
  /// anonymous data members.
  ///
  /// This returns a vector of pair of two data members: the one that
  /// was replaced, and the anonymous data member that replaced it.
  ///
  /// @return the sorted vector data members replaced by anonymous data members.
  const changed_var_sptrs_type&
  class_or_union_diff::ordered_data_members_replaced_by_adms() const
  {
    if (priv_->dms_replaced_by_adms_ordered_.empty())
      {
        for (string_decl_base_sptr_map::const_iterator it =
  	     priv_->dms_replaced_by_adms_.begin();
  	   it != priv_->dms_replaced_by_adms_.end();
  	   ++it)
  	{
  	  const var_decl_sptr dm =
  	    first_class_or_union()->find_data_member(it->first);
  	  ABG_ASSERT(dm);
  	  changed_var_sptr changed_dm(dm, is_data_member(it->second));
  	  priv_->dms_replaced_by_adms_ordered_.push_back(changed_dm);
  	}
        sort_changed_data_members(priv_->dms_replaced_by_adms_ordered_);
      }
  
    return priv_->dms_replaced_by_adms_ordered_;
  }
  
  /// @return the edit script of the member function templates of the two
  /// @ref class_or_union.
  const edit_script&
  class_or_union_diff::member_fn_tmpls_changes() const
  {return get_priv()->member_fn_tmpls_changes_;}
  
  /// @return the edit script of the member function templates of the
  /// two @ref class_or_union.
  edit_script&
  class_or_union_diff::member_fn_tmpls_changes()
  {return get_priv()->member_fn_tmpls_changes_;}
  
  /// @return the edit script of the member class templates of the two
  /// @ref class_or_union.
  const edit_script&
  class_or_union_diff::member_class_tmpls_changes() const
  {return get_priv()->member_class_tmpls_changes_;}
  
  /// @return the edit script of the member class templates of the two
  /// @ref class_or_union.
  edit_script&
  class_or_union_diff::member_class_tmpls_changes()
  {return get_priv()->member_class_tmpls_changes_;}
  
  /// Test if the current diff node carries a change.
  bool
  class_or_union_diff::has_changes() const
  {return first_class_or_union() != second_class_or_union();}
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  class_or_union_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_class_or_union(), *second_class_or_union(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  
  /// Report the changes carried by the current @ref class_or_union_diff
  /// node in a textual format.
  ///
  /// @param out the output stream to write the textual report to.
  ///
  /// @param indent the number of white space to use as indentation.
  void
  class_or_union_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref class_or_union_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  class_or_union_diff::chain_into_hierarchy()
  {
    // data member changes
    for (var_diff_sptrs_type::const_iterator i =
  	 get_priv()->sorted_subtype_changed_dm_.begin();
         i != get_priv()->sorted_subtype_changed_dm_.end();
         ++i)
      if (diff_sptr d = *i)
        append_child_node(d);
  
    for (var_diff_sptrs_type::const_iterator i =
  	 get_priv()->sorted_changed_dm_.begin();
         i != get_priv()->sorted_changed_dm_.end();
         ++i)
      if (diff_sptr d = *i)
        append_child_node(d);
  
    // member types changes
    for (diff_sptrs_type::const_iterator i =
  	 get_priv()->sorted_changed_member_types_.begin();
         i != get_priv()->sorted_changed_member_types_.end();
         ++i)
      if (diff_sptr d = *i)
        append_child_node(d);
  
    // member function changes
    for (function_decl_diff_sptrs_type::const_iterator i =
  	 get_priv()->sorted_changed_member_functions_.begin();
         i != get_priv()->sorted_changed_member_functions_.end();
         ++i)
      if (diff_sptr d = *i)
        append_child_node(d);
  }
  
  // </class_or_union_diff stuff>
  
  //<class_diff stuff>
  
  /// Clear the lookup tables useful for reporting.
  ///
  /// This function must be updated each time a lookup table is added or
  /// removed from the class_diff::priv.
  void
  class_diff::clear_lookup_tables(void)
  {
    priv_->deleted_bases_.clear();
    priv_->inserted_bases_.clear();
    priv_->changed_bases_.clear();
  }
  
  /// Tests if the lookup tables are empty.
  ///
  /// @return true if the lookup tables are empty, false otherwise.
  bool
  class_diff::lookup_tables_empty(void) const
  {
    return (priv_->deleted_bases_.empty()
  	  && priv_->inserted_bases_.empty()
  	  && priv_->changed_bases_.empty());
  }
  
  /// If the lookup tables are not yet built, walk the differences and
  /// fill them.
  void
  class_diff::ensure_lookup_tables_populated(void) const
  {
    class_or_union_diff::ensure_lookup_tables_populated();
  
    if (!lookup_tables_empty())
      return;
  
    {
      edit_script& e = get_priv()->base_changes_;
  
      for (vector<deletion>::const_iterator it = e.deletions().begin();
  	 it != e.deletions().end();
  	 ++it)
        {
  	unsigned i = it->index();
  	class_decl::base_spec_sptr b =
  	  first_class_decl()->get_base_specifiers()[i];
  	string name = b->get_base_class()->get_name();
  	ABG_ASSERT(get_priv()->deleted_bases_.find(name)
  	       == get_priv()->deleted_bases_.end());
  	get_priv()->deleted_bases_[name] = b;
        }
  
      for (vector<insertion>::const_iterator it = e.insertions().begin();
  	 it != e.insertions().end();
  	 ++it)
        {
  	for (vector<unsigned>::const_iterator iit =
  	       it->inserted_indexes().begin();
  	     iit != it->inserted_indexes().end();
  	     ++iit)
  	  {
  	    unsigned i = *iit;
  	    class_decl::base_spec_sptr b =
  	      second_class_decl()->get_base_specifiers()[i];
  	    string name = b->get_base_class()->get_name();
  	    ABG_ASSERT(get_priv()->inserted_bases_.find(name)
  		   == get_priv()->inserted_bases_.end());
  	    string_base_sptr_map::const_iterator j =
  	      get_priv()->deleted_bases_.find(name);
  	    if (j != get_priv()->deleted_bases_.end())
  	      {
  		if (j->second != b)
  		  get_priv()->changed_bases_[name] =
  		    compute_diff(j->second, b, context());
  		get_priv()->deleted_bases_.erase(j);
  	      }
  	    else
  	      get_priv()->inserted_bases_[name] = b;
  	  }
        }
    }
  
    sort_string_base_sptr_map(get_priv()->deleted_bases_,
  			    get_priv()->sorted_deleted_bases_);
    sort_string_base_sptr_map(get_priv()->inserted_bases_,
  			    get_priv()->sorted_inserted_bases_);
    sort_string_base_diff_sptr_map(get_priv()->changed_bases_,
  				 get_priv()->sorted_changed_bases_);
  
    {
      const class_or_union_diff::priv_sptr &p = class_or_union_diff::get_priv();
  
      edit_script& e = p->member_fns_changes_;
  
      for (vector<deletion>::const_iterator it = e.deletions().begin();
  	 it != e.deletions().end();
  	 ++it)
        {
  	unsigned i = it->index();
  	method_decl_sptr mem_fn =
  	  first_class_decl()->get_virtual_mem_fns()[i];
  	string name = mem_fn->get_linkage_name();
  	if (name.empty())
  	  name = mem_fn->get_pretty_representation();
  	ABG_ASSERT(!name.empty());
  	if (p->deleted_member_functions_.find(name)
  	    != p->deleted_member_functions_.end())
  	  continue;
  	p->deleted_member_functions_[name] = mem_fn;
        }
  
      for (vector<insertion>::const_iterator it = e.insertions().begin();
  	 it != e.insertions().end();
  	 ++it)
        {
  	for (vector<unsigned>::const_iterator iit =
  	       it->inserted_indexes().begin();
  	     iit != it->inserted_indexes().end();
  	     ++iit)
  	  {
  	    unsigned i = *iit;
  
  	    method_decl_sptr mem_fn =
  	      second_class_decl()->get_virtual_mem_fns()[i];
  	    string name = mem_fn->get_linkage_name();
  	    if (name.empty())
  	      name = mem_fn->get_pretty_representation();
  	    ABG_ASSERT(!name.empty());
  	    if (p->inserted_member_functions_.find(name)
  		!= p->inserted_member_functions_.end())
  	      continue;
  	    string_member_function_sptr_map::const_iterator j =
  	      p->deleted_member_functions_.find(name);
  
  	    if (j != p->deleted_member_functions_.end())
  	      {
  		if (*j->second != *mem_fn)
  		  p->changed_member_functions_[name] =
  		    compute_diff(static_pointer_cast<function_decl>(j->second),
  				 static_pointer_cast<function_decl>(mem_fn),
  				 context());
  		p->deleted_member_functions_.erase(j);
  	      }
  	    else
  	      p->inserted_member_functions_[name] = mem_fn;
  	  }
        }
  
      // Now walk the allegedly deleted member functions; check if their
      // underlying symbols are deleted as well; otherwise, consider
      // that the member function in question hasn't been deleted.
  
      vector<string> to_delete;
      corpus_sptr f = context()->get_first_corpus(),
        s = context()->get_second_corpus();
      if (s)
        for (string_member_function_sptr_map::const_iterator i =
  	     deleted_member_fns().begin();
  	   i != deleted_member_fns().end();
  	   ++i)
  	{
  	  if (get_member_function_is_virtual(i->second))
  	    continue;
  	  // We assume that all non-virtual member functions functions
  	  // we look at here have ELF symbols.
  	  if (!i->second->get_symbol()
  	      || s->lookup_function_symbol(*i->second->get_symbol()))
  	    to_delete.push_back(i->first);
  	}
  
  
      for (vector<string>::const_iterator i = to_delete.begin();
  	 i != to_delete.end();
  	 ++i)
        p->deleted_member_functions_.erase(*i);
  
      // Do something similar for added functions.
      to_delete.clear();
      if (f)
        for (string_member_function_sptr_map::const_iterator i =
  	     inserted_member_fns().begin();
  	   i != inserted_member_fns().end();
  	   ++i)
  	{
  	  if (get_member_function_is_virtual(i->second))
  	    continue;
  	  // We assume that all non-virtual member functions functions
  	  // we look at here have ELF symbols.
  	  if (!i->second->get_symbol()
  	      || f->lookup_function_symbol(*i->second->get_symbol()))
  	    to_delete.push_back(i->first);
  	}
  
      for (vector<string>::const_iterator i = to_delete.begin();
  	 i != to_delete.end();
  	 ++i)
        p->inserted_member_functions_.erase(*i);
  
      sort_string_member_function_sptr_map(p->deleted_member_functions_,
  					 p->sorted_deleted_member_functions_);
  
      sort_string_member_function_sptr_map(p->inserted_member_functions_,
  					 p->sorted_inserted_member_functions_);
  
      sort_string_virtual_member_function_diff_sptr_map
        (p->changed_member_functions_,
         p->sorted_changed_member_functions_);
    }
  }
  
  /// Allocate the memory for the priv_ pimpl data member of the @ref
  /// class_diff class.
  void
  class_diff::allocate_priv_data()
  {
    class_or_union_diff::allocate_priv_data();
    if (!priv_)
      priv_.reset(new priv);
  }
  
  /// Test whether a given base class has changed.  A base class has
  /// changed if it's in both in deleted *and* inserted bases.
  ///
  ///@param d the declaration for the base class to consider.
  ///
  /// @return the new base class if the given base class has changed, or
  /// NULL if it hasn't.
  class_decl::base_spec_sptr
  class_diff::priv::base_has_changed(class_decl::base_spec_sptr d) const
  {
    string qname = d->get_base_class()->get_qualified_name();
    string_base_diff_sptr_map::const_iterator it =
      changed_bases_.find(qname);
  
    return (it == changed_bases_.end())
      ? class_decl::base_spec_sptr()
      : it->second->second_base();
  
  }
  
  /// Count the number of bases classes whose changes got filtered out.
  ///
  /// @return the number of bases classes whose changes got filtered
  /// out.
  size_t
  class_diff::priv::count_filtered_bases()
  {
    size_t num_filtered = 0;
    for (base_diff_sptrs_type::const_iterator i = sorted_changed_bases_.begin();
         i != sorted_changed_bases_.end();
         ++i)
      {
        diff_sptr diff = *i;
        if (diff && diff->is_filtered_out())
  	++num_filtered;
      }
    return num_filtered;
  }
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref class_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  class_diff::chain_into_hierarchy()
  {
    class_or_union_diff::chain_into_hierarchy();
  
    // base class changes.
    for (base_diff_sptrs_type::const_iterator i =
  	 get_priv()->sorted_changed_bases_.begin();
         i != get_priv()->sorted_changed_bases_.end();
         ++i)
      if (diff_sptr d = *i)
        append_child_node(d);
  }
  
  /// Constructor of class_diff
  ///
  /// @param first_scope the first class of the diff.
  ///
  /// @param second_scope the second class of the diff.
  ///
  /// @param ctxt the diff context to use.
  class_diff::class_diff(class_decl_sptr first_scope,
  		       class_decl_sptr second_scope,
  		       diff_context_sptr ctxt)
    : class_or_union_diff(first_scope, second_scope, ctxt)
      //  We don't initialize the priv_ data member here.  This is an
      //  optimization to reduce memory consumption (and also execution
      //  time) for cases where there are a lot of instances of
      //  class_diff in the same equivalence class.  In compute_diff(),
      //  the priv_ is set to the priv_ of the canonical diff node.
      //  See PR libabigail/17948.
  {}
  
  class_diff::~class_diff()
  {}
  
  /// Getter of the private data of the @ref class_diff type.
  ///
  /// Note that due to an optimization, the private data of @ref
  /// class_diff can be shared among several instances of class_diff, so
  /// you should never try to access class_diff::priv directly.
  ///
  /// When class_diff::priv is shared, this function returns the correct
  /// shared one.
  ///
  /// @return the (possibly) shared private data of the current instance
  /// of class_diff.
  const class_diff::priv_sptr&
  class_diff::get_priv() const
  {
    if (priv_)
      return priv_;
  
    // If the current class_diff::priv member is empty, then look for
    // the shared one, from the canonical type.
    class_diff *canonical =
      dynamic_cast<class_diff*>(get_canonical_diff());
    ABG_ASSERT(canonical);
    ABG_ASSERT(canonical->priv_);
  
    return canonical->priv_;
  }
  
  /// Finish building the current instance of @ref class_diff.
  void
  class_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// @return the pretty representation of the current instance of @ref
  /// class_diff.
  const string&
  class_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "class_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Return true iff the current diff node carries a change.
  ///
  /// @return true iff the current diff node carries a change.
  bool
  class_diff::has_changes() const
  {return (first_class_decl() != second_class_decl());}
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  class_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_class_decl(), *second_class_decl(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// @return the first class invoveld in the diff.
  shared_ptr<class_decl>
  class_diff::first_class_decl() const
  {return dynamic_pointer_cast<class_decl>(first_subject());}
  
  /// Getter of the second class involved in the diff.
  ///
  /// @return the second class invoveld in the diff
  shared_ptr<class_decl>
  class_diff::second_class_decl() const
  {return dynamic_pointer_cast<class_decl>(second_subject());}
  
  /// @return the edit script of the bases of the two classes.
  const edit_script&
  class_diff::base_changes() const
  {return get_priv()->base_changes_;}
  
  /// Getter for the deleted base classes of the diff.
  ///
  /// @return a map containing the deleted base classes, keyed with
  /// their pretty representation.
  const string_base_sptr_map&
  class_diff::deleted_bases() const
  {return get_priv()->deleted_bases_;}
  
  /// Getter for the inserted base classes of the diff.
  ///
  /// @return a map containing the inserted base classes, keyed with
  /// their pretty representation.
  const string_base_sptr_map&
  class_diff::inserted_bases() const
  {return get_priv()->inserted_bases_;}
  
  /// Getter for the changed base classes of the diff.
  ///
  /// @return a sorted vector containing the changed base classes
  const base_diff_sptrs_type&
  class_diff::changed_bases()
  {return get_priv()->sorted_changed_bases_;}
  
  /// @return the edit script of the bases of the two classes.
  edit_script&
  class_diff::base_changes()
  {return get_priv()->base_changes_;}
  
  /// Produce a basic report about the changes between two class_decl.
  ///
  /// @param out the output stream to report the changes to.
  ///
  /// @param indent the string to use as an indentation prefix in the
  /// report.
  void
  class_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Compute the set of changes between two instances of class_decl.
  ///
  /// Note that the two types must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the first class_decl to consider.
  ///
  /// @param second the second class_decl to consider.
  ///
  /// @return changes the resulting changes.
  ///
  /// @param ctxt the diff context to use.
  class_diff_sptr
  compute_diff(const class_decl_sptr	first,
  	     const class_decl_sptr	second,
  	     diff_context_sptr		ctxt)
  {
    if (first && second)
      ABG_ASSERT(first->get_environment() == second->get_environment());
  
    class_decl_sptr f = is_class_type(look_through_decl_only_class(first)),
      s = is_class_type(look_through_decl_only_class(second));
  
    class_diff_sptr changes(new class_diff(f, s, ctxt));
  
    ctxt->initialize_canonical_diff(changes);
    ABG_ASSERT(changes->get_canonical_diff());
  
    if (!ctxt->get_canonical_diff_for(first, second))
      {
        // Either first or second is a decl-only class; let's set the
        // canonical diff here in that case.
        diff_sptr canonical_diff = ctxt->get_canonical_diff_for(changes);
        ABG_ASSERT(canonical_diff);
        ctxt->set_canonical_diff_for(first, second, canonical_diff);
      }
  
    // Ok, so this is an optimization.  Do not freak out if it looks
    // weird, because, well, it does look weird.  This speeds up
    // greatly, for instance, the test case given at PR
    // libabigail/17948.
    //
    // We are setting the private data of the new instance of class_diff
    // (which is 'changes') to the private data of its canonical
    // instance.  That is, we are sharing the private data of 'changes'
    // with the private data of its canonical instance to consume less
    // memory in cases where the equivalence class of 'changes' is huge.
    //
    // But if changes is its own canonical instance, then we initialize
    // its private data properly
    if (is_class_diff(changes->get_canonical_diff()) == changes.get())
      // changes is its own canonical instance, so it gets a brand new
      // private data.
      changes->allocate_priv_data();
    else
      {
        // changes has a non-empty equivalence class so it's going to
        // share its private data with its canonical instance.  Next
        // time class_diff::get_priv() is invoked, it's going to return
        // the shared private data of the canonical instance.
        return changes;
      }
  
    // Compare base specs
    compute_diff(f->get_base_specifiers().begin(),
  	       f->get_base_specifiers().end(),
  	       s->get_base_specifiers().begin(),
  	       s->get_base_specifiers().end(),
  	       changes->base_changes());
  
    // Do *not* compare member types because it generates lots of noise
    // and I doubt it's really useful.
  #if 0
    compute_diff(f->get_member_types().begin(),
  	       f->get_member_types().end(),
  	       s->get_member_types().begin(),
  	       s->get_member_types().end(),
  	       changes->member_types_changes());
  #endif
  
    // Compare data member
    compute_diff(f->get_non_static_data_members().begin(),
  	       f->get_non_static_data_members().end(),
  	       s->get_non_static_data_members().begin(),
  	       s->get_non_static_data_members().end(),
  	       changes->data_members_changes());
  
    // Compare virtual member functions
    compute_diff(f->get_virtual_mem_fns().begin(),
  	       f->get_virtual_mem_fns().end(),
  	       s->get_virtual_mem_fns().begin(),
  	       s->get_virtual_mem_fns().end(),
  	       changes->member_fns_changes());
  
    // Compare member function templates
    compute_diff(f->get_member_function_templates().begin(),
  	       f->get_member_function_templates().end(),
  	       s->get_member_function_templates().begin(),
  	       s->get_member_function_templates().end(),
  	       changes->member_fn_tmpls_changes());
  
    // Likewise, do not compare member class templates
  #if 0
    compute_diff(f->get_member_class_templates().begin(),
  	       f->get_member_class_templates().end(),
  	       s->get_member_class_templates().begin(),
  	       s->get_member_class_templates().end(),
  	       changes->member_class_tmpls_changes());
  #endif
  
    changes->ensure_lookup_tables_populated();
  
    return changes;
  }
  
  //</class_diff stuff>
  
  // <base_diff stuff>
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref base_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  base_diff::chain_into_hierarchy()
  {append_child_node(get_underlying_class_diff());}
  
  /// @param first the first base spec to consider.
  ///
  /// @param second the second base spec to consider.
  ///
  /// @param ctxt the context of the diff.  Note that this context
  /// object must stay alive at least during the life time of the
  /// current instance of @ref base_diff.  Otherwise memory corruption
  /// issues occur.
  base_diff::base_diff(class_decl::base_spec_sptr first,
  		     class_decl::base_spec_sptr second,
  		     class_diff_sptr		underlying,
  		     diff_context_sptr		ctxt)
    : diff(first, second, ctxt),
      priv_(new priv(underlying))
  {}
  
  /// Finish building the current instance of @ref base_diff.
  void
  base_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
  
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// Getter for the first base spec of the diff object.
  ///
  /// @return the first base specifier for the diff object.
  class_decl::base_spec_sptr
  base_diff::first_base() const
  {return dynamic_pointer_cast<class_decl::base_spec>(first_subject());}
  
  /// Getter for the second base spec of the diff object.
  ///
  /// @return the second base specifier for the diff object.
  class_decl::base_spec_sptr
  base_diff::second_base() const
  {return dynamic_pointer_cast<class_decl::base_spec>(second_subject());}
  
  /// Getter for the diff object for the diff of the underlying base
  /// classes.
  ///
  /// @return the diff object for the diff of the underlying base
  /// classes.
  const class_diff_sptr
  base_diff::get_underlying_class_diff() const
  {return priv_->underlying_class_diff_;}
  
  /// Setter for the diff object for the diff of the underlyng base
  /// classes.
  ///
  /// @param d the new diff object for the diff of the underlying base
  /// classes.
  void
  base_diff::set_underlying_class_diff(class_diff_sptr d)
  {priv_->underlying_class_diff_ = d;}
  
  /// @return the pretty representation for the current instance of @ref
  /// base_diff.
  const string&
  base_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "base_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Return true iff the current diff node carries a change.
  ///
  /// Return true iff the current diff node carries a change.
  bool
  base_diff::has_changes() const
  {return first_base() != second_base();}
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  base_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_base(), *second_base(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// Generates a report for the current instance of base_diff.
  ///
  /// @param out the output stream to send the report to.
  ///
  /// @param indent the string to use for indentation.
  void
  base_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Constructs the diff object representing a diff between two base
  /// class specifications.
  ///
  /// Note that the two artifacts must have been created in the same
  /// @ref environment, otherwise, this function aborts.
  ///
  /// @param first the first base class specification.
  ///
  /// @param second the second base class specification.
  ///
  /// @param ctxt the content of the diff.
  ///
  /// @return the resulting diff object.
  base_diff_sptr
  compute_diff(const class_decl::base_spec_sptr	first,
  	     const class_decl::base_spec_sptr	second,
  	     diff_context_sptr			ctxt)
  {
    if (first && second)
      {
        ABG_ASSERT(first->get_environment() == second->get_environment());
        ABG_ASSERT(first->get_base_class()->get_environment()
  	     == second->get_base_class()->get_environment());
        ABG_ASSERT(first->get_environment()
  	     == first->get_base_class()->get_environment());
      }
  
    class_diff_sptr cl = compute_diff(first->get_base_class(),
  				    second->get_base_class(),
  				    ctxt);
    base_diff_sptr changes(new base_diff(first, second, cl, ctxt));
  
    ctxt->initialize_canonical_diff(changes);
  
    return changes;
  }
  
  // </base_diff stuff>
  
  
  // <union_diff stuff>
  
  /// Clear the lookup tables useful for reporting.
  ///
  /// This function must be updated each time a lookup table is added or
  /// removed from the union_diff::priv.
  void
  union_diff::clear_lookup_tables(void)
  {class_or_union_diff::clear_lookup_tables();}
  
  /// Tests if the lookup tables are empty.
  ///
  /// @return true if the lookup tables are empty, false otherwise.
  bool
  union_diff::lookup_tables_empty(void) const
  {return class_or_union_diff::lookup_tables_empty();}
  
  /// If the lookup tables are not yet built, walk the differences and
  /// fill them.
  void
  union_diff::ensure_lookup_tables_populated(void) const
  {class_or_union_diff::ensure_lookup_tables_populated();}
  
  /// Allocate the memory for the priv_ pimpl data member of the @ref
  /// union_diff class.
  void
  union_diff::allocate_priv_data()
  {
    class_or_union_diff::allocate_priv_data();
  }
  
  /// Constructor for the @ref union_diff type.
  ///
  /// @param first_union the first object of the comparison.
  ///
  /// @param second_union the second object of the comparison.
  ///
  /// @param ctxt the context of the comparison.
  union_diff::union_diff(union_decl_sptr first_union,
  		       union_decl_sptr second_union,
  		       diff_context_sptr ctxt)
    : class_or_union_diff(first_union, second_union, ctxt)
  {}
  
  /// Finish building the current instance of @ref union_diff.
  void
  union_diff::finish_diff_type()
  {class_or_union_diff::finish_diff_type();}
  
  /// Destructor of the union_diff node.
  union_diff::~union_diff()
  {}
  
  /// @return the first object of the comparison.
  union_decl_sptr
  union_diff::first_union_decl() const
  {return is_union_type(first_subject());}
  
  /// @return the second object of the comparison.
  union_decl_sptr
  union_diff::second_union_decl() const
  {return is_union_type(second_subject());}
  
  /// @return the pretty representation of the current diff node.
  const string&
  union_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "union_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Report the changes carried by the current @ref union_diff node in
  /// a textual format.
  ///
  /// @param out the output stream to write the textual report to.
  ///
  /// @param indent the number of white space to use as indentation.
  void
  union_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Compute the difference between two @ref union_decl types.
  ///
  /// Note that the two types must hav been created in the same
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the first @ref union_decl to consider.
  ///
  /// @param second the second @ref union_decl to consider.
  ///
  /// @param ctxt the context of the diff to use.
  union_diff_sptr
  compute_diff(const union_decl_sptr	first,
  	     const union_decl_sptr	second,
  	     diff_context_sptr	ctxt)
  {
    if (first && second)
      ABG_ASSERT(first->get_environment() == second->get_environment());
  
    union_diff_sptr changes(new union_diff(first, second, ctxt));
  
    ctxt->initialize_canonical_diff(changes);
    ABG_ASSERT(changes->get_canonical_diff());
  
    // Ok, so this is an optimization.  Do not freak out if it looks
    // weird, because, well, it does look weird.  This speeds up
    // greatly, for instance, the test case given at PR
    // libabigail/17948.
    //
    // We are setting the private data of the new instance of class_diff
    // (which is 'changes') to the private data of its canonical
    // instance.  That is, we are sharing the private data of 'changes'
    // with the private data of its canonical instance to consume less
    // memory in cases where the equivalence class of 'changes' is huge.
    //
    // But if changes is its own canonical instance, then we initialize
    // its private data properly.
    if (is_union_diff(changes->get_canonical_diff()) ==  changes.get())
      // changes is its own canonical instance, so it gets a brand new
      // private data.
      changes->allocate_priv_data();
    else
      {
        // changes has a non-empty equivalence class so it's going to
        // share its private data with its canonical instance.  Next
        // time class_diff::get_priv() is invoked, it's going to return
        // the shared private data of the canonical instance.
        return changes;
      }
  
    // Compare data member
    compute_diff(first->get_non_static_data_members().begin(),
  	       first->get_non_static_data_members().end(),
  	       second->get_non_static_data_members().begin(),
  	       second->get_non_static_data_members().end(),
  	       changes->data_members_changes());
  
  #if 0
    // Compare member functions
    compute_diff(first->get_mem_fns().begin(),
  	       first->get_mem_fns().end(),
  	       second->get_mem_fns().begin(),
  	       second->get_mem_fns().end(),
  	       changes->member_fns_changes());
  
    // Compare member function templates
    compute_diff(first->get_member_function_templates().begin(),
  	       first->get_member_function_templates().end(),
  	       second->get_member_function_templates().begin(),
  	       second->get_member_function_templates().end(),
  	       changes->member_fn_tmpls_changes());
  #endif
  
    changes->ensure_lookup_tables_populated();
  
    return changes;
  }
  
  // </union_diff stuff>
  
  //<scope_diff stuff>
  
  /// Clear the lookup tables that are useful for reporting.
  ///
  /// This function must be updated each time a lookup table is added or
  /// removed.
  void
  scope_diff::clear_lookup_tables()
  {
    priv_->deleted_types_.clear();
    priv_->deleted_decls_.clear();
    priv_->inserted_types_.clear();
    priv_->inserted_decls_.clear();
    priv_->changed_types_.clear();
    priv_->changed_decls_.clear();
    priv_->removed_types_.clear();
    priv_->removed_decls_.clear();
    priv_->added_types_.clear();
    priv_->added_decls_.clear();
  }
  
  /// Tests if the lookup tables are empty.
  ///
  /// This function must be updated each time a lookup table is added or
  /// removed.
  ///
  /// @return true iff all the lookup tables are empty.
  bool
  scope_diff::lookup_tables_empty() const
  {
    return (priv_->deleted_types_.empty()
  	  && priv_->deleted_decls_.empty()
  	  && priv_->inserted_types_.empty()
  	  && priv_->inserted_decls_.empty()
  	  && priv_->changed_types_.empty()
  	  && priv_->changed_decls_.empty()
  	  && priv_->removed_types_.empty()
  	  && priv_->removed_decls_.empty()
  	  && priv_->added_types_.empty()
  	  && priv_->added_decls_.empty());
  }
  
  /// If the lookup tables are not yet built, walk the member_changes_
  /// member and fill the lookup tables.
  void
  scope_diff::ensure_lookup_tables_populated()
  {
    if (!lookup_tables_empty())
      return;
  
    edit_script& e = priv_->member_changes_;
  
    // Populate deleted types & decls lookup tables.
    for (vector<deletion>::const_iterator i = e.deletions().begin();
         i != e.deletions().end();
         ++i)
      {
        decl_base_sptr decl = deleted_member_at(i);
        string qname = decl->get_qualified_name();
        if (is_type(decl))
  	{
  	  class_decl_sptr klass_decl = dynamic_pointer_cast<class_decl>(decl);
  	  if (klass_decl && klass_decl->get_is_declaration_only())
  	    continue;
  
  	  ABG_ASSERT(priv_->deleted_types_.find(qname)
  		 == priv_->deleted_types_.end());
  	  priv_->deleted_types_[qname] = decl;
  	}
        else
  	{
  	  ABG_ASSERT(priv_->deleted_decls_.find(qname)
  		 == priv_->deleted_decls_.end());
  	  priv_->deleted_decls_[qname] = decl;
  	}
      }
  
    // Populate inserted types & decls as well as chagned types & decls
    // lookup tables.
    for (vector<insertion>::const_iterator it = e.insertions().begin();
         it != e.insertions().end();
         ++it)
      {
        for (vector<unsigned>::const_iterator i = it->inserted_indexes().begin();
  	   i != it->inserted_indexes().end();
  	   ++i)
  	{
  	  decl_base_sptr decl = inserted_member_at(i);
  	  string qname = decl->get_qualified_name();
  	  if (is_type(decl))
  	    {
  	      class_decl_sptr klass_decl =
  		dynamic_pointer_cast<class_decl>(decl);
  	      if (klass_decl && klass_decl->get_is_declaration_only())
  		continue;
  
  	      ABG_ASSERT(priv_->inserted_types_.find(qname)
  		     == priv_->inserted_types_.end());
  	      string_decl_base_sptr_map::const_iterator j =
  		priv_->deleted_types_.find(qname);
  	      if (j != priv_->deleted_types_.end())
  		{
  		  if (*j->second != *decl)
  		    priv_->changed_types_[qname] =
  		      compute_diff(j->second, decl, context());
  		  priv_->deleted_types_.erase(j);
  		}
  	      else
  		priv_->inserted_types_[qname] = decl;
  	    }
  	  else
  	    {
  	      ABG_ASSERT(priv_->inserted_decls_.find(qname)
  		     == priv_->inserted_decls_.end());
  	      string_decl_base_sptr_map::const_iterator j =
  		priv_->deleted_decls_.find(qname);
  	      if (j != priv_->deleted_decls_.end())
  		{
  		  if (*j->second != *decl)
  		    priv_->changed_decls_[qname] =
  		      compute_diff(j->second, decl, context());
  		  priv_->deleted_decls_.erase(j);
  		}
  	      else
  		priv_->inserted_decls_[qname] = decl;
  	    }
  	}
      }
  
    sort_string_diff_sptr_map(priv_->changed_decls_,
  			    priv_->sorted_changed_decls_);
    sort_string_diff_sptr_map(priv_->changed_types_,
  			    priv_->sorted_changed_types_);
  
    // Populate removed types/decls lookup tables
    for (string_decl_base_sptr_map::const_iterator i =
  	 priv_->deleted_types_.begin();
         i != priv_->deleted_types_.end();
         ++i)
      {
        string_decl_base_sptr_map::const_iterator r =
  	priv_->inserted_types_.find(i->first);
        if (r == priv_->inserted_types_.end())
  	priv_->removed_types_[i->first] = i->second;
      }
    for (string_decl_base_sptr_map::const_iterator i =
  	 priv_->deleted_decls_.begin();
         i != priv_->deleted_decls_.end();
         ++i)
      {
        string_decl_base_sptr_map::const_iterator r =
  	priv_->inserted_decls_.find(i->first);
        if (r == priv_->inserted_decls_.end())
  	priv_->removed_decls_[i->first] = i->second;
      }
  
    // Populate added types/decls.
    for (string_decl_base_sptr_map::const_iterator i =
  	 priv_->inserted_types_.begin();
         i != priv_->inserted_types_.end();
         ++i)
      {
        string_decl_base_sptr_map::const_iterator r =
  	priv_->deleted_types_.find(i->first);
        if (r == priv_->deleted_types_.end())
  	priv_->added_types_[i->first] = i->second;
      }
    for (string_decl_base_sptr_map::const_iterator i =
  	 priv_->inserted_decls_.begin();
         i != priv_->inserted_decls_.end();
         ++i)
      {
        string_decl_base_sptr_map::const_iterator r =
  	priv_->deleted_decls_.find(i->first);
        if (r == priv_->deleted_decls_.end())
  	priv_->added_decls_[i->first] = i->second;
      }
  }
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref scope_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  scope_diff::chain_into_hierarchy()
  {
    for (diff_sptrs_type::const_iterator i = changed_types().begin();
         i != changed_types().end();
         ++i)
      if (*i)
        append_child_node(*i);
  
    for (diff_sptrs_type::const_iterator i = changed_decls().begin();
         i != changed_decls().end();
         ++i)
      if (*i)
        append_child_node(*i);
  }
  
  /// Constructor for scope_diff
  ///
  /// @param first_scope the first scope to consider for the diff.
  ///
  /// @param second_scope the second scope to consider for the diff.
  ///
  /// @param ctxt the diff context to use.  Note that this context
  /// object must stay alive at least during the life time of the
  /// current instance of @ref scope_diff.  Otherwise memory corruption
  /// issues occur.
  scope_diff::scope_diff(scope_decl_sptr first_scope,
  		       scope_decl_sptr second_scope,
  		       diff_context_sptr ctxt)
    : diff(first_scope, second_scope, ctxt),
      priv_(new priv)
  {}
  
  /// Finish building the current instance of @ref scope_diff.
  void
  scope_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// Getter for the first scope of the diff.
  ///
  /// @return the first scope of the diff.
  const scope_decl_sptr
  scope_diff::first_scope() const
  {return dynamic_pointer_cast<scope_decl>(first_subject());}
  
  /// Getter for the second scope of the diff.
  ///
  /// @return the second scope of the diff.
  const scope_decl_sptr
  scope_diff::second_scope() const
  {return dynamic_pointer_cast<scope_decl>(second_subject());}
  
  /// Accessor of the edit script of the members of a scope.
  ///
  /// This edit script is computed using the equality operator that
  /// applies to shared_ptr<decl_base>.
  ///
  /// That has interesting consequences.  For instance, consider two
  /// scopes S0 and S1.  S0 contains a class C0 and S1 contains a class
  /// S0'.  C0 and C0' have the same qualified name, but have different
  /// members.  The edit script will consider that C0 has been deleted
  /// from S0 and that S0' has been inserted.  This is a low level
  /// canonical representation of the changes; a higher level
  /// representation would give us a simpler way to say "the class C0
  /// has been modified into C0'".  But worry not.  We do have such
  /// higher representation as well; that is what changed_types() and
  /// changed_decls() is for.
  ///
  /// @return the edit script of the changes encapsulatd in this
  /// instance of scope_diff.
  const edit_script&
  scope_diff::member_changes() const
  {return priv_->member_changes_;}
  
  /// Accessor of the edit script of the members of a scope.
  ///
  /// This edit script is computed using the equality operator that
  /// applies to shared_ptr<decl_base>.
  ///
  /// That has interesting consequences.  For instance, consider two
  /// scopes S0 and S1.  S0 contains a class C0 and S1 contains a class
  /// S0'.  C0 and C0' have the same qualified name, but have different
  /// members.  The edit script will consider that C0 has been deleted
  /// from S0 and that S0' has been inserted.  This is a low level
  /// canonical representation of the changes; a higher level
  /// representation would give us a simpler way to say "the class C0
  /// has been modified into C0'".  But worry not.  We do have such
  /// higher representation as well; that is what changed_types() and
  /// changed_decls() is for.
  ///
  /// @return the edit script of the changes encapsulatd in this
  /// instance of scope_diff.
  edit_script&
  scope_diff::member_changes()
  {return priv_->member_changes_;}
  
  /// Accessor that eases the manipulation of the edit script associated
  /// to this instance.  It returns the scope member that is reported
  /// (in the edit script) as deleted at a given index.
  ///
  /// @param i the index (in the edit script) of an element of the first
  /// scope that has been reported as being delete.
  ///
  /// @return the scope member that has been reported by the edit script
  /// as being deleted at index i.
  const decl_base_sptr
  scope_diff::deleted_member_at(unsigned i) const
  {
    scope_decl_sptr scope = dynamic_pointer_cast<scope_decl>(first_subject());
   return scope->get_member_decls()[i];
  }
  
  /// Accessor that eases the manipulation of the edit script associated
  /// to this instance.  It returns the scope member (of the first scope
  /// of this diff instance) that is reported (in the edit script) as
  /// deleted at a given iterator.
  ///
  /// @param i the iterator of an element of the first scope that has
  /// been reported as being delete.
  ///
  /// @return the scope member of the first scope of this diff that has
  /// been reported by the edit script as being deleted at iterator i.
  const decl_base_sptr
  scope_diff::deleted_member_at(vector<deletion>::const_iterator i) const
  {return deleted_member_at(i->index());}
  
  /// Accessor that eases the manipulation of the edit script associated
  /// to this instance.  It returns the scope member (of the second
  /// scope of this diff instance) that is reported as being inserted
  /// from a given index.
  ///
  /// @param i the index of an element of the second scope this diff
  /// that has been reported by the edit script as being inserted.
  ///
  /// @return the scope member of the second scope of this diff that has
  /// been reported as being inserted from index i.
  const decl_base_sptr
  scope_diff::inserted_member_at(unsigned i)
  {
    scope_decl_sptr scope = dynamic_pointer_cast<scope_decl>(second_subject());
    return scope->get_member_decls()[i];
  }
  
  /// Accessor that eases the manipulation of the edit script associated
  /// to this instance.  It returns the scope member (of the second
  /// scope of this diff instance) that is reported as being inserted
  /// from a given iterator.
  ///
  /// @param i the iterator of an element of the second scope this diff
  /// that has been reported by the edit script as being inserted.
  ///
  /// @return the scope member of the second scope of this diff that has
  /// been reported as being inserted from iterator i.
  const decl_base_sptr
  scope_diff::inserted_member_at(vector<unsigned>::const_iterator i)
  {return inserted_member_at(*i);}
  
  /// @return a sorted vector of the types which content has changed
  /// from the first scope to the other.
  const diff_sptrs_type&
  scope_diff::changed_types() const
  {return priv_->sorted_changed_types_;}
  
  /// @return a sorted vector of the decls which content has changed
  /// from the first scope to the other.
  const diff_sptrs_type&
  scope_diff::changed_decls() const
  {return priv_->sorted_changed_decls_;}
  
  const string_decl_base_sptr_map&
  scope_diff::removed_types() const
  {return priv_->removed_types_;}
  
  const string_decl_base_sptr_map&
  scope_diff::removed_decls() const
  {return priv_->removed_decls_;}
  
  const string_decl_base_sptr_map&
  scope_diff::added_types() const
  {return priv_->added_types_;}
  
  const string_decl_base_sptr_map&
  scope_diff::added_decls() const
  {return priv_->added_decls_;}
  
  /// @return the pretty representation for the current instance of @ref
  /// scope_diff.
  const string&
  scope_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "scope_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Return true iff the current diff node carries a change.
  ///
  /// Return true iff the current diff node carries a change.
  bool
  scope_diff::has_changes() const
  {
    // TODO: add the number of really removed/added stuff.
    return changed_types().size() + changed_decls().size();
  }
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  scope_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_scope(), *second_scope(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// Report the changes of one scope against another.
  ///
  /// @param out the out stream to report the changes to.
  ///
  /// @param indent the string to use for indentation.
  void
  scope_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Compute the diff between two scopes.
  ///
  /// Note that the two decls must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the first scope to consider in computing the diff.
  ///
  /// @param second the second scope to consider in the diff
  /// computation.  The second scope is diffed against the first scope.
  ///
  /// @param d a pointer to the diff object to populate with the
  /// computed diff.
  ///
  /// @return return the populated \a d parameter passed to this
  /// function.
  ///
  /// @param ctxt the diff context to use.
  scope_diff_sptr
  compute_diff(const scope_decl_sptr	first,
  	     const scope_decl_sptr	second,
  	     scope_diff_sptr		d,
  	     diff_context_sptr		ctxt)
  {
    ABG_ASSERT(d->first_scope() == first && d->second_scope() == second);
  
    if (first && second)
      ABG_ASSERT(first->get_environment() == second->get_environment());
  
    compute_diff(first->get_member_decls().begin(),
  	       first->get_member_decls().end(),
  	       second->get_member_decls().begin(),
  	       second->get_member_decls().end(),
  	       d->member_changes());
  
    d->ensure_lookup_tables_populated();
    d->context(ctxt);
  
    return d;
  }
  
  /// Compute the diff between two scopes.
  ///
  /// Note that the two decls must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first_scope the first scope to consider in computing the diff.
  ///
  /// @param second_scope the second scope to consider in the diff
  /// computation.  The second scope is diffed against the first scope.
  ///
  /// @param ctxt the diff context to use.
  ///
  /// @return return the resulting diff
  scope_diff_sptr
  compute_diff(const scope_decl_sptr	first_scope,
  	     const scope_decl_sptr	second_scope,
  	     diff_context_sptr		ctxt)
  {
    if (first_scope && second_scope)
      ABG_ASSERT(first_scope->get_environment()
  	   == second_scope->get_environment());
  
    scope_diff_sptr d(new scope_diff(first_scope, second_scope, ctxt));
    d = compute_diff(first_scope, second_scope, d, ctxt);
    ctxt->initialize_canonical_diff(d);
    return d;
  }
  
  //</scope_diff stuff>
  
  // <fn_parm_diff stuff>
  
  /// Constructor for the fn_parm_diff type.
  ///
  /// @param first the first subject of the diff.
  ///
  /// @param second the second subject of the diff.
  ///
  /// @param ctxt the context of the diff.  Note that this context
  /// object must stay alive at least during the life time of the
  /// current instance of @ref fn_parm_diff.  Otherwise memory
  /// corruption issues occur.
  fn_parm_diff::fn_parm_diff(const function_decl::parameter_sptr	first,
  			   const function_decl::parameter_sptr	second,
  			   diff_context_sptr			ctxt)
    : decl_diff_base(first, second, ctxt),
      priv_(new priv)
  {
    ABG_ASSERT(first->get_index() == second->get_index());
    priv_->type_diff = compute_diff(first->get_type(),
  				  second->get_type(),
  				  ctxt);
    ABG_ASSERT(priv_->type_diff);
  }
  
  /// Finish the building of the current instance of @ref fn_parm_diff.
  void
  fn_parm_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// Getter for the first subject of this diff node.
  ///
  /// @return the first function_decl::parameter_sptr subject of this
  /// diff node.
  const function_decl::parameter_sptr
  fn_parm_diff::first_parameter() const
  {return dynamic_pointer_cast<function_decl::parameter>(first_subject());}
  
  /// Getter for the second subject of this diff node.
  ///
  /// @return the second function_decl::parameter_sptr subject of this
  /// diff node.
  const function_decl::parameter_sptr
  fn_parm_diff::second_parameter() const
  {return dynamic_pointer_cast<function_decl::parameter>(second_subject());}
  
  /// Getter for the diff representing the changes on the type of the
  /// function parameter involved in the current instance of @ref
  /// fn_parm_diff.
  ///
  /// @return a diff_sptr representing the changes on the type of the
  /// function parameter we are interested in.
  diff_sptr
  fn_parm_diff::type_diff() const
  {return priv_->type_diff;}
  
  /// Build and return a textual representation of the current instance
  /// of @ref fn_parm_diff.
  ///
  /// @return the string representing the current instance of
  /// fn_parm_diff.
  const string&
  fn_parm_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "function_parameter_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Return true iff the current diff node carries a change.
  ///
  /// @return true iff the current diff node carries a change.
  bool
  fn_parm_diff::has_changes() const
  {return *first_parameter() != *second_parameter();}
  
  /// Check if the current diff node carries a local change.
  ///
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  fn_parm_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_parameter(), *second_parameter(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// Emit a textual report about the current fn_parm_diff instance.
  ///
  /// @param out the output stream to emit the textual report to.
  ///
  /// @param indent the indentation string to use in the report.
  void
  fn_parm_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Populate the vector of children nodes of the @ref diff base type
  /// sub-object of this instance of @ref fn_parm_diff.
  ///
  /// The children nodes can then later be retrieved using
  /// diff::children_nodes()
  void
  fn_parm_diff::chain_into_hierarchy()
  {
    if (type_diff())
      append_child_node(type_diff());
  }
  
  /// Compute the difference between two function_decl::parameter_sptr;
  /// that is, between two function parameters.  Return a resulting
  /// fn_parm_diff_sptr that represents the changes.
  ///
  /// Note that the two decls must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the first subject of the diff.
  ///
  /// @param second the second subject of the diff.
  ///
  /// @param ctxt the context of the diff.
  ///
  /// @return fn_parm_diff_sptr the resulting diff node.
  fn_parm_diff_sptr
  compute_diff(const function_decl::parameter_sptr	first,
  	     const function_decl::parameter_sptr	second,
  	     diff_context_sptr				ctxt)
  {
    if (!first || !second)
      return fn_parm_diff_sptr();
  
    ABG_ASSERT(first->get_environment() == second->get_environment());
  
    fn_parm_diff_sptr result(new fn_parm_diff(first, second, ctxt));
    ctxt->initialize_canonical_diff(result);
  
    return result;
  }
  // </fn_parm_diff stuff>
  
  // <function_type_diff stuff>
  
  void
  function_type_diff::ensure_lookup_tables_populated()
  {
    priv_->return_type_diff_ =
      compute_diff(first_function_type()->get_return_type(),
  		 second_function_type()->get_return_type(),
  		 context());
  
    string parm_name;
    function_decl::parameter_sptr parm;
    for (vector<deletion>::const_iterator i =
  	 priv_->parm_changes_.deletions().begin();
         i != priv_->parm_changes_.deletions().end();
         ++i)
      {
        parm = *(first_function_type()->get_first_parm()
  	       + i->index());
        parm_name = parm->get_name_id();
        // If for a reason the type name is empty we want to know and
        // fix that.
        ABG_ASSERT(!parm_name.empty());
        priv_->deleted_parms_[parm_name] = parm;
        priv_->deleted_parms_by_id_[parm->get_index()] = parm;
      }
  
    for (vector<insertion>::const_iterator i =
  	 priv_->parm_changes_.insertions().begin();
         i != priv_->parm_changes_.insertions().end();
         ++i)
      {
        for (vector<unsigned>::const_iterator j =
  	     i->inserted_indexes().begin();
  	   j != i->inserted_indexes().end();
  	   ++j)
  	{
  	  parm = *(second_function_type()->get_first_parm() + *j);
  	  parm_name = parm->get_name_id();
  	  // If for a reason the type name is empty we want to know and
  	  // fix that.
  	  ABG_ASSERT(!parm_name.empty());
  	  {
  	    string_parm_map::const_iterator k =
  	      priv_->deleted_parms_.find(parm_name);
  	    if (k != priv_->deleted_parms_.end())
  	      {
  		if (*k->second != *parm)
  		  priv_->subtype_changed_parms_[parm_name] =
  		    compute_diff(k->second, parm, context());
  		priv_->deleted_parms_.erase(parm_name);
  	      }
  	    else
  	      priv_->added_parms_[parm_name] = parm;
  	  }
  	  {
  	    unsigned_parm_map::const_iterator k =
  	      priv_->deleted_parms_by_id_.find(parm->get_index());
  	    if (k != priv_->deleted_parms_by_id_.end())
  	      {
  		if (*k->second != *parm
  		    && (k->second->get_name_id() != parm_name))
  		  priv_->changed_parms_by_id_[parm->get_index()] =
  		    compute_diff(k->second, parm, context());
  		priv_->added_parms_.erase(parm_name);
  		priv_->deleted_parms_.erase(k->second->get_name_id());
  		priv_->deleted_parms_by_id_.erase(parm->get_index());
  	      }
  	    else
  	      priv_->added_parms_by_id_[parm->get_index()] = parm;
  	  }
  	}
      }
  
    sort_string_fn_parm_diff_sptr_map(priv_->subtype_changed_parms_,
  				    priv_->sorted_subtype_changed_parms_);
    sort_string_fn_parm_diff_sptr_map(priv_->changed_parms_by_id_,
  				    priv_->sorted_changed_parms_by_id_);
    sort_string_parm_map(priv_->deleted_parms_,
  		       priv_->sorted_deleted_parms_);
  
    sort_string_parm_map(priv_->added_parms_,
  		       priv_->sorted_added_parms_);
  }
  
  /// In the vector of deleted parameters, get the one that is at a given
  /// index.
  ///
  /// @param i the index of the deleted parameter to get.
  ///
  /// @return the parameter returned.
  const function_decl::parameter_sptr
  function_type_diff::deleted_parameter_at(int i) const
  {return first_function_type()->get_parameters()[i];}
  
  /// In the vector of inserted parameters, get the one that is at a
  /// given index.
  ///
  /// @param i the index of the inserted parameter to get.
  ///
  /// @return the parameter returned.
  const function_decl::parameter_sptr
  function_type_diff::inserted_parameter_at(int i) const
  {return second_function_type()->get_parameters()[i];}
  
  /// Consutrctor of the @ref function_type type.
  ///
  /// @param first the first @ref function_type subject of the diff to
  /// create.
  ///
  /// @param second the second @ref function_type subject of the diff to
  /// create.
  ///
  /// @param ctxt the diff context to be used by the newly created
  /// instance of function_type_diff.  Note that this context object
  /// must stay alive at least during the life time of the current
  /// instance of @ref function_type_diff.  Otherwise memory corruption
  /// issues occur.
  function_type_diff::function_type_diff(const function_type_sptr first,
  				       const function_type_sptr second,
  				       diff_context_sptr	ctxt)
    : type_diff_base(first, second, ctxt),
      priv_(new priv)
  {}
  
  /// Finish building the current instance of @ref function_type_diff
  void
  function_type_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// Getter for the first subject of the diff.
  ///
  /// @return the first function type involved in the diff.
  const function_type_sptr
  function_type_diff::first_function_type() const
  {return dynamic_pointer_cast<function_type>(first_subject());}
  
  /// Getter for the second subject of the diff.
  ///
  /// @return the second function type involved in the diff.
  const function_type_sptr
  function_type_diff::second_function_type() const
  {return dynamic_pointer_cast<function_type>(second_subject());}
  
  /// Getter for the diff of the return types of the two function types
  /// of the current diff.
  ///
  /// @return the diff of the return types of the two function types of
  /// the current diff.
  const diff_sptr
  function_type_diff::return_type_diff() const
  {return priv_->return_type_diff_;}
  
  /// Getter for the map of function parameter changes of the current diff.
  ///
  /// @return a map of function parameter changes of the current diff.
  const string_fn_parm_diff_sptr_map&
  function_type_diff::subtype_changed_parms() const
  {return priv_->subtype_changed_parms_;}
  
  /// Getter for the map of parameters that got removed.
  ///
  /// @return the map of parameters that got removed.
  const string_parm_map&
  function_type_diff::removed_parms() const
  {return priv_->deleted_parms_;}
  
  /// Getter for the map of parameters that got added.
  ///
  /// @return the map of parameters that got added.
  const string_parm_map&
  function_type_diff::added_parms() const
  {return priv_->added_parms_;}
  
  /// Build and return a copy of a pretty representation of the current
  /// instance of @ref function_type_diff.
  ///
  /// @return a copy of the pretty representation of the current
  /// instance of @ref function_type_diff.
  const string&
  function_type_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "function_type_diff["
  	<< abigail::ir::get_pretty_representation(first_function_type())
  	<< ", "
  	<< abigail::ir::get_pretty_representation(second_function_type())
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Test if the current diff node carries changes.
  ///
  /// @return true iff the current diff node carries changes.
  bool
  function_type_diff::has_changes() const
  {return *first_function_type() != *second_function_type();}
  
  /// Test if the current diff node carries local changes.
  ///
  /// A local change is a change that is carried by this diff node, not
  /// by any of its children nodes.
  ///
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  function_type_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_function_type(), *second_function_type(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// Build and emit a textual report about the current @ref
  /// function_type_diff instance.
  ///
  /// @param out the output stream.
  ///
  /// @param indent the indentation string to use.
  void
  function_type_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref function_type_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  function_type_diff::chain_into_hierarchy()
  {
    if (diff_sptr d = return_type_diff())
      append_child_node(d);
  
    for (vector<fn_parm_diff_sptr>::const_iterator i =
  	 priv_->sorted_subtype_changed_parms_.begin();
         i != priv_->sorted_subtype_changed_parms_.end();
         ++i)
      if (diff_sptr d = *i)
        append_child_node(d);
  
    for (vector<fn_parm_diff_sptr>::const_iterator i =
  	 priv_->sorted_changed_parms_by_id_.begin();
         i != priv_->sorted_changed_parms_by_id_.end();
         ++i)
      if (diff_sptr d = *i)
        append_child_node(d);
  }
  
  /// Compute the diff between two instances of @ref function_type.
  ///
  /// Note that the two types must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the first @ref function_type to consider for the diff.
  ///
  /// @param second the second @ref function_type to consider for the diff.
  ///
  /// @param ctxt the diff context to use.
  ///
  /// @return the resulting diff between the two @ref function_type.
  function_type_diff_sptr
  compute_diff(const function_type_sptr	first,
  	     const function_type_sptr	second,
  	     diff_context_sptr		ctxt)
  {
    if (!first || !second)
      {
        // TODO: implement this for either first or second being NULL.
        return function_type_diff_sptr();
      }
  
    ABG_ASSERT(first->get_environment() == second->get_environment());
  
    function_type_diff_sptr result(new function_type_diff(first, second, ctxt));
  
    diff_utils::compute_diff(first->get_first_parm(),
  			   first->get_parameters().end(),
  			   second->get_first_parm(),
  			   second->get_parameters().end(),
  			   result->priv_->parm_changes_);
  
    result->ensure_lookup_tables_populated();
  
    ctxt->initialize_canonical_diff(result);
  
    return result;
  }
  // </function_type_diff stuff>
  
  // <function_decl_diff stuff>
  
  /// Build the lookup tables of the diff, if necessary.
  void
  function_decl_diff::ensure_lookup_tables_populated()
  {
  }
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref function_decl_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  function_decl_diff::chain_into_hierarchy()
  {
    if (diff_sptr d = type_diff())
      append_child_node(d);
  }
  
  /// Constructor for function_decl_diff
  ///
  /// @param first the first function considered by the diff.
  ///
  /// @param second the second function considered by the diff.
  ///
  /// @param ctxt the context of the diff.  Note that this context
  /// object must stay alive at least during the life time of the
  /// current instance of @ref function_decl_diff.  Otherwise memory
  /// corruption issues occur.
  function_decl_diff::function_decl_diff(const function_decl_sptr first,
  				       const function_decl_sptr second,
  				       diff_context_sptr	ctxt)
    : decl_diff_base(first, second, ctxt),
      priv_(new priv)
  {
  }
  
  /// Finish building the current instance of @ref function_decl_diff.
  void
  function_decl_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// @return the first function considered by the diff.
  const function_decl_sptr
  function_decl_diff::first_function_decl() const
  {return dynamic_pointer_cast<function_decl>(first_subject());}
  
  /// @return the second function considered by the diff.
  const function_decl_sptr
  function_decl_diff::second_function_decl() const
  {return dynamic_pointer_cast<function_decl>(second_subject());}
  
  const function_type_diff_sptr
  function_decl_diff::type_diff() const
  {return priv_->type_diff_;}
  
  /// @return the pretty representation for the current instance of @ref
  /// function_decl_diff.
  const string&
  function_decl_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "function_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Return true iff the current diff node carries a change.
  ///
  /// @return true iff the current diff node carries a change.
  bool
  function_decl_diff::has_changes() const
  {return *first_function_decl() != *second_function_decl();}
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  function_decl_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_function_decl(), *second_function_decl(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// Serialize a report of the changes encapsulated in the current
  /// instance of @ref function_decl_diff over to an output stream.
  ///
  /// @param out the output stream to serialize the report to.
  ///
  /// @param indent the string to use an an indentation prefix.
  void
  function_decl_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Compute the diff between two function_decl.
  ///
  /// Note that the two decls must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first the first function_decl to consider for the diff
  ///
  /// @param second the second function_decl to consider for the diff
  ///
  /// @param ctxt the diff context to use.
  ///
  /// @return the computed diff
  function_decl_diff_sptr
  compute_diff(const function_decl_sptr first,
  	     const function_decl_sptr second,
  	     diff_context_sptr ctxt)
  {
    if (!first || !second)
      {
        // TODO: implement this for either first or second being NULL.
        return function_decl_diff_sptr();
      }
  
    ABG_ASSERT(first->get_environment() == second->get_environment());
  
    function_type_diff_sptr type_diff = compute_diff(first->get_type(),
  						   second->get_type(),
  						   ctxt);
  
    function_decl_diff_sptr result(new function_decl_diff(first, second,
  							ctxt));
    result->priv_->type_diff_ = type_diff;
  
    result->ensure_lookup_tables_populated();
  
    ctxt->initialize_canonical_diff(result);
  
    return result;
  }
  
  // </function_decl_diff stuff>
  
  // <type_decl_diff stuff>
  
  /// Constructor for type_decl_diff.
  ///
  /// @param first the first subject of the diff.
  ///
  /// @param second the second subject of the diff.
  ///
  /// @param ctxt the context of the diff.  Note that this context
  /// object must stay alive at least during the life time of the
  /// current instance of @ref type_decl_diff.  Otherwise memory
  /// corruption issues occur.
  type_decl_diff::type_decl_diff(const type_decl_sptr first,
  			       const type_decl_sptr second,
  			       diff_context_sptr ctxt)
    : type_diff_base(first, second, ctxt)
  {}
  
  /// Finish building the current instance of @ref type_decl_diff.
  void
  type_decl_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    diff::priv_->finished_ = true;
  }
  
  /// Getter for the first subject of the type_decl_diff.
  ///
  /// @return the first type_decl involved in the diff.
  const type_decl_sptr
  type_decl_diff::first_type_decl() const
  {return dynamic_pointer_cast<type_decl>(first_subject());}
  
  /// Getter for the second subject of the type_decl_diff.
  ///
  /// @return the second type_decl involved in the diff.
  const type_decl_sptr
  type_decl_diff::second_type_decl() const
  {return dynamic_pointer_cast<type_decl>(second_subject());}
  
  /// @return the pretty representation for the current instance of @ref
  /// type_decl_diff.
  const string&
  type_decl_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "type_decl_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  /// Return true iff the current diff node carries a change.
  ///
  /// @return true iff the current diff node carries a change.
  bool
  type_decl_diff::has_changes() const
  {return first_type_decl() != second_type_decl();}
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  type_decl_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_type_decl(), *second_type_decl(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  /// Ouputs a report of the differences between of the two type_decl
  /// involved in the type_decl_diff.
  ///
  /// @param out the output stream to emit the report to.
  ///
  /// @param indent the string to use for indentatino indent.
  void
  type_decl_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Compute a diff between two type_decl.
  ///
  /// Note that the two types must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// This function doesn't actually compute a diff.  As a type_decl is
  /// very simple (unlike compound constructs like function_decl or
  /// class_decl) it's easy to just compare the components of the
  /// type_decl to know what has changed.  Thus this function just
  /// builds and return a type_decl_diff object.  The
  /// type_decl_diff::report function will just compare the components
  /// of the the two type_decl and display where and how they differ.
  ///
  /// @param first a pointer to the first type_decl to
  /// consider.
  ///
  /// @param second a pointer to the second type_decl to consider.
  ///
  /// @param ctxt the diff context to use.
  ///
  /// @return a pointer to the resulting type_decl_diff.
  type_decl_diff_sptr
  compute_diff(const type_decl_sptr	first,
  	     const type_decl_sptr	second,
  	     diff_context_sptr		ctxt)
  {
    if (first && second)
      ABG_ASSERT(first->get_environment() == second->get_environment());
  
    type_decl_diff_sptr result(new type_decl_diff(first, second, ctxt));
  
    // We don't need to actually compute a diff here as a type_decl
    // doesn't have complicated sub-components.  type_decl_diff::report
    // just walks the members of the type_decls and display information
    // about the ones that have changed.  On a similar note,
    // type_decl_diff::length returns 0 if the two type_decls are equal,
    // and 1 otherwise.
  
    ctxt->initialize_canonical_diff(result);
  
    return result;
  }
  
  // </type_decl_diff stuff>
  
  // <typedef_diff stuff>
  
  /// Populate the vector of children node of the @ref diff base type
  /// sub-object of this instance of @ref typedef_diff.
  ///
  /// The children node can then later be retrieved using
  /// diff::children_node().
  void
  typedef_diff::chain_into_hierarchy()
  {append_child_node(underlying_type_diff());}
  
  /// Constructor for typedef_diff.
  ///
  /// @param first the first subject of the diff.
  ///
  /// @param second the second subject of the diff.
  ///
  /// @param underlying the underlying diff of the @ref typedef_diff.
  /// That is the diff between the underlying types of @p first and @p
  /// second.
  ///
  /// @param ctxt the context of the diff.  Note that this context
  /// object must stay alive at least during the life time of the
  /// current instance of @ref typedef_diff.  Otherwise memory
  /// corruption issues occur.
  typedef_diff::typedef_diff(const typedef_decl_sptr	first,
  			   const typedef_decl_sptr	second,
  			   const diff_sptr		underlying,
  			   diff_context_sptr		ctxt)
    : type_diff_base(first, second, ctxt),
      priv_(new priv(underlying))
  {}
  
  /// Finish building the current instance of @ref typedef_diff.
  void
  typedef_diff::finish_diff_type()
  {
    if (diff::priv_->finished_)
      return;
    chain_into_hierarchy();
    diff::priv_->finished_ = true;
  }
  
  /// Getter for the firt typedef_decl involved in the diff.
  ///
  /// @return the first subject of the diff.
  const typedef_decl_sptr
  typedef_diff::first_typedef_decl() const
  {return dynamic_pointer_cast<typedef_decl>(first_subject());}
  
  /// Getter for the second typedef_decl involved in the diff.
  ///
  /// @return the second subject of the diff.
  const typedef_decl_sptr
  typedef_diff::second_typedef_decl() const
  {return dynamic_pointer_cast<typedef_decl>(second_subject());}
  
  /// Getter for the diff between the two underlying types of the
  /// typedefs.
  ///
  /// @return the diff object reprensenting the difference between the
  /// two underlying types of the typedefs.
  const diff_sptr
  typedef_diff::underlying_type_diff() const
  {return priv_->underlying_type_diff_;}
  
  /// Setter for the diff between the two underlying types of the
  /// typedefs.
  ///
  /// @param d the new diff object reprensenting the difference between
  /// the two underlying types of the typedefs.
  void
  typedef_diff::underlying_type_diff(const diff_sptr d)
  {priv_->underlying_type_diff_ = d;}
  
  /// @return the pretty representation for the current instance of @ref
  /// typedef_diff.
  const string&
  typedef_diff::get_pretty_representation() const
  {
    if (diff::priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "typedef_diff["
  	<< first_subject()->get_pretty_representation()
  	<< ", "
  	<< second_subject()->get_pretty_representation()
  	<< "]";
        diff::priv_->pretty_representation_ = o.str();
      }
    return diff::priv_->pretty_representation_;
  }
  
  /// Return true iff the current diff node carries a change.
  ///
  /// @return true iff the current diff node carries a change.
  bool
  typedef_diff::has_changes() const
  {
    decl_base_sptr second = second_typedef_decl();
    return !(*first_typedef_decl() == *second);
  }
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  typedef_diff::has_local_changes() const
  {
    ir::change_kind k = ir::NO_CHANGE_KIND;
    if (!equals(*first_typedef_decl(), *second_typedef_decl(), &k))
      return k & ir::ALL_LOCAL_CHANGES_MASK;
    return ir::NO_CHANGE_KIND;
  }
  
  /// Reports the difference between the two subjects of the diff in a
  /// serialized form.
  ///
  /// @param out the output stream to emit the report to.
  ///
  /// @param indent the indentation string to use.
  void
  typedef_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Compute a diff between two typedef_decl.
  ///
  /// Note that the two types must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param first a pointer to the first typedef_decl to consider.
  ///
  /// @param second a pointer to the second typedef_decl to consider.
  ///
  /// @param ctxt the diff context to use.
  ///
  /// @return a pointer to the the resulting typedef_diff.
  typedef_diff_sptr
  compute_diff(const typedef_decl_sptr	first,
  	     const typedef_decl_sptr	second,
  	     diff_context_sptr		ctxt)
  {
    if (first && second)
      ABG_ASSERT(first->get_environment() == second->get_environment());
  
    diff_sptr d = compute_diff_for_types(first->get_underlying_type(),
  				       second->get_underlying_type(),
  				       ctxt);
    typedef_diff_sptr result(new typedef_diff(first, second, d, ctxt));
  
    ctxt->initialize_canonical_diff(result);
  
    return result;
  }
  
  /// Return the leaf underlying diff node of a @ref typedef_diff node.
  ///
  /// If the underlying diff node of a @ref typedef_diff node is itself
  /// a @ref typedef_diff node, then recursively look at the underlying
  /// diff nodes to get the first one that is not a a @ref typedef_diff
  /// node.  This is what a leaf underlying diff node means.
  ///
  /// Otherwise, if the underlying diff node of @ref typedef_diff is
  /// *NOT* a @ref typedef_diff node, then just return the underlying
  /// diff node.
  ///
  /// And if the diff node considered is not a @ref typedef_diff node,
  /// then just return it.
  ///
  /// @return the leaf underlying diff node of a @p diff.
  const diff*
  get_typedef_diff_underlying_type_diff(const diff* diff)
  {
    const typedef_diff* d = dynamic_cast<const typedef_diff*>(diff);
    if (!d)
      return diff;
  
    if (const typedef_diff* deef =
        dynamic_cast<const typedef_diff*>(d->underlying_type_diff().get()))
      return get_typedef_diff_underlying_type_diff(deef);
  
    return d->underlying_type_diff().get();
  }
  
  // </typedef_diff stuff>
  
  // <translation_unit_diff stuff>
  
  /// Constructor for translation_unit_diff.
  ///
  /// @param first the first translation unit to consider for this diff.
  ///
  /// @param second the second translation unit to consider for this diff.
  ///
  /// @param ctxt the context of the diff.  Note that this context
  /// object must stay alive at least during the life time of the
  /// current instance of @ref translation_unit_diff.  Otherwise memory
  /// corruption issues occur.
  translation_unit_diff::translation_unit_diff(translation_unit_sptr first,
  					     translation_unit_sptr second,
  					     diff_context_sptr ctxt)
    : scope_diff(first->get_global_scope(), second->get_global_scope(), ctxt),
      priv_(new priv(first, second))
  {
  }
  
  /// Getter for the first translation unit of this diff.
  ///
  /// @return the first translation unit of this diff.
  const translation_unit_sptr
  translation_unit_diff::first_translation_unit() const
  {return priv_->first_;}
  
  /// Getter for the second translation unit of this diff.
  ///
  /// @return the second translation unit of this diff.
  const translation_unit_sptr
  translation_unit_diff::second_translation_unit() const
  {return priv_->second_;}
  
  /// Return true iff the current diff node carries a change.
  ///
  /// @return true iff the current diff node carries a change.
  bool
  translation_unit_diff::has_changes() const
  {return scope_diff::has_changes();}
  
  /// @return the kind of local change carried by the current diff node.
  /// The value returned is zero if the current node carries no local
  /// change.
  enum change_kind
  translation_unit_diff::has_local_changes() const
  {return ir::NO_CHANGE_KIND;}
  
  /// Report the diff in a serialized form.
  ///
  /// @param out the output stream to serialize the report to.
  ///
  /// @param indent the prefix to use as indentation for the report.
  void
  translation_unit_diff::report(ostream& out, const string& indent) const
  {scope_diff::report(out, indent);}
  
  /// Compute the diff between two translation_units.
  ///
  /// Note that the two translation units must have been created in the
  /// same @ref environment, otherwise, this function aborts.
  ///
  /// @param first the first translation_unit to consider.
  ///
  /// @param second the second translation_unit to consider.
  ///
  /// @param ctxt the diff context to use.  If null, this function will
  /// create a new context and set to the diff object returned.
  ///
  /// @return the newly created diff object.
  translation_unit_diff_sptr
  compute_diff(const translation_unit_sptr	first,
  	     const translation_unit_sptr	second,
  	     diff_context_sptr			ctxt)
  {
    ABG_ASSERT(first && second);
  
    ABG_ASSERT(first->get_environment() == second->get_environment());
  
    if (!ctxt)
      ctxt.reset(new diff_context);
  
    // TODO: handle first or second having empty contents.
    translation_unit_diff_sptr tu_diff(new translation_unit_diff(first, second,
  							       ctxt));
    scope_diff_sptr sc_diff = dynamic_pointer_cast<scope_diff>(tu_diff);
  
    compute_diff(static_pointer_cast<scope_decl>(first->get_global_scope()),
  	       static_pointer_cast<scope_decl>(second->get_global_scope()),
  	       sc_diff,
  	       ctxt);
  
    ctxt->initialize_canonical_diff(tu_diff);
  
    return tu_diff;
  }
  
  // </translation_unit_diff stuff>
  
  // <diff_maps stuff>
  
  /// The private data of the @ref diff_maps type.
  struct diff_maps::priv
  {
    string_diff_ptr_map type_decl_diff_map_;
    string_diff_ptr_map enum_diff_map_;
    string_diff_ptr_map class_diff_map_;
    string_diff_ptr_map union_diff_map_;
    string_diff_ptr_map typedef_diff_map_;
    string_diff_ptr_map array_diff_map_;
    string_diff_ptr_map reference_diff_map_;
    string_diff_ptr_map function_type_diff_map_;
    string_diff_ptr_map function_decl_diff_map_;
    string_diff_ptr_map var_decl_diff_map_;
    string_diff_ptr_map distinct_diff_map_;
    string_diff_ptr_map fn_parm_diff_map_;
    diff_artifact_set_map_type impacted_artifacts_map_;
  }; // end struct diff_maps::priv
  
  /// Default constructor of the @ref diff_maps type.
  diff_maps::diff_maps()
    : priv_(new diff_maps::priv())
  {}
  
  /// Getter of the map that contains basic type diffs.
  ///
  /// @return the map that contains basic type diffs.
  const string_diff_ptr_map&
  diff_maps::get_type_decl_diff_map() const
  {return priv_->type_decl_diff_map_;}
  
  /// Getter of the map that contains basic type diffs.
  ///
  /// @return the map that contains basic type diffs.
  string_diff_ptr_map&
  diff_maps::get_type_decl_diff_map()
  {return priv_->type_decl_diff_map_;}
  
  /// Getter of the map that contains enum type diffs.
  ///
  /// @return the map that contains enum type diffs.
  const string_diff_ptr_map&
  diff_maps::get_enum_diff_map() const
  {return priv_->enum_diff_map_;}
  
  /// Getter of the map that contains enum type diffs.
  ///
  /// @return the map that contains enum type diffs.
  string_diff_ptr_map&
  diff_maps::get_enum_diff_map()
  {return priv_->enum_diff_map_;}
  
  /// Getter of the map that contains class type diffs.
  ///
  /// @return the map that contains class type diffs.
  const string_diff_ptr_map&
  diff_maps::get_class_diff_map() const
  {return priv_->class_diff_map_;}
  
  /// Getter of the map that contains class type diffs.
  ///
  /// @return the map that contains class type diffs.
  string_diff_ptr_map&
  diff_maps::get_class_diff_map()
  {return priv_->class_diff_map_;}
  
  /// Getter of the map that contains union type diffs.
  ///
  /// @return the map that contains union type diffs.
  const string_diff_ptr_map&
  diff_maps::get_union_diff_map() const
  {return priv_->union_diff_map_;}
  
  /// Getter of the map that contains union type diffs.
  ///
  /// @return the map that contains union type diffs.
  string_diff_ptr_map&
  diff_maps::get_union_diff_map()
  {return priv_->union_diff_map_;}
  
  /// Getter of the map that contains typedef type diffs.
  ///
  /// @return the map that contains typedef type diffs.
  const string_diff_ptr_map&
  diff_maps::get_typedef_diff_map() const
  {return priv_->typedef_diff_map_;}
  
  /// Getter of the map that contains typedef type diffs.
  ///
  /// @return the map that contains typedef type diffs.
  string_diff_ptr_map&
  diff_maps::get_typedef_diff_map()
  {return priv_->typedef_diff_map_;}
  
  /// Getter of the map that contains array type diffs.
  ///
  /// @return the map that contains array type diffs.
  const string_diff_ptr_map&
  diff_maps::get_array_diff_map() const
  {return priv_->array_diff_map_;}
  
  /// Getter of the map that contains array type diffs.
  ///
  /// @return the map that contains array type diffs.
  string_diff_ptr_map&
  diff_maps::get_array_diff_map()
  {return priv_->array_diff_map_;}
  
  /// Getter of the map that contains reference type diffs.
  ///
  /// @return the map that contains reference type diffs.
  const string_diff_ptr_map&
  diff_maps::get_reference_diff_map() const
  {return priv_->reference_diff_map_;}
  
  /// Getter of the map that contains reference type diffs.
  ///
  /// @return the map that contains reference type diffs.
  string_diff_ptr_map&
  diff_maps::get_reference_diff_map()
  {{return priv_->reference_diff_map_;}}
  
  /// Getter of the map that contains function parameter diffs.
  ///
  /// @return the map that contains function parameter diffs.
  const string_diff_ptr_map&
  diff_maps::get_fn_parm_diff_map() const
  {return priv_->fn_parm_diff_map_;}
  
  /// Getter of the map that contains function parameter diffs.
  ///
  /// @return the map that contains function parameter diffs.
  string_diff_ptr_map&
  diff_maps::get_fn_parm_diff_map()
  {return priv_->fn_parm_diff_map_;}
  
  /// Getter of the map that contains function type diffs.
  ///
  /// @return the map that contains function type diffs.
  const string_diff_ptr_map&
  diff_maps::get_function_type_diff_map() const
  {return priv_->function_type_diff_map_;}
  
  /// Getter of the map that contains function type diffs.
  ///
  /// @return the map that contains function type diffs.
  string_diff_ptr_map&
  diff_maps::get_function_type_diff_map()
  {return priv_->function_type_diff_map_;}
  
  /// Getter of the map that contains function decl diffs.
  ///
  /// @return the map that contains function decl diffs.
  const string_diff_ptr_map&
  diff_maps::get_function_decl_diff_map() const
  {return priv_->function_decl_diff_map_;}
  
  /// Getter of the map that contains function decl diffs.
  ///
  /// @return the map that contains function decl diffs.
  string_diff_ptr_map&
  diff_maps::get_function_decl_diff_map()
  {return priv_->function_decl_diff_map_;}
  
  /// Getter of the map that contains var decl diffs.
  ///
  /// @return the map that contains var decl diffs.
  const string_diff_ptr_map&
  diff_maps::get_var_decl_diff_map() const
  {return priv_->var_decl_diff_map_;}
  
  /// Getter of the map that contains var decl diffs.
  ///
  /// @return the map that contains var decl diffs.
  string_diff_ptr_map&
  diff_maps::get_var_decl_diff_map()
  {return priv_->var_decl_diff_map_;}
  
  /// Getter of the map that contains distinct diffs.
  ///
  /// @return the map that contains distinct diffs.
  const string_diff_ptr_map&
  diff_maps::get_distinct_diff_map() const
  {return priv_->distinct_diff_map_;}
  
  /// Getter of the map that contains distinct diffs.
  ///
  /// @return the map that contains distinct diffs.
  string_diff_ptr_map&
  diff_maps::get_distinct_diff_map()
  {return priv_->distinct_diff_map_;}
  
  /// Insert a new diff node into the current instance of @ref diff_maps.
  ///
  /// @param dif the new diff node to insert into the @ref diff_maps.
  ///
  /// @param impacted_iface the interface (global function or variable)
  /// currently being analysed that led to analysing the diff node @p
  /// dif.  In other words, this is the interface impacted by the diff
  /// node @p dif.  Note that this can be nil in cases where we are
  /// directly analysing changes to a type that is not reachable from
  /// any global function or variable.
  ///
  /// @return true iff the diff node could be added to the current
  /// instance of @ref diff_maps.
  bool
  diff_maps::insert_diff_node(const diff *dif,
  			    const type_or_decl_base_sptr& impacted_iface)
  {
    string n = get_pretty_representation(dif->first_subject(),
  				       /*internal=*/true);
    if (const type_decl_diff *d = is_diff_of_basic_type(dif))
      get_type_decl_diff_map()[n] = const_cast<type_decl_diff*>(d);
    else if (const enum_diff *d = is_enum_diff(dif))
      get_enum_diff_map()[n] = const_cast<enum_diff*>(d);
    else if (const class_diff *d = is_class_diff(dif))
        get_class_diff_map()[n] = const_cast<class_diff*>(d);
    else if (const union_diff *d = is_union_diff(dif))
      get_union_diff_map()[n] = const_cast<union_diff*>(d);
    else if (const typedef_diff *d = is_typedef_diff(dif))
      get_typedef_diff_map()[n] = const_cast<typedef_diff*>(d);
    else if (const array_diff *d = is_array_diff(dif))
        get_array_diff_map()[n] = const_cast<array_diff*>(d);
    else if (const reference_diff *d = is_reference_diff(dif))
      get_reference_diff_map()[n] = const_cast<reference_diff*>(d);
    else if (const fn_parm_diff *d = is_fn_parm_diff(dif))
      get_fn_parm_diff_map()[n] = const_cast<fn_parm_diff*>(d);
    else if (const function_type_diff *d = is_function_type_diff(dif))
      get_function_type_diff_map()[n] = const_cast<function_type_diff*>(d);
    else if (const var_diff *d = is_var_diff(dif))
      get_var_decl_diff_map()[n] = const_cast<var_diff*>(d);
    else if (const function_decl_diff *d = is_function_decl_diff(dif))
      get_function_decl_diff_map()[n] = const_cast<function_decl_diff*>(d);
    else if (const distinct_diff *d = is_distinct_diff(dif))
      get_distinct_diff_map()[n] = const_cast<distinct_diff*>(d);
    else if (is_base_diff(dif))
      // we silently drop this case.
      return true;
    else
        ABG_ASSERT_NOT_REACHED;
  
    // Update the map that associates this diff node to the set of
    // interfaces it impacts.
  
    if (impacted_iface)
      {
        diff_artifact_set_map_type::iterator i =
  	priv_->impacted_artifacts_map_.find(dif);
  
        if (i == priv_->impacted_artifacts_map_.end())
  	{
  	  artifact_sptr_set_type set;
  	  set.insert(impacted_iface);
  	  priv_->impacted_artifacts_map_[dif] = set;
  	}
        else
  	i->second.insert(impacted_iface);
      }
  
    return true;
  }
  
  /// Lookup the interfaces that are impacted by a given leaf diff node.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return the set of artifacts impacted by @p d.
  artifact_sptr_set_type*
  diff_maps::lookup_impacted_interfaces(const diff *d) const
  {
    diff_artifact_set_map_type::iterator i =
      priv_->impacted_artifacts_map_.find(d);
  
    if (i == priv_->impacted_artifacts_map_.end())
      return 0;
  
    return &i->second;
  }
  
  //
  // </diff_maps stuff>
  
  /// Constructor for the @ref diff_stat type.
  ///
  /// @param ctxt the context of the corpus diff.  Note that this
  /// context object must stay alive at least during the life time of
  /// the current instance of @ref corpus_diff::diff_stats.  Otherwise
  /// memory corruption issues occur.
  corpus_diff::diff_stats::diff_stats(diff_context_sptr ctxt)
    : priv_(new priv(ctxt))
  {}
  
  /// Getter for the number of functions removed.
  ///
  /// @return the number of functions removed.
  size_t
  corpus_diff::diff_stats::num_func_removed() const
  {return priv_->num_func_removed;}
  
  /// Setter for the number of functions removed.
  ///
  /// @param n the new number of functions removed.
  void
  corpus_diff::diff_stats::num_func_removed(size_t n)
  {priv_->num_func_removed = n;}
  
  /// Getter for the number of removed functions that have been filtered
  /// out.
  ///
  /// @return the number of removed functions that have been filtered
  /// out.
  size_t
  corpus_diff::diff_stats::num_removed_func_filtered_out() const
  {
    if (priv_->ctxt() && !priv_->ctxt()->show_deleted_fns())
      return num_func_removed();
    return priv_->num_removed_func_filtered_out;
  }
  
  /// Setter for the number of removed functions that have been filtered
  /// out.
  ///
  /// @param t the new value.
  void
  corpus_diff::diff_stats::num_removed_func_filtered_out(size_t t)
  {priv_->num_removed_func_filtered_out = t;}
  
  /// Getter for the net number of function removed.
  ///
  /// This is the difference between the number of functions removed and
  /// the number of functons removed that have been filtered out.
  ///
  /// @return the net number of function removed.
  size_t
  corpus_diff::diff_stats::net_num_func_removed() const
  {
    ABG_ASSERT(num_func_removed() >= num_removed_func_filtered_out());
    return num_func_removed() - num_removed_func_filtered_out();
  }
  
  /// Getter for the number of functions added.
  ///
  /// @return the number of functions added.
  size_t
  corpus_diff::diff_stats::num_func_added() const
  {return priv_->num_func_added;}
  
  /// Setter for the number of functions added.
  ///
  /// @param n the new number of functions added.
  void
  corpus_diff::diff_stats::num_func_added(size_t n)
  {priv_->num_func_added = n;}
  
  /// Getter for the number of added function that have been filtered out.
  ///
  /// @return the number of added function that have been filtered out.
  size_t
  corpus_diff::diff_stats::num_added_func_filtered_out() const
  {
    if (priv_->ctxt() && !priv_->ctxt()->show_added_fns())
      return num_func_added();
    return priv_->num_added_func_filtered_out;
  }
  
  /// Setter for the number of added function that have been filtered
  /// out.
  ///
  /// @param n the new value.
  void
  corpus_diff::diff_stats::num_added_func_filtered_out(size_t n)
  {priv_->num_added_func_filtered_out = n;}
  
  /// Getter for the net number of added functions.
  ///
  /// The net number of added functions is the difference between the
  /// number of added functions and the number of added functions that
  /// have been filtered out.
  ///
  /// @return the net number of added functions.
  size_t
  corpus_diff::diff_stats::net_num_func_added() const
  {
    ABG_ASSERT(num_func_added() >= num_added_func_filtered_out());
    return num_func_added() - num_added_func_filtered_out();
  }
  
  /// Getter for the number of functions that have a change in one of
  /// their sub-types.
  ///
  /// @return the number of functions that have a change in one of their
  /// sub-types.
  size_t
  corpus_diff::diff_stats::num_func_changed() const
  {return priv_->num_func_changed;}
  
  /// Setter for the number of functions that have a change in one of
  /// their sub-types.
  ///
  /// @@param n the new number of functions that have a change in one of
  /// their sub-types.
  void
  corpus_diff::diff_stats::num_func_changed(size_t n)
  {priv_->num_func_changed = n;}
  
  /// Getter for the number of functions that have a change in one of
  /// their sub-types, and that have been filtered out.
  ///
  /// @return the number of functions that have a change in one of their
  /// sub-types, and that have been filtered out.
  size_t
  corpus_diff::diff_stats::num_changed_func_filtered_out() const
  {return priv_->num_changed_func_filtered_out;}
  
  /// Setter for the number of functions that have a change in one of
  /// their sub-types, and that have been filtered out.
  ///
  /// @param n the new number of functions that have a change in one of their
  /// sub-types, and that have been filtered out.
  void
  corpus_diff::diff_stats::num_changed_func_filtered_out(size_t n)
  {priv_->num_changed_func_filtered_out = n;}
  
  /// Getter for the number of functions that carry virtual member
  /// offset changes.
  ///
  /// @return the number of functions that carry virtual member changes.
  size_t
  corpus_diff::diff_stats::num_func_with_virtual_offset_changes() const
  {return priv_->num_func_with_virt_offset_changes;}
  
  /// Setter for the number of functions that carry virtual member
  /// offset changes.
  ///
  /// @param n the new number of functions that carry virtual member
  /// offset.  changes.
  void
  corpus_diff::diff_stats::num_func_with_virtual_offset_changes(size_t n)
  {priv_->num_func_with_virt_offset_changes = n;}
  
  /// Getter for the number of functions that have a change in their
  /// sub-types, minus the number of these functions that got filtered
  /// out from the diff.
  ///
  /// @return for the the number of functions that have a change in
  /// their sub-types, minus the number of these functions that got
  /// filtered out from the diff.
  size_t
  corpus_diff::diff_stats::net_num_func_changed() const
  {return num_func_changed() - num_changed_func_filtered_out();}
  
  /// Getter for the number of variables removed.
  ///
  /// @return the number of variables removed.
  size_t
  corpus_diff::diff_stats::num_vars_removed() const
  {return priv_->num_vars_removed;}
  
  /// Setter for the number of variables removed.
  ///
  /// @param n the new number of variables removed.
  void
  corpus_diff::diff_stats::num_vars_removed(size_t n)
  {priv_->num_vars_removed = n;}
  
  /// Getter for the number removed variables that have been filtered
  /// out.
  ///
  /// @return the number removed variables that have been filtered out.
  size_t
  corpus_diff::diff_stats::num_removed_vars_filtered_out() const
  {
    if (priv_->ctxt() && !priv_->ctxt()->show_deleted_vars())
      return num_vars_removed();
    return priv_->num_removed_vars_filtered_out;
  }
  
  /// Setter for the number of removed variables that have been filtered
  /// out.
  ///
  /// @param n the new value.
  void
  corpus_diff::diff_stats::num_removed_vars_filtered_out(size_t n) const
  {priv_->num_removed_vars_filtered_out = n;}
  
  /// Getter for the net number of removed variables.
  ///
  /// The net number of removed variables is the difference between the
  /// number of removed variables and the number of removed variables
  /// that have been filtered out.
  ///
  /// @return the net number of removed variables.
  size_t
  corpus_diff::diff_stats::net_num_vars_removed() const
  {
    ABG_ASSERT(num_vars_removed() >= num_removed_vars_filtered_out());
    return num_vars_removed() - num_removed_vars_filtered_out();
  }
  
  /// Getter for the number of variables added.
  ///
  /// @return the number of variables added.
  size_t
  corpus_diff::diff_stats::num_vars_added() const
  {return priv_->num_vars_added;}
  
  /// Setter for the number of variables added.
  ///
  /// @param n the new number of variables added.
  void
  corpus_diff::diff_stats::num_vars_added(size_t n)
  {priv_->num_vars_added = n;}
  
  /// Getter for the number of added variables that have been filtered
  /// out.
  ///
  /// @return the number of added variables that have been filtered out.
  size_t
  corpus_diff::diff_stats::num_added_vars_filtered_out() const
  {
    if (priv_->ctxt() && !priv_->ctxt()->show_added_vars())
      return num_vars_added();
    return priv_->num_added_vars_filtered_out;
  }
  
  /// Setter for the number of added variables that have been filtered
  /// out.
  ///
  /// @param n the new value.
  void
  corpus_diff::diff_stats::num_added_vars_filtered_out(size_t n)
  {priv_->num_added_vars_filtered_out = n;}
  
  /// Getter for the net number of added variables.
  ///
  /// The net number of added variables is the difference between the
  /// number of added variables and the number of added variables that
  /// have been filetered out.
  ///
  /// @return the net number of added variables.
  size_t
  corpus_diff::diff_stats::net_num_vars_added() const
  {
    ABG_ASSERT(num_vars_added() >= num_added_vars_filtered_out());
    return num_vars_added() - num_added_vars_filtered_out();
  }
  
  /// Getter for the number of variables that have a change in one of
  /// their sub-types.
  ///
  /// @return the number of variables that have a change in one of their
  /// sub-types.
  size_t
  corpus_diff::diff_stats::num_vars_changed() const
  {return priv_->num_vars_changed;}
  
  /// Setter for the number of variables that have a change in one of
  /// their sub-types.
  ///
  /// @param n the new number of variables that have a change in one of
  /// their sub-types.
  void
  corpus_diff::diff_stats::num_vars_changed(size_t n)
  {priv_->num_vars_changed = n;}
  
  /// Getter for the number of variables that have a change in one of
  /// their sub-types, and that have been filtered out.
  ///
  /// @return the number of functions that have a change in one of their
  /// sub-types, and that have been filtered out.
  size_t
  corpus_diff::diff_stats::num_changed_vars_filtered_out() const
  {return priv_->num_changed_vars_filtered_out;}
  
  /// Setter for the number of variables that have a change in one of
  /// their sub-types, and that have been filtered out.
  ///
  /// @param n the new number of variables that have a change in one of their
  /// sub-types, and that have been filtered out.
  void
  corpus_diff::diff_stats::num_changed_vars_filtered_out(size_t n)
  {priv_->num_changed_vars_filtered_out = n;}
  
  /// Getter for the number of variables that have a change in their
  /// sub-types, minus the number of these variables that got filtered
  /// out from the diff.
  ///
  /// @return for the the number of variables that have a change in
  /// their sub-types, minus the number of these variables that got
  /// filtered out from the diff.
  size_t
  corpus_diff::diff_stats::net_num_vars_changed() const
  {return num_vars_changed() - num_changed_vars_filtered_out();}
  
  /// Getter for the number of function symbols (not referenced by any
  /// debug info) that got removed.
  ///
  /// @return the number of function symbols (not referenced by any
  /// debug info) that got removed.
  size_t
  corpus_diff::diff_stats::num_func_syms_removed() const
  {return priv_->num_func_syms_removed;}
  
  /// Setter for the number of function symbols (not referenced by any
  /// debug info) that got removed.
  ///
  /// @param n the number of function symbols (not referenced by any
  /// debug info) that got removed.
  void
  corpus_diff::diff_stats::num_func_syms_removed(size_t n)
  {priv_->num_func_syms_removed = n;}
  
  /// Getter for the number of removed function symbols, not referenced
  /// by debug info, that have been filtered out.
  ///
  /// @return the number of removed function symbols, not referenced by
  /// debug info, that have been filtered out.
  size_t
  corpus_diff::diff_stats::num_removed_func_syms_filtered_out() const
  {
    if (priv_->ctxt()
        && !priv_->ctxt()->show_symbols_unreferenced_by_debug_info())
      return num_func_syms_removed();
    return priv_->num_removed_func_syms_filtered_out;
  }
  
  /// Setter for the number of removed function symbols, not referenced
  /// by debug info, that have been filtered out.
  ///
  /// @param n the new the number of removed function symbols, not
  /// referenced by debug info, that have been filtered out.
  void
  corpus_diff::diff_stats::num_removed_func_syms_filtered_out(size_t n)
  {priv_->num_removed_func_syms_filtered_out = n;}
  
  /// Getter of the net number of removed function symbols that are not
  /// referenced by any debug info.
  ///
  /// This is the difference between the total number of removed
  /// function symbols and the number of removed function symbols that
  /// have been filteted out.  Both numbers are for symbols not
  /// referenced by debug info.
  ///
  /// return the net number of removed function symbols that are not
  /// referenced by any debug info.
  size_t
  corpus_diff::diff_stats::net_num_removed_func_syms() const
  {
    ABG_ASSERT(num_func_syms_removed() >= num_removed_func_syms_filtered_out());
    return num_func_syms_removed() - num_removed_func_syms_filtered_out();
  }
  
  /// Getter for the number of function symbols (not referenced by any
  /// debug info) that got added.
  ///
  /// @return the number of function symbols (not referenced by any
  /// debug info) that got added.
  size_t
  corpus_diff::diff_stats::num_func_syms_added() const
  {return priv_->num_func_syms_added;}
  
  /// Setter for the number of function symbols (not referenced by any
  /// debug info) that got added.
  ///
  /// @param n the new number of function symbols (not referenced by any
  /// debug info) that got added.
  void
  corpus_diff::diff_stats::num_func_syms_added(size_t n)
  {priv_->num_func_syms_added = n;}
  
  /// Getter for the number of added function symbols, not referenced by
  /// any debug info, that have been filtered out.
  ///
  /// @return the number of added function symbols, not referenced by
  /// any debug info, that have been filtered out.
  size_t
  corpus_diff::diff_stats::num_added_func_syms_filtered_out() const
  {
    if (priv_->ctxt()
        && !(priv_->ctxt()->show_added_symbols_unreferenced_by_debug_info()
  	   && priv_->ctxt()->show_symbols_unreferenced_by_debug_info()))
      return num_func_syms_added();
    return priv_->num_added_func_syms_filtered_out;
  }
  
  /// Setter for the number of added function symbols, not referenced by
  /// any debug info, that have been filtered out.
  ///
  /// @param n the new number of added function symbols, not referenced
  /// by any debug info, that have been filtered out.
  void
  corpus_diff::diff_stats::num_added_func_syms_filtered_out(size_t n)
  {priv_->num_added_func_syms_filtered_out = n;}
  
  /// Getter of the net number of added function symbols that are not
  /// referenced by any debug info.
  ///
  /// This is the difference between the total number of added
  /// function symbols and the number of added function symbols that
  /// have been filteted out.  Both numbers are for symbols not
  /// referenced by debug info.
  ///
  /// return the net number of added function symbols that are not
  /// referenced by any debug info.
  size_t
  corpus_diff::diff_stats::net_num_added_func_syms() const
  {
    ABG_ASSERT(num_func_syms_added() >= num_added_func_syms_filtered_out());
    return num_func_syms_added()- num_added_func_syms_filtered_out();
  }
  
  /// Getter for the number of variable symbols (not referenced by any
  /// debug info) that got removed.
  ///
  /// @return the number of variable symbols (not referenced by any
  /// debug info) that got removed.
  size_t
  corpus_diff::diff_stats::num_var_syms_removed() const
  {return priv_->num_var_syms_removed;}
  
  /// Setter for the number of variable symbols (not referenced by any
  /// debug info) that got removed.
  ///
  /// @param n the number of variable symbols (not referenced by any
  /// debug info) that got removed.
  void
  corpus_diff::diff_stats::num_var_syms_removed(size_t n)
  {priv_->num_var_syms_removed = n;}
  
  /// Getter for the number of removed variable symbols, not referenced
  /// by any debug info, that have been filtered out.
  ///
  /// @return the number of removed variable symbols, not referenced
  /// by any debug info, that have been filtered out.
  size_t
  corpus_diff::diff_stats::num_removed_var_syms_filtered_out() const
  {
    if (priv_->ctxt()
        && !priv_->ctxt()->show_symbols_unreferenced_by_debug_info())
      return num_var_syms_removed();
    return priv_->num_removed_var_syms_filtered_out;
  }
  
  /// Setter for the number of removed variable symbols, not referenced
  /// by any debug info, that have been filtered out.
  ///
  /// @param n the number of removed variable symbols, not referenced by
  /// any debug info, that have been filtered out.
  void
  corpus_diff::diff_stats::num_removed_var_syms_filtered_out(size_t n)
  {priv_->num_removed_var_syms_filtered_out = n;}
  
  /// Getter of the net number of removed variable symbols that are not
  /// referenced by any debug info.
  ///
  /// This is the difference between the total number of removed
  /// variable symbols and the number of removed variable symbols that
  /// have been filteted out.  Both numbers are for symbols not
  /// referenced by debug info.
  ///
  /// return the net number of removed variable symbols that are not
  /// referenced by any debug info.
  size_t
  corpus_diff::diff_stats::net_num_removed_var_syms() const
  {
    ABG_ASSERT(num_var_syms_removed() >= num_removed_var_syms_filtered_out());
    return num_var_syms_removed() - num_removed_var_syms_filtered_out();
  }
  
  /// Getter for the number of variable symbols (not referenced by any
  /// debug info) that got added.
  ///
  /// @return the number of variable symbols (not referenced by any
  /// debug info) that got added.
  size_t
  corpus_diff::diff_stats::num_var_syms_added() const
  {return priv_->num_var_syms_added;}
  
  /// Setter for the number of variable symbols (not referenced by any
  /// debug info) that got added.
  ///
  /// @param n the new number of variable symbols (not referenced by any
  /// debug info) that got added.
  void
  corpus_diff::diff_stats::num_var_syms_added(size_t n)
  {priv_->num_var_syms_added = n;}
  
  /// Getter for the number of added variable symbols, not referenced by
  /// any debug info, that have been filtered out.
  ///
  /// @return the number of added variable symbols, not referenced by
  /// any debug info, that have been filtered out.
  size_t
  corpus_diff::diff_stats::num_added_var_syms_filtered_out() const
  {
    if (priv_->ctxt()
        && !(priv_->ctxt()->show_added_symbols_unreferenced_by_debug_info()
  	   && priv_->ctxt()->show_symbols_unreferenced_by_debug_info()))
      return num_var_syms_added();
    return priv_->num_added_var_syms_filtered_out;
  }
  
  /// Setter for the number of added variable symbols, not referenced by
  /// any debug info, that have been filtered out.
  ///
  /// @param n the new number of added variable symbols, not referenced
  /// by any debug info, that have been filtered out.
  void
  corpus_diff::diff_stats::num_added_var_syms_filtered_out(size_t n)
  {priv_->num_added_var_syms_filtered_out = n;}
  
  /// Getter of the net number of added variable symbols that are not
  /// referenced by any debug info.
  ///
  /// This is the difference between the total number of added
  /// variable symbols and the number of added variable symbols that
  /// have been filteted out.  Both numbers are for symbols not
  /// referenced by debug info.
  ///
  /// return the net number of added variable symbols that are not
  /// referenced by any debug info.
  size_t
  corpus_diff::diff_stats::net_num_added_var_syms() const
  {
    ABG_ASSERT(num_var_syms_added() >= num_added_var_syms_filtered_out());
    return num_var_syms_added() - num_added_var_syms_filtered_out();
  }
  
  /// Getter of the number of leaf type change diff nodes.
  ///
  /// @return the number of leaf type change diff nodes.
  size_t
  corpus_diff::diff_stats::num_leaf_changes() const
  {return priv_->num_leaf_changes;}
  
  /// Setter of the number of leaf type change diff nodes.
  ///
  /// @param n the new number of leaf type change diff nodes.
  void
  corpus_diff::diff_stats::num_leaf_changes(size_t n)
  {priv_->num_leaf_changes = n;}
  
  /// Getter of the number of leaf type change diff nodes that have been
  /// filtered out.
  ///
  /// @return the number of leaf type change diff nodes that have been
  size_t
  corpus_diff::diff_stats::num_leaf_changes_filtered_out() const
  {return priv_->num_leaf_changes_filtered_out;}
  
  /// Setter of the number of leaf type change diff nodes that have been
  /// filtered out.
  ///
  /// @param n the new number of leaf type change diff nodes that have
  /// been filtered out.
  void
  corpus_diff::diff_stats::num_leaf_changes_filtered_out(size_t n)
  {priv_->num_leaf_changes_filtered_out = n;}
  
  /// Getter of the net number of leaf change diff nodes.
  ///
  /// This is the difference between the total number of leaf change
  /// diff nodes, and the number of the leaf change diff nodes that have
  /// been filtered out.
  ///
  /// A leaf change is either a type change, a function change or a
  /// variable change.
  size_t
  corpus_diff::diff_stats::net_num_leaf_changes() const
  {
    ABG_ASSERT(num_leaf_changes() >= num_leaf_changes_filtered_out());
    return num_leaf_changes() - num_leaf_changes_filtered_out();
  }
  
  /// Getter for the number of leaf type change diff nodes.
  ///
  /// @return the number of leaf type changes diff nodes.
  size_t
  corpus_diff::diff_stats::num_leaf_type_changes() const
  {return priv_->num_leaf_type_changes;}
  
  /// Setter for the number of leaf type change diff nodes.
  ///
  /// @param n the new number of leaf type change diff nodes.
  void
  corpus_diff::diff_stats::num_leaf_type_changes(size_t n)
  {priv_->num_leaf_type_changes = n;}
  
  /// Getter for the number of filtered out leaf type change diff nodes.
  ///
  /// @return the number of filtered out leaf type change diff nodes.
  size_t
  corpus_diff::diff_stats::num_leaf_type_changes_filtered_out() const
  {return priv_->num_leaf_type_changes_filtered_out;}
  
  /// Setter for the number of filtered out leaf type change diff nodes.
  /// @param n the new number of filtered out leaf type change diff nodes.
  void
  corpus_diff::diff_stats::num_leaf_type_changes_filtered_out(size_t n)
  {priv_->num_leaf_type_changes_filtered_out = n;}
  
  /// Getter for the net number of leaf type change diff nodes.
  ///
  /// This is the difference between the number of leaf type changes and
  /// the number of filtered out leaf type changes.
  ///
  /// @return the net number of leaf type change diff nodes.
  size_t
  corpus_diff::diff_stats::net_num_leaf_type_changes() const
  {return num_leaf_type_changes() - num_leaf_type_changes_filtered_out();}
  
  /// Getter for the number of leaf function change diff nodes.
  ///
  /// @return the number of leaf function change diff nodes.
  size_t
  corpus_diff::diff_stats::num_leaf_func_changes() const
  {return priv_->num_leaf_func_changes;}
  
  /// Setter for the number of leaf function change diff nodes.
  ///
  /// @param n the new number of leaf function change diff nodes.
  void
  corpus_diff::diff_stats::num_leaf_func_changes(size_t n)
  {priv_->num_leaf_func_changes = n;}
  
  /// Getter for the number of leaf function change diff nodes that were
  /// filtered out.
  ///
  /// @return the number of leaf function change diff nodes that were
  /// filtered out.
  size_t
  corpus_diff::diff_stats::num_leaf_func_changes_filtered_out() const
  {return priv_->num_leaf_func_changes_filtered_out;}
  
  /// Setter for the number of leaf function change diff nodes that were
  /// filtered out.
  ///
  /// @param n the new number of leaf function change diff nodes that
  /// were filtered out.
  void
  corpus_diff::diff_stats::num_leaf_func_changes_filtered_out(size_t n)
  {priv_->num_leaf_func_changes_filtered_out = n;}
  
  /// Getter for the net number of leaf function change diff nodes.
  ///
  /// This is the difference between the number of leaf function change
  /// diff nodes and the number of filtered out leaf function change
  /// diff nodes.
  ///
  /// @return the net number of leaf function change diff nodes.
  size_t
  corpus_diff::diff_stats::net_num_leaf_func_changes() const
  {return num_leaf_func_changes() - num_leaf_func_changes_filtered_out();}
  
  /// Getter for the number of leaf variable change diff nodes.
  ///
  /// @return the number of leaf variable change diff nodes.
  size_t
  corpus_diff::diff_stats::num_leaf_var_changes() const
  {return priv_->num_leaf_var_changes;}
  
  /// Setter for the number of leaf variable change diff nodes.
  ///
  /// @param n the number of leaf variable change diff nodes.
  void
  corpus_diff::diff_stats::num_leaf_var_changes(size_t n)
  {priv_->num_leaf_var_changes = n;}
  
  /// Getter of the number of added types that are unreachable from the
  /// public interface of the ABI corpus.
  ///
  /// Public interface means the set of defined and publicly exported
  /// functions and variables of the ABI corpus.
  ///
  /// @return the number of added types that are unreachable from the
  /// public interface of the ABI corpus.
  size_t
  corpus_diff::diff_stats::num_added_unreachable_types() const
  {return priv_->num_added_unreachable_types;}
  
  /// Setter of the number of added types that are unreachable from the
  /// public interface (global functions or variables) of the ABI
  /// corpus.
  ///
  /// Public interface means the set of defined and publicly exported
  /// functions and variables of the ABI corpus.
  ///
  /// @param n the new number of added types that are unreachable from
  /// the public interface of the ABI corpus.
  void
  corpus_diff::diff_stats::num_added_unreachable_types(size_t n)
  {priv_->num_added_unreachable_types = n;}
  
  /// Getter of the number of added types that are unreachable from
  /// public interfaces and that are filtered out by suppression
  /// specifications.
  ///
  /// @return the number of added types that are unreachable from public
  /// interfaces and that are filtered out by suppression
  /// specifications.
  size_t
  corpus_diff::diff_stats::num_added_unreachable_types_filtered_out() const
  {return priv_->num_added_unreachable_types_filtered_out;}
  
  /// Setter of the number of added types that are unreachable from
  /// public interfaces and that are filtered out by suppression
  /// specifications.
  ///
  /// @param n the new number of added types that are unreachable from
  /// public interfaces and that are filtered out by suppression
  /// specifications.
  void
  corpus_diff::diff_stats::num_added_unreachable_types_filtered_out(size_t n)
  {priv_->num_added_unreachable_types_filtered_out = n;}
  
  /// Getter of the number of added types that are unreachable from
  /// public interfaces and that are *NOT* filtered out by suppression
  /// specifications.
  ///
  /// @return the number of added types that are unreachable from public
  /// interfaces and that are *NOT* filtered out by suppression
  /// specifications.
  size_t
  corpus_diff::diff_stats::net_num_added_unreachable_types() const
  {
    ABG_ASSERT(num_added_unreachable_types()
  	     >=
  	     num_added_unreachable_types_filtered_out());
  
    return (num_added_unreachable_types()
  	  -
  	  num_added_unreachable_types_filtered_out());
  }
  
  /// Getter of the number of removed types that are unreachable from
  /// the public interface of the ABI corpus.
  ///
  /// Public interface means the set of defined and publicly exported
  /// functions and variables of the ABI corpus.
  ///
  /// @return the number of removed types that are unreachable from
  /// the public interface of the ABI corpus.
  size_t
  corpus_diff::diff_stats::num_removed_unreachable_types() const
  {return priv_->num_removed_unreachable_types;}
  
  /// Setter of the number of removed types that are unreachable from
  /// the public interface of the ABI corpus.
  ///
  /// Public interface means the set of defined and publicly exported
  /// functions and variables of the ABI corpus.
  ///
  ///@param n the new number of removed types that are unreachable from
  /// the public interface of the ABI corpus.
  void
  corpus_diff::diff_stats::num_removed_unreachable_types(size_t n)
  {priv_->num_removed_unreachable_types = n;}
  
  /// Getter of the number of removed types that are not reachable from
  /// public interfaces and that have been filtered out by suppression
  /// specifications.
  ///
  /// @return the number of removed types that are not reachable from
  /// public interfaces and that have been filtered out by suppression
  /// specifications.
  size_t
  corpus_diff::diff_stats::num_removed_unreachable_types_filtered_out() const
  {return priv_->num_removed_unreachable_types_filtered_out;}
  
  /// Setter of the number of removed types that are not reachable from
  /// public interfaces and that have been filtered out by suppression
  /// specifications.
  ///
  /// @param n the new number of removed types that are not reachable
  /// from public interfaces and that have been filtered out by
  /// suppression specifications.
  void
  corpus_diff::diff_stats::num_removed_unreachable_types_filtered_out(size_t n)
  {priv_->num_removed_unreachable_types_filtered_out = n;}
  
  /// Getter of the number of removed types that are not reachable from
  /// public interfaces and that have *NOT* been filtered out by
  /// suppression specifications.
  ///
  /// @return the number of removed types that are not reachable from
  /// public interfaces and that have *NOT* been filtered out by
  /// suppression specifications.
  size_t
  corpus_diff::diff_stats::net_num_removed_unreachable_types() const
  {
    ABG_ASSERT(num_removed_unreachable_types()
  	     >=
  	     num_removed_unreachable_types_filtered_out());
  
    return (num_removed_unreachable_types()
  	  -
  	  num_removed_unreachable_types_filtered_out());
  }
  
  /// Getter of the number of changed types that are unreachable from
  /// the public interface of the ABI corpus.
  ///
  /// Public interface means the set of defined and publicly exported
  /// functions and variables of the ABI corpus.
  ///
  /// @return the number of changed types that are unreachable from the
  /// public interface of the ABI corpus.
  size_t
  corpus_diff::diff_stats::num_changed_unreachable_types() const
  {return priv_->num_changed_unreachable_types;}
  
  /// Setter of the number of changed types that are unreachable from
  /// the public interface of the ABI corpus.
  ///
  /// Public interface means the set of defined and publicly exported
  /// functions and variables of the ABI corpus.
  ///
  ///@param n the new number of changed types that are unreachable from
  /// the public interface of the ABI corpus.
  void
  corpus_diff::diff_stats::num_changed_unreachable_types(size_t n)
  {priv_->num_changed_unreachable_types = n;}
  
  /// Getter of the number of changed types that are unreachable from
  /// public interfaces and that have been filtered out by suppression
  /// specifications.
  ///
  /// @return the number of changed types that are unreachable from
  /// public interfaces and that have been filtered out by suppression
  /// specifications.
  size_t
  corpus_diff::diff_stats::num_changed_unreachable_types_filtered_out() const
  {return priv_->num_changed_unreachable_types_filtered_out;}
  
  /// Setter of the number of changed types that are unreachable from
  /// public interfaces and that have been filtered out by suppression
  /// specifications.
  ///
  /// @param n the new number of changed types that are unreachable from
  /// public interfaces and that have been filtered out by suppression
  /// specifications.
  void
  corpus_diff::diff_stats::num_changed_unreachable_types_filtered_out(size_t n)
  {priv_->num_changed_unreachable_types_filtered_out = n;}
  
  /// Getter of the number of changed types that are unreachable from
  /// public interfaces and that have *NOT* been filtered out by
  /// suppression specifications.
  ///
  /// @return the number of changed types that are unreachable from
  /// public interfaces and that have *NOT* been filtered out by
  /// suppression specifications.
  size_t
  corpus_diff::diff_stats::net_num_changed_unreachable_types() const
  {
    ABG_ASSERT(num_changed_unreachable_types()
  	     >=
  	     num_changed_unreachable_types_filtered_out());
  
    return (num_changed_unreachable_types()
  	  -
  	  num_changed_unreachable_types_filtered_out());
  }
  
  /// Getter for the number of leaf variable changes diff nodes that
  /// have been filtered out.
  ///
  /// @return the number of leaf variable changes diff nodes that have
  /// been filtered out.
  size_t
  corpus_diff::diff_stats::num_leaf_var_changes_filtered_out() const
  {return priv_->num_leaf_var_changes_filtered_out;}
  
  /// Setter for the number of leaf variable changes diff nodes that
  /// have been filtered out.
  ///
  /// @param n the number of leaf variable changes diff nodes that have
  /// been filtered out.
  void
  corpus_diff::diff_stats::num_leaf_var_changes_filtered_out(size_t n)
  {priv_->num_leaf_var_changes_filtered_out = n;}
  
  /// Getter for the net number of leaf variable change diff nodes.
  ///
  /// This the difference between the number of leaf variable change
  /// diff nodes and the number of filtered out leaf variable change
  /// diff nodes.
  ///
  /// @return the net number of leaf variable change diff nodes.
  size_t
  corpus_diff::diff_stats::net_num_leaf_var_changes() const
  {return num_leaf_var_changes() - num_leaf_var_changes_filtered_out();}
  
  
  // <corpus_diff stuff>
  
  /// Getter of the context associated with this corpus.
  ///
  /// @return a smart pointer to the context associate with the corpus.
  diff_context_sptr
  corpus_diff::priv::get_context()
  {return ctxt_.lock();}
  
  /// Tests if the lookup tables are empty.
  ///
  /// @return true if the lookup tables are empty, false otherwise.
  bool
  corpus_diff::priv::lookup_tables_empty() const
  {
    return (deleted_fns_.empty()
  	  && added_fns_.empty()
  	  && changed_fns_map_.empty()
  	  && deleted_vars_.empty()
  	  && added_vars_.empty()
  	  && changed_vars_map_.empty());
  }
  
  /// Clear the lookup tables useful for reporting an enum_diff.
  void
  corpus_diff::priv::clear_lookup_tables()
  {
    deleted_fns_.clear();
    added_fns_.clear();
    changed_fns_map_.clear();
    deleted_vars_.clear();
    added_vars_.clear();
    changed_vars_map_.clear();
  }
  
  /// If the lookup tables are not yet built, walk the differences and
  /// fill the lookup tables.
  void
  corpus_diff::priv::ensure_lookup_tables_populated()
  {
    if (!lookup_tables_empty())
      return;
  
    diff_context_sptr ctxt = get_context();
  
    {
      edit_script& e = fns_edit_script_;
  
      for (vector<deletion>::const_iterator it = e.deletions().begin();
  	 it != e.deletions().end();
  	 ++it)
        {
  	unsigned i = it->index();
  	ABG_ASSERT(i < first_->get_functions().size());
  
  	function_decl* deleted_fn = first_->get_functions()[i];
  	string n = deleted_fn->get_id();
  	ABG_ASSERT(!n.empty());
  	// The below is commented out because there can be several
  	// functions with the same ID in the corpus.  So several
  	// functions with the same ID can be deleted.
  	// ABG_ASSERT(deleted_fns_.find(n) == deleted_fns_.end());
  	deleted_fns_[n] = deleted_fn;
        }
  
      for (vector<insertion>::const_iterator it = e.insertions().begin();
  	 it != e.insertions().end();
  	 ++it)
        {
  	for (vector<unsigned>::const_iterator iit =
  	       it->inserted_indexes().begin();
  	     iit != it->inserted_indexes().end();
  	     ++iit)
  	  {
  	    unsigned i = *iit;
  	    function_decl* added_fn = second_->get_functions()[i];
  	    string n = added_fn->get_id();
  	    ABG_ASSERT(!n.empty());
  	    // The below is commented out because there can be several
  	    // functions with the same ID in the corpus.  So several
  	    // functions with the same ID can be added.
  	    // ABG_ASSERT(added_fns_.find(n) == added_fns_.end());
  	    string_function_ptr_map::const_iterator j =
  	      deleted_fns_.find(n);
  	    if (j != deleted_fns_.end())
  	      {
  		function_decl_sptr f(j->second, noop_deleter());
  		function_decl_sptr s(added_fn, noop_deleter());
  		function_decl_diff_sptr d = compute_diff(f, s, ctxt);
  		if (*j->second != *added_fn)
  		  changed_fns_map_[j->first] = d;
  		deleted_fns_.erase(j);
  	      }
  	    else
  	      added_fns_[n] = added_fn;
  	  }
        }
      sort_string_function_decl_diff_sptr_map(changed_fns_map_, changed_fns_);
  
      // Now walk the allegedly deleted functions; check if their
      // underlying symbols are deleted as well; otherwise, consider
      // that the function in question hasn't been deleted.
  
      vector<string> to_delete;
      for (string_function_ptr_map::const_iterator i = deleted_fns_.begin();
  	 i != deleted_fns_.end();
  	 ++i)
        if (second_->lookup_function_symbol(*i->second->get_symbol()))
  	to_delete.push_back(i->first);
  
      for (vector<string>::const_iterator i = to_delete.begin();
  	 i != to_delete.end();
  	 ++i)
        deleted_fns_.erase(*i);
  
      // Do something similar for added functions.
  
      to_delete.clear();
      for (string_function_ptr_map::const_iterator i = added_fns_.begin();
  	 i != added_fns_.end();
  	 ++i)
        {
  	if (first_->lookup_function_symbol(*i->second->get_symbol()))
  	  to_delete.push_back(i->first);
  	else if (! i->second->get_symbol()->get_version().is_empty()
  		 && i->second->get_symbol()->get_version().is_default())
  	  // We are looking for a symbol that has a default version,
  	  // and which seems to be newly added.  Let's see if the same
  	  // symbol with *no* version was already present in the
  	  // former corpus.  If yes, then the symbol shouldn't be
  	  // considered as 'added'.
  	  {
  	    elf_symbol::version empty_version;
  	    if (first_->lookup_function_symbol(i->second->get_symbol()->get_name(),
  					       empty_version))
  	      to_delete.push_back(i->first);
  	  }
        }
  
      for (vector<string>::const_iterator i = to_delete.begin();
  	 i != to_delete.end();
  	 ++i)
        added_fns_.erase(*i);
    }
  
    {
      edit_script& e = vars_edit_script_;
  
      for (vector<deletion>::const_iterator it = e.deletions().begin();
  	 it != e.deletions().end();
  	 ++it)
        {
  	unsigned i = it->index();
  	ABG_ASSERT(i < first_->get_variables().size());
  
  	var_decl* deleted_var = first_->get_variables()[i];
  	string n = deleted_var->get_id();
  	ABG_ASSERT(!n.empty());
  	ABG_ASSERT(deleted_vars_.find(n) == deleted_vars_.end());
  	deleted_vars_[n] = deleted_var;
        }
  
      for (vector<insertion>::const_iterator it = e.insertions().begin();
  	 it != e.insertions().end();
  	 ++it)
        {
  	for (vector<unsigned>::const_iterator iit =
  	       it->inserted_indexes().begin();
  	     iit != it->inserted_indexes().end();
  	     ++iit)
  	  {
  	    unsigned i = *iit;
  	    var_decl* added_var = second_->get_variables()[i];
  	    string n = added_var->get_id();
  	    ABG_ASSERT(!n.empty());
  	    {
  	      string_var_ptr_map::const_iterator k = added_vars_.find(n);
  	      if ( k != added_vars_.end())
  		{
  		  ABG_ASSERT(is_member_decl(k->second)
  			 && get_member_is_static(k->second));
  		  continue;
  		}
  	    }
  	    string_var_ptr_map::const_iterator j =
  	      deleted_vars_.find(n);
  	    if (j != deleted_vars_.end())
  	      {
  		if (*j->second != *added_var)
  		  {
  		    var_decl_sptr f(j->second, noop_deleter());
  		    var_decl_sptr s(added_var, noop_deleter());
  		    changed_vars_map_[n] = compute_diff(f, s, ctxt);
  		  }
  		deleted_vars_.erase(j);
  	      }
  	    else
  	      added_vars_[n] = added_var;
  	  }
        }
      sort_string_var_diff_sptr_map(changed_vars_map_,
  				  sorted_changed_vars_);
  
      // Now walk the allegedly deleted variables; check if their
      // underlying symbols are deleted as well; otherwise consider
      // that the variable in question hasn't been deleted.
  
      vector<string> to_delete;
      for (string_var_ptr_map::const_iterator i = deleted_vars_.begin();
  	 i != deleted_vars_.end();
  	 ++i)
        if (second_->lookup_variable_symbol(*i->second->get_symbol()))
  	to_delete.push_back(i->first);
  
      for (vector<string>::const_iterator i = to_delete.begin();
  	 i != to_delete.end();
  	 ++i)
        deleted_vars_.erase(*i);
  
      // Do something similar for added variables.
  
      to_delete.clear();
      for (string_var_ptr_map::const_iterator i = added_vars_.begin();
  	 i != added_vars_.end();
  	 ++i)
        if (first_->lookup_variable_symbol(*i->second->get_symbol()))
  	to_delete.push_back(i->first);
        else if (! i->second->get_symbol()->get_version().is_empty()
  		 && i->second->get_symbol()->get_version().is_default())
  	// We are looking for a symbol that has a default version,
  	// and which seems to be newly added.  Let's see if the same
  	// symbol with *no* version was already present in the
  	// former corpus.  If yes, then the symbol shouldn't be
  	// considered as 'added'.
  	{
  	  elf_symbol::version empty_version;
  	  if (first_->lookup_variable_symbol(i->second->get_symbol()->get_name(),
  					     empty_version))
  	    to_delete.push_back(i->first);
  	}
  
      for (vector<string>::const_iterator i = to_delete.begin();
  	 i != to_delete.end();
  	 ++i)
        added_vars_.erase(*i);
    }
  
    // Massage the edit script for added/removed function symbols that
    // were not referenced by any debug info and turn them into maps of
    // {symbol_name, symbol}.
    {
      edit_script& e = unrefed_fn_syms_edit_script_;
      for (vector<deletion>::const_iterator it = e.deletions().begin();
  	 it != e.deletions().end();
  	 ++it)
        {
  	unsigned i = it->index();
  	ABG_ASSERT(i < first_->get_unreferenced_function_symbols().size());
  	elf_symbol_sptr deleted_sym =
  	  first_->get_unreferenced_function_symbols()[i];
  	if (!second_->lookup_function_symbol(*deleted_sym))
  	  deleted_unrefed_fn_syms_[deleted_sym->get_id_string()] = deleted_sym;
        }
  
      for (vector<insertion>::const_iterator it = e.insertions().begin();
  	 it != e.insertions().end();
  	 ++it)
        {
  	for (vector<unsigned>::const_iterator iit =
  	       it->inserted_indexes().begin();
  	     iit != it->inserted_indexes().end();
  	     ++iit)
  	  {
  	    unsigned i = *iit;
  	    ABG_ASSERT(i < second_->get_unreferenced_function_symbols().size());
  	    elf_symbol_sptr added_sym =
  	      second_->get_unreferenced_function_symbols()[i];
  	    if ((deleted_unrefed_fn_syms_.find(added_sym->get_id_string())
  		 == deleted_unrefed_fn_syms_.end()))
  	      {
  		if (!first_->lookup_function_symbol(*added_sym))
  		  {
  		    bool do_add = true;
  		    if (! added_sym->get_version().is_empty()
  			&& added_sym->get_version().is_default())
  		      {
  			// So added_seem has a default version.  If
  			// the former corpus had a symbol with the
  			// same name as added_sym but with *no*
  			// version, then added_sym shouldn't be
  			// considered as a newly added symbol.
  			elf_symbol::version empty_version;
  			if (first_->lookup_function_symbol(added_sym->get_name(),
  							   empty_version))
  			  do_add = false;
  		      }
  
  		    if (do_add)
  		      added_unrefed_fn_syms_[added_sym->get_id_string()] =
  			added_sym;
  		  }
  	      }
  	    else
  	      deleted_unrefed_fn_syms_.erase(added_sym->get_id_string());
  	  }
        }
    }
  
    // Massage the edit script for added/removed variable symbols that
    // were not referenced by any debug info and turn them into maps of
    // {symbol_name, symbol}.
    {
      edit_script& e = unrefed_var_syms_edit_script_;
      for (vector<deletion>::const_iterator it = e.deletions().begin();
  	 it != e.deletions().end();
  	 ++it)
        {
  	unsigned i = it->index();
  	ABG_ASSERT(i < first_->get_unreferenced_variable_symbols().size());
  	elf_symbol_sptr deleted_sym =
  	  first_->get_unreferenced_variable_symbols()[i];
  	if (!second_->lookup_variable_symbol(*deleted_sym))
  	  deleted_unrefed_var_syms_[deleted_sym->get_id_string()] = deleted_sym;
        }
  
      for (vector<insertion>::const_iterator it = e.insertions().begin();
  	 it != e.insertions().end();
  	 ++it)
        {
  	for (vector<unsigned>::const_iterator iit =
  	       it->inserted_indexes().begin();
  	     iit != it->inserted_indexes().end();
  	     ++iit)
  	  {
  	    unsigned i = *iit;
  	    ABG_ASSERT(i < second_->get_unreferenced_variable_symbols().size());
  	    elf_symbol_sptr added_sym =
  	      second_->get_unreferenced_variable_symbols()[i];
  	    if (deleted_unrefed_var_syms_.find(added_sym->get_id_string())
  		== deleted_unrefed_var_syms_.end())
  	      {
  		if (!first_->lookup_variable_symbol(*added_sym))
  		  {
  		    bool do_add = true;
  		    if (! added_sym->get_version().is_empty()
  			&& added_sym->get_version().is_default())
  		      {
  			// So added_seem has a default version.  If
  			// the former corpus had a symbol with the
  			// same name as added_sym but with *no*
  			// version, then added_sym shouldn't be
  			// considered as a newly added symbol.
  			elf_symbol::version empty_version;
  			if (first_->lookup_variable_symbol(added_sym->get_name(),
  							   empty_version))
  			  do_add = false;
  		      }
  
  		    if (do_add)
  		      added_unrefed_var_syms_[added_sym->get_id_string()] =
  			added_sym;
  		  }
  	      }
  	    else
  	      deleted_unrefed_var_syms_.erase(added_sym->get_id_string());
  	  }
        }
    }
  
    // Handle the unreachable_types_edit_script_
    {
      edit_script& e = unreachable_types_edit_script_;
  
      // Populate the map of deleted unreachable types from the
      // deletions of the edit script.
      for (vector<deletion>::const_iterator it = e.deletions().begin();
  	 it != e.deletions().end();
  	 ++it)
        {
  	unsigned i = it->index();
  	type_base_sptr t
  	  (first_->get_types_not_reachable_from_public_interfaces()[i]);
  
  	if (!is_user_defined_type(t))
  	  continue;
  
  	string repr = abigail::ir::get_pretty_representation(t, true);
  	deleted_unreachable_types_[repr] = t;
        }
  
      // Populate the map of added and change unreachable types from the
      // insertions of the edit script.
      for (vector<insertion>::const_iterator it = e.insertions().begin();
  	 it != e.insertions().end();
  	 ++it)
        {
  	for (vector<unsigned>::const_iterator iit =
  	       it->inserted_indexes().begin();
  	     iit != it->inserted_indexes().end();
  	     ++iit)
  	  {
  	    unsigned i = *iit;
  	    type_base_sptr t
  	      (second_->get_types_not_reachable_from_public_interfaces()[i]);
  
  	    if (!is_user_defined_type(t))
  	      continue;
  
  	    string repr = abigail::ir::get_pretty_representation(t, true);
  
  	    // Let's see if the inserted type we are looking at was
  	    // reported as deleted as well.
  	    //
  	    // If it's been deleted and a different version of it has
  	    // now been added, it means it's been *changed*.  In that
  	    // case we'll compute the diff of that change and store it
  	    // in the map of changed unreachable types.
  	    //
  	    // Otherwise, it means the type's been added so we'll add
  	    // it to the set of added unreachable types.
  
  	    string_type_base_sptr_map::const_iterator j =
  	      deleted_unreachable_types_.find(repr);
  	    if (j != deleted_unreachable_types_.end())
  	      {
  		// So there was another type of the same pretty
  		// representation which was reported as deleted.
  		// Let's see if they are different or not ...
  		decl_base_sptr old_type = is_decl(j->second);
  		decl_base_sptr new_type = is_decl(t);
  		if (old_type != new_type)
  		  {
  		    // The previously added type is different from this
  		    // one that is added.  That means the initial type
  		    // was changed.  Let's compute its diff and store it
  		    // as a changed type.
  		    diff_sptr d = compute_diff(old_type, new_type, ctxt);
  		    ABG_ASSERT(d->has_changes());
  		    changed_unreachable_types_[repr]= d;
  		  }
  
  		// In any case, the type was both deleted and added,
  		// so we cannot have it marked as being deleted.  So
  		// let's remove it from the deleted types.
  		deleted_unreachable_types_.erase(j);
  	      }
  	    else
  	      // The type wasn't previously reported as deleted, so
  	      // it's really added.
  	      added_unreachable_types_[repr] = t;
  	  }
        }
    }
  }
  
  /// Test if a change reports about a given @ref function_decl that is
  /// changed in a certain way is suppressed by a given suppression
  /// specifiation
  ///
  /// @param fn the @ref function_decl to consider.
  ///
  /// @param suppr the suppression specification to consider.
  ///
  /// @param k the kind of change that happened to @p fn.
  ///
  /// @param ctxt the context of the current diff.
  ///
  /// @return true iff the suppression specification @p suppr suppresses
  /// change reports about function @p fn, if that function changes in
  /// the way expressed by @p k.
  static bool
  function_is_suppressed(const function_decl* fn,
  		       const suppression_sptr suppr,
  		       function_suppression::change_kind k,
  		       const diff_context_sptr ctxt)
  {
    function_suppression_sptr fn_suppr = is_function_suppression(suppr);
    if (!fn_suppr)
      return false;
    return fn_suppr->suppresses_function(fn, k, ctxt);
  }
  
  /// Test if a change reports about a given @ref var_decl that is
  /// changed in a certain way is suppressed by a given suppression
  /// specifiation
  ///
  /// @param fn the @ref var_decl to consider.
  ///
  /// @param suppr the suppression specification to consider.
  ///
  /// @param k the kind of change that happened to @p fn.
  ///
  /// @param ctxt the context of the current diff.
  ///
  /// @return true iff the suppression specification @p suppr suppresses
  /// change reports about variable @p fn, if that variable changes in
  /// the way expressed by @p k.
  static bool
  variable_is_suppressed(const var_decl* var,
  		       const suppression_sptr suppr,
  		       variable_suppression::change_kind k,
  		       const diff_context_sptr ctxt)
  {
    variable_suppression_sptr var_suppr = is_variable_suppression(suppr);
    if (!var_suppr)
      return false;
    return var_suppr->suppresses_variable(var, k, ctxt);
  }
  
  /// Apply suppression specifications for this corpus diff to the set
  /// of added/removed functions/variables, as well as to types not
  /// reachable from global functions/variables.
  void
  corpus_diff::priv::apply_supprs_to_added_removed_fns_vars_unreachable_types()
  {
    diff_context_sptr ctxt = get_context();
  
    const suppressions_type& suppressions = ctxt->suppressions();
    for (suppressions_type::const_iterator i = suppressions.begin();
         i != suppressions.end();
         ++i)
      {
        // Added/Deleted functions.
        if (function_suppression_sptr fn_suppr = is_function_suppression(*i))
  	{
  	  // Added functions
  	  for (string_function_ptr_map::const_iterator e = added_fns_.begin();
  	       e != added_fns_.end();
  	       ++e)
  	    if (function_is_suppressed(e->second, fn_suppr,
  				       function_suppression::ADDED_FUNCTION_CHANGE_KIND,
  				       ctxt))
  	      suppressed_added_fns_[e->first] = e->second;
  
  	  // Deleted functions.
  	  for (string_function_ptr_map::const_iterator e = deleted_fns_.begin();
  	       e != deleted_fns_.end();
  	       ++e)
  	    if (function_is_suppressed(e->second, fn_suppr,
  				       function_suppression::DELETED_FUNCTION_CHANGE_KIND,
  				       ctxt))
  	      suppressed_deleted_fns_[e->first] = e->second;
  
  	  // Added function symbols not referenced by any debug info
  	  for (string_elf_symbol_map::const_iterator e =
  		 added_unrefed_fn_syms_.begin();
  	       e != added_unrefed_fn_syms_.end();
  	       ++e)
  	    if (fn_suppr->suppresses_function_symbol(e->second,
  						     function_suppression::ADDED_FUNCTION_CHANGE_KIND,
  						     ctxt))
  	      suppressed_added_unrefed_fn_syms_[e->first] = e->second;
  
  	  // Removed function symbols not referenced by any debug info
  	  for (string_elf_symbol_map::const_iterator e =
  		 deleted_unrefed_fn_syms_.begin();
  	       e != deleted_unrefed_fn_syms_.end();
  	       ++e)
  	    if (fn_suppr->suppresses_function_symbol(e->second,
  						     function_suppression::DELETED_FUNCTION_CHANGE_KIND,
  						     ctxt))
  	      suppressed_deleted_unrefed_fn_syms_[e->first] = e->second;
  	}
        // Added/Delete virtual member functions changes that might be
        // suppressed by a type_suppression that matches the enclosing
        // class of the virtual member function.
        else if (type_suppression_sptr type_suppr = is_type_suppression(*i))
  	{
  	  // Added virtual functions
  	  for (string_function_ptr_map::const_iterator e = added_fns_.begin();
  	       e != added_fns_.end();
  	       ++e)
  	    if (is_member_function(e->second)
  		&& get_member_function_is_virtual(e->second))
  	      {
  		function_decl *f = e->second;
  		class_decl_sptr c =
  		  is_class_type(is_method_type(f->get_type())->get_class_type());
  		ABG_ASSERT(c);
  		if (type_suppr->suppresses_type(c, ctxt))
  		  suppressed_added_fns_[e->first] = e->second;
  	      }
  	  // Deleted virtual functions
  	  for (string_function_ptr_map::const_iterator e = deleted_fns_.begin();
  	       e != deleted_fns_.end();
  	       ++e)
  	    if (is_member_function(e->second)
  		&& get_member_function_is_virtual(e->second))
  	      {
  		function_decl *f = e->second;
  		class_decl_sptr c =
  		  is_class_type(is_method_type(f->get_type())->get_class_type());
  		ABG_ASSERT(c);
  		if (type_suppr->suppresses_type(c, ctxt))
  		  suppressed_deleted_fns_[e->first] = e->second;
  	      }
  
  	  // Apply this type suppression to deleted types
  	  // non-reachable from a public interface.
  	  for (string_type_base_sptr_map::const_iterator e =
  		 deleted_unreachable_types_.begin();
  	       e != deleted_unreachable_types_.end();
  	       ++e)
  	    if (type_suppr->suppresses_type(e->second, ctxt))
  	      suppressed_deleted_unreachable_types_[e->first] = e->second;
  
  	  // Apply this type suppression to added types
  	  // non-reachable from a public interface.
  	  for (string_type_base_sptr_map::const_iterator e =
  		 added_unreachable_types_.begin();
  	       e != added_unreachable_types_.end();
  	       ++e)
  	    if (type_suppr->suppresses_type(e->second, ctxt))
  	      suppressed_added_unreachable_types_[e->first] = e->second;
  	}
        // Added/Deleted variables
        else if (variable_suppression_sptr var_suppr =
  	       is_variable_suppression(*i))
  	{
  	  // Added variables
  	  for (string_var_ptr_map::const_iterator e = added_vars_.begin();
  	       e != added_vars_.end();
  	       ++e)
  	    if (variable_is_suppressed(e->second, var_suppr,
  				       variable_suppression::ADDED_VARIABLE_CHANGE_KIND,
  				       ctxt))
  	      suppressed_added_vars_[e->first] = e->second;
  
  	  //Deleted variables
  	  for (string_var_ptr_map::const_iterator e = deleted_vars_.begin();
  	       e != deleted_vars_.end();
  	       ++e)
  	    if (variable_is_suppressed(e->second, var_suppr,
  				       variable_suppression::DELETED_VARIABLE_CHANGE_KIND,
  				       ctxt))
  	      suppressed_deleted_vars_[e->first] = e->second;
  
  	  // Added variable symbols not referenced by any debug info
  	  for (string_elf_symbol_map::const_iterator e =
  		 added_unrefed_var_syms_.begin();
  	       e != added_unrefed_var_syms_.end();
  	       ++e)
  	    if (var_suppr->suppresses_variable_symbol(e->second,
  						      variable_suppression::ADDED_VARIABLE_CHANGE_KIND,
  						      ctxt))
  	      suppressed_added_unrefed_var_syms_[e->first] = e->second;
  
  	  // Removed variable symbols not referenced by any debug info
  	  for (string_elf_symbol_map::const_iterator e =
  		 deleted_unrefed_var_syms_.begin();
  	       e != deleted_unrefed_var_syms_.end();
  	       ++e)
  	    if (var_suppr->suppresses_variable_symbol(e->second,
  						      variable_suppression::DELETED_VARIABLE_CHANGE_KIND,
  						      ctxt))
  	      suppressed_deleted_unrefed_var_syms_[e->first] = e->second;
  	}
      }
  }
  
  /// Test if the change reports for a given deleted function have
  /// been deleted.
  ///
  /// @param fn the function to consider.
  ///
  /// @return true iff the change reports for a give given deleted
  /// function have been deleted.
  bool
  corpus_diff::priv::deleted_function_is_suppressed(const function_decl* fn) const
  {
    if (!fn)
      return false;
  
    string_function_ptr_map::const_iterator i =
      suppressed_deleted_fns_.find(fn->get_id());
  
    return (i != suppressed_deleted_fns_.end());
  }
  
  /// Test if an added type that is unreachable from public interface
  /// has been suppressed by a suppression specification.
  ///
  /// @param t the added unreachable type to be considered.
  ///
  /// @return true iff @p t has been suppressed by a suppression
  /// specification.
  bool
  corpus_diff::priv::added_unreachable_type_is_suppressed(const type_base *t)const
  {
    if (!t)
      return false;
  
    string repr = abigail::ir::get_pretty_representation(t, /*internal=*/true);
    string_type_base_sptr_map::const_iterator i =
      suppressed_added_unreachable_types_.find(repr);
    if (i == suppressed_added_unreachable_types_.end())
      return false;
  
    return true;
  }
  
  /// Test if a deleted type that is unreachable from public interface
  /// has been suppressed by a suppression specification.
  ///
  /// @param t the deleted unreachable type to be considered.
  ///
  /// @return true iff @p t has been suppressed by a suppression
  /// specification.
  bool
  corpus_diff::priv::deleted_unreachable_type_is_suppressed(const type_base *t) const
  {
    if (!t)
      return false;
  
    string repr = abigail::ir::get_pretty_representation(t, /*internal=*/true);
    string_type_base_sptr_map::const_iterator i =
      suppressed_deleted_unreachable_types_.find(repr);
    if (i == suppressed_deleted_unreachable_types_.end())
      return false;
  
    return true;
  }
  
  /// Test if the change reports for a give given added function has
  /// been deleted.
  ///
  /// @param fn the function to consider.
  ///
  /// @return true iff the change reports for a give given added
  /// function has been deleted.
  bool
  corpus_diff::priv::added_function_is_suppressed(const function_decl* fn) const
  {
    if (!fn)
      return false;
  
    string_function_ptr_map::const_iterator i =
      suppressed_added_fns_.find(fn->get_id());
  
    return (i != suppressed_added_fns_.end());
  }
  
  /// Test if the change reports for a give given deleted variable has
  /// been deleted.
  ///
  /// @param var the variable to consider.
  ///
  /// @return true iff the change reports for a give given deleted
  /// variable has been deleted.
  bool
  corpus_diff::priv::deleted_variable_is_suppressed(const var_decl* var) const
  {
    if (!var)
      return false;
  
    string_var_ptr_map::const_iterator i =
      suppressed_deleted_vars_.find(var->get_id());
  
    return (i != suppressed_deleted_vars_.end());
  }
  
  /// Test if the change reports for a given added variable have been
  /// suppressed.
  ///
  /// @param var the variable to consider.
  ///
  /// @return true iff the change reports for a given deleted
  /// variable has been deleted.
  bool
  corpus_diff::priv::added_variable_is_suppressed(const var_decl* var) const
  {
    if (!var)
      return false;
  
    string_var_ptr_map::const_iterator i =
      suppressed_added_vars_.find(var->get_id());
  
    return (i != suppressed_added_vars_.end());
  }
  
  /// Test if the change reports for a given deleted function symbol
  /// (that is not referenced by any debug info) has been suppressed.
  ///
  /// @param var the function to consider.
  ///
  /// @return true iff the change reports for a given deleted function
  /// symbol has been suppressed.
  bool
  corpus_diff::priv::deleted_unrefed_fn_sym_is_suppressed(const elf_symbol* s) const
  {
    if (!s)
      return false;
  
    string_elf_symbol_map::const_iterator i =
      suppressed_deleted_unrefed_fn_syms_.find(s->get_id_string());
  
    return (i != suppressed_deleted_unrefed_fn_syms_.end());
  }
  
  /// Test if the change reports for a given added function symbol
  /// (that is not referenced by any debug info) has been suppressed.
  ///
  /// @param var the function to consider.
  ///
  /// @return true iff the change reports for a given added function
  /// symbol has been suppressed.
  bool
  corpus_diff::priv::added_unrefed_fn_sym_is_suppressed(const elf_symbol* s) const
  {
    if (!s)
      return false;
  
    string_elf_symbol_map::const_iterator i =
      suppressed_added_unrefed_fn_syms_.find(s->get_id_string());
  
    return (i != suppressed_added_unrefed_fn_syms_.end());
  }
  
  /// Test if the change reports for a given deleted variable symbol
  /// (that is not referenced by any debug info) has been suppressed.
  ///
  /// @param var the variable to consider.
  ///
  /// @return true iff the change reports for a given deleted variable
  /// symbol has been suppressed.
  bool
  corpus_diff::priv::deleted_unrefed_var_sym_is_suppressed(const elf_symbol* s) const
  {
    if (!s)
      return false;
  
    string_elf_symbol_map::const_iterator i =
      suppressed_deleted_unrefed_var_syms_.find(s->get_id_string());
  
    return (i != suppressed_deleted_unrefed_var_syms_.end());
  }
  
  /// Test if the change reports for a given added variable symbol
  /// (that is not referenced by any debug info) has been suppressed.
  ///
  /// @param var the variable to consider.
  ///
  /// @return true iff the change reports for a given added variable
  /// symbol has been suppressed.
  bool
  corpus_diff::priv::added_unrefed_var_sym_is_suppressed(const elf_symbol* s) const
  {
    if (!s)
      return false;
  
    string_elf_symbol_map::const_iterator i =
      suppressed_added_unrefed_var_syms_.find(s->get_id_string());
  
    return (i != suppressed_added_unrefed_var_syms_.end());
  }
  
  #ifdef do_count_diff_map_changes
  #undef do_count_diff_map_changes
  #endif
  #define do_count_diff_map_changes(diff_map, n_changes, n_filtered)	\
    {									\
      string_diff_ptr_map::const_iterator i;				\
      for (i = diff_map.begin();						\
  	 i != diff_map.end();						\
  	 ++i)								\
        { \
  	if (const var_diff* d = is_var_diff(i->second))		\
  	  if (is_data_member(d->first_var()))				\
  	    continue;							\
  									\
  	if (i->second->has_local_changes())				\
  	  ++n_changes;							\
  	if (!i->second->get_canonical_diff()->to_be_reported())		\
  	  ++n_filtered;						\
        }								\
    }
  
  /// Count the number of leaf changes as well as the number of the
  /// changes that have been filtered out.
  ///
  /// @param num_changes out parameter.  This is set to the total number
  /// of leaf changes.
  ///
  /// @param num_filtered out parameter.  This is set to the number of
  /// leaf changes that have been filtered out.
  void
  corpus_diff::priv::count_leaf_changes(size_t &num_changes, size_t &num_filtered)
  {
    count_leaf_type_changes(num_changes, num_filtered);
  
    // Now count the non-type changes.
    do_count_diff_map_changes(leaf_diffs_.get_function_decl_diff_map(),
      num_changes, num_filtered);
    do_count_diff_map_changes(leaf_diffs_.get_var_decl_diff_map(),
      num_changes, num_filtered);
  }
  
  /// Count the number of leaf *type* changes as well as the number of
  /// the leaf type changes that have been filtered out.
  ///
  /// @param num_changes out parameter.  This is set to the total number
  /// of leaf type changes.
  ///
  /// @param num_filtered out parameter.  This is set to the number of
  /// leaf type changes that have been filtered out.
  void
  corpus_diff::priv::count_leaf_type_changes(size_t &num_changes,
  					   size_t &num_filtered)
  {
    do_count_diff_map_changes(leaf_diffs_.get_type_decl_diff_map(),
      num_changes, num_filtered);
    do_count_diff_map_changes(leaf_diffs_.get_enum_diff_map(),
      num_changes, num_filtered);
    do_count_diff_map_changes(leaf_diffs_.get_class_diff_map(),
      num_changes, num_filtered);
    do_count_diff_map_changes(leaf_diffs_.get_union_diff_map(),
      num_changes, num_filtered);
    do_count_diff_map_changes(leaf_diffs_.get_typedef_diff_map(),
      num_changes, num_filtered);
    do_count_diff_map_changes(leaf_diffs_.get_array_diff_map(),
      num_changes, num_filtered);
    do_count_diff_map_changes(leaf_diffs_.get_distinct_diff_map(),
      num_changes, num_filtered);
    do_count_diff_map_changes(leaf_diffs_.get_fn_parm_diff_map(),
  			    num_changes, num_filtered);
  }
  
  /// Count the number of types not reachable from the interface (i.e,
  /// not reachable from global functions or variables).
  ///
  /// @param num_added this is set to the number of added types not
  /// reachable from the interface.
  ///
  /// @param num_deleted this is set to the number of deleted types not
  /// reachable from the interface.
  ///
  /// @param num_changed this is set to the number of changed types not
  /// reachable from the interface.
  ///
  /// @param num_filtered_added this is set to the number of added types
  /// not reachable from the interface and that have been filtered out
  /// by suppression specifications.
  ///
  /// @param num_filtered_deleted this is set to the number of deleted
  /// types not reachable from the interface and that have been filtered
  /// out by suppression specifications.
  ///
  /// @param num_filtered_changed this is set to the number of changed
  /// types not reachable from the interface and that have been filtered
  /// out by suppression specifications.
  void
  corpus_diff::priv::count_unreachable_types(size_t &num_added,
  					   size_t &num_deleted,
  					   size_t &num_changed,
  					   size_t &num_filtered_added,
  					   size_t &num_filtered_deleted,
  					   size_t &num_filtered_changed)
  {
    num_added = added_unreachable_types_.size();
    num_deleted = deleted_unreachable_types_.size();
    num_changed = changed_unreachable_types_.size();
    num_filtered_added = suppressed_added_unreachable_types_.size();
    num_filtered_deleted = suppressed_deleted_unreachable_types_.size();
  
    for (vector<diff_sptr>::const_iterator i =
  	 changed_unreachable_types_sorted().begin();
         i != changed_unreachable_types_sorted().end();
         ++i)
      if (!(*i)->to_be_reported())
        ++num_filtered_changed;
  }
  
  /// Get the sorted vector of diff nodes representing changed
  /// unreachable types.
  ///
  /// Upon the first invocation of this method, if the vector is empty,
  /// this function gets the diff nodes representing changed
  /// unreachable, sort them, and return the sorted vector.
  ///
  /// @return the sorted vector of diff nodes representing changed
  /// unreachable types.
  const vector<diff_sptr>&
  corpus_diff::priv::changed_unreachable_types_sorted() const
  {
  if (changed_unreachable_types_sorted_.empty())
    if (!changed_unreachable_types_.empty())
      sort_string_diff_sptr_map(changed_unreachable_types_,
  			      changed_unreachable_types_sorted_);
  
   return changed_unreachable_types_sorted_;
  }
  
  /// Compute the diff stats.
  ///
  /// To know the number of functions that got filtered out, this
  /// function applies the categorizing filters to the diff sub-trees of
  /// each function changes diff, prior to calculating the stats.
  ///
  /// @param num_removed the number of removed functions.
  ///
  /// @param num_added the number of added functions.
  ///
  /// @param num_changed the number of changed functions.
  ///
  /// @param num_filtered_out the number of changed functions that are
  /// got filtered out from the report
  void
  corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat)
  {
    stat.num_func_removed(deleted_fns_.size());
    stat.num_removed_func_filtered_out(suppressed_deleted_fns_.size());
    stat.num_func_added(added_fns_.size());
    stat.num_added_func_filtered_out(suppressed_added_fns_.size());
    stat.num_func_changed(changed_fns_map_.size());
  
    stat.num_vars_removed(deleted_vars_.size());
    stat.num_removed_vars_filtered_out(suppressed_deleted_vars_.size());
    stat.num_vars_added(added_vars_.size());
    stat.num_added_vars_filtered_out(suppressed_added_vars_.size());
    stat.num_vars_changed(changed_vars_map_.size());
  
    diff_context_sptr ctxt = get_context();
  
    // Walk the changed function diff nodes to apply the categorization
    // filters.
    diff_sptr diff;
    for (function_decl_diff_sptrs_type::const_iterator i =
  	 changed_fns_.begin();
         i != changed_fns_.end();
         ++i)
      {
        diff_sptr diff = *i;
        ctxt->maybe_apply_filters(diff);
      }
  
    // Walk the changed variable diff nodes to apply the categorization
    // filters.
    for (var_diff_sptrs_type::const_iterator i = sorted_changed_vars_.begin();
         i != sorted_changed_vars_.end();
         ++i)
      {
        diff_sptr diff = *i;
        ctxt->maybe_apply_filters(diff);
      }
  
    // walk the changed unreachable types to apply categorization
    // filters
    for (diff_sptrs_type::const_iterator i =
  	  changed_unreachable_types_sorted().begin();
  	i != changed_unreachable_types_sorted().end();
         ++i)
      {
        diff_sptr diff = *i;
        ctxt->maybe_apply_filters(diff);
      }
  
    categorize_redundant_changed_sub_nodes();
  
    // Walk the changed function diff nodes to count the number of
    // filtered-out functions and the number of functions with virtual
    // offset changes.
    for (function_decl_diff_sptrs_type::const_iterator i =
  	 changed_fns_.begin();
         i != changed_fns_.end();
         ++i)
      {
        if ((*i)->is_filtered_out())
  	{
  	  stat.num_changed_func_filtered_out
  	    (stat.num_changed_func_filtered_out() + 1);
  
  	  if ((*i)->has_local_changes())
  	    stat.num_leaf_func_changes_filtered_out
  	      (stat.num_leaf_func_changes_filtered_out() + 1);
  	}
        else
  	{
  	  if ((*i)->get_category() & VIRTUAL_MEMBER_CHANGE_CATEGORY)
  	    stat.num_func_with_virtual_offset_changes
  	      (stat.num_func_with_virtual_offset_changes() + 1);
  	}
  
        if ((*i)->has_local_changes())
  	stat.num_leaf_func_changes
  	  (stat.num_leaf_func_changes() + 1);
      }
  
    // Walk the changed variables diff nodes to count the number of
    // filtered-out variables.
    for (var_diff_sptrs_type ::const_iterator i = sorted_changed_vars_.begin();
         i != sorted_changed_vars_.end();
         ++i)
      {
        if ((*i)->is_filtered_out())
  	{
  	  stat.num_changed_vars_filtered_out
  	    (stat.num_changed_vars_filtered_out() + 1);
  
  	  if ((*i)->has_local_changes())
  	    stat.num_leaf_var_changes_filtered_out
  	      (stat.num_leaf_var_changes_filtered_out() + 1);
  	}
        if ((*i)->has_local_changes())
  	stat.num_leaf_var_changes
  	  (stat.num_leaf_var_changes() + 1);
      }
  
    stat.num_func_syms_added(added_unrefed_fn_syms_.size());
    stat.num_added_func_syms_filtered_out(suppressed_added_unrefed_fn_syms_.size());
    stat.num_func_syms_removed(deleted_unrefed_fn_syms_.size());
    stat.num_removed_func_syms_filtered_out(suppressed_deleted_unrefed_fn_syms_.size());
    stat.num_var_syms_added(added_unrefed_var_syms_.size());
    stat.num_added_var_syms_filtered_out(suppressed_added_unrefed_var_syms_.size());
    stat.num_var_syms_removed(deleted_unrefed_var_syms_.size());
    stat.num_removed_var_syms_filtered_out(suppressed_deleted_unrefed_var_syms_.size());
  
    // Walk the general leaf type diff nodes to count them
    {
      size_t num_type_changes = 0, num_type_filtered = 0;
      count_leaf_type_changes(num_type_changes, num_type_filtered);
  
      stat.num_leaf_type_changes(num_type_changes);
      stat.num_leaf_type_changes_filtered_out(num_type_filtered);
    }
  
    // Walk the general leaf artefacts diff nodes to count them
    {
      size_t num_changes = 0, num_filtered = 0;
      count_leaf_changes(num_changes, num_filtered);
  
      stat.num_leaf_changes(num_changes);
      stat.num_leaf_changes_filtered_out(num_filtered);
    }
  
    // Walk the unreachable types to count them
    {
      size_t num_added_unreachable_types = 0,
        num_changed_unreachable_types = 0,
        num_deleted_unreachable_types = 0,
        num_added_unreachable_types_filtered = 0,
        num_changed_unreachable_types_filtered = 0,
        num_deleted_unreachable_types_filtered = 0;
  
      count_unreachable_types(num_added_unreachable_types,
  			    num_deleted_unreachable_types,
  			    num_changed_unreachable_types,
  			    num_added_unreachable_types_filtered,
  			    num_deleted_unreachable_types_filtered,
  			    num_changed_unreachable_types_filtered);
  
      stat.num_added_unreachable_types(num_added_unreachable_types);
      stat.num_removed_unreachable_types(num_deleted_unreachable_types);
      stat.num_changed_unreachable_types(num_changed_unreachable_types);
      stat.num_added_unreachable_types_filtered_out
        (num_added_unreachable_types_filtered);
      stat.num_removed_unreachable_types_filtered_out
        (num_deleted_unreachable_types_filtered);
      stat.num_changed_unreachable_types_filtered_out
        (num_changed_unreachable_types_filtered);
    }
  }
  
  /// Emit the summary of the functions & variables that got
  /// removed/changed/added.
  ///
  /// TODO: This should be handled by the reporters, just like what is
  /// done for reporter_base::diff_to_be_reported.
  ///
  /// @param out the output stream to emit the stats to.
  ///
  /// @param indent the indentation string to use in the summary.
  void
  corpus_diff::priv::emit_diff_stats(const diff_stats&	s,
  				   ostream&		out,
  				   const string&	indent)
  {
    /// Report added/removed/changed functions.
    size_t net_num_leaf_changes =
      s.net_num_func_removed() +
      s.net_num_func_added() +
      s.net_num_leaf_func_changes() +
      s.net_num_vars_removed() +
      s.net_num_vars_added() +
      s.net_num_leaf_var_changes() +
      s.net_num_leaf_type_changes();
  
    if (!sonames_equal_)
      out << indent << "ELF SONAME changed\n";
  
    if (!architectures_equal_)
      out << indent << "ELF architecture changed\n";
  
    diff_context_sptr ctxt = get_context();
  
    if (ctxt->show_leaf_changes_only())
      {
        out << "Leaf changes summary: ";
        out << net_num_leaf_changes << " artifact";
        if (net_num_leaf_changes > 1)
  	out << "s";
        out << " changed";
  
        if (size_t num_filtered = s.num_leaf_changes_filtered_out())
  	out << " (" << num_filtered << " filtered out)";
        out << "\n";
  
        out << indent << "Changed leaf types summary: "
  	  << s.net_num_leaf_type_changes();
        if (s.num_leaf_type_changes_filtered_out())
  	out << " (" << s.num_leaf_type_changes_filtered_out()
  	    << " filtered out)";
        out << " leaf type";
        if (s.num_leaf_type_changes() > 1)
  	out << "s";
        out << " changed\n";
  
        // function changes summary
        out << indent << "Removed/Changed/Added functions summary: ";
        out << s.net_num_func_removed() << " Removed";
       if (s.num_removed_func_filtered_out())
  	out << " ("
  	    << s.num_removed_func_filtered_out()
  	    << " filtered out)";
        out << ", ";
  
        out << s.net_num_leaf_func_changes() << " Changed";
        if (s.num_leaf_func_changes_filtered_out())
  	    out << " ("
  		<< s.num_leaf_func_changes_filtered_out()
  		<< " filtered out)";
        out << ", ";
  
        out << s.net_num_func_added()<< " Added ";
        if (s.net_num_func_added() <= 1)
  	out << "function";
        else
  	out << "functions";
        if (s.num_added_func_filtered_out())
  	out << " (" << s.num_added_func_filtered_out() << " filtered out)";
        out << "\n";
  
        // variables changes summary
        out << indent << "Removed/Changed/Added variables summary: ";
        out << s.net_num_vars_removed() << " Removed";
        if (s.num_removed_vars_filtered_out())
  	out << " (" << s.num_removed_vars_filtered_out()
  	    << " filtered out)";
        out << ", ";
  
        out << s.net_num_leaf_var_changes() << " Changed";
        if (s.num_leaf_var_changes_filtered_out())
  	out << " ("
  	    << s.num_leaf_var_changes_filtered_out()
  	    << " filtered out)";
        out << ", ";
  
        out << s.net_num_vars_added() << " Added ";
        if (s.net_num_vars_added() <= 1)
  	out << "variable";
        else
  	out << "variables";
        if (s.num_added_vars_filtered_out())
  	out << " (" << s.num_added_vars_filtered_out()
  	    << " filtered out)";
        out << "\n";
      }
    else // if (ctxt->show_leaf_changes_only())
      {
        size_t total_nb_function_changes = s.num_func_removed()
  	+ s.num_func_changed() +  s.num_func_added();
  
        // function changes summary
        out << indent << "Functions changes summary: ";
        out << s.net_num_func_removed() << " Removed";
        if (s.num_removed_func_filtered_out())
  	out << " ("
  	    << s.num_removed_func_filtered_out()
  	    << " filtered out)";
        out << ", ";
  
        out << s.net_num_func_changed() << " Changed";
        if (s.num_changed_func_filtered_out())
  	out << " (" << s.num_changed_func_filtered_out() << " filtered out)";
        out << ", ";
  
        out << s.net_num_func_added() << " Added";
        if (s.num_added_func_filtered_out())
  	out << " (" << s.num_added_func_filtered_out() << " filtered out)";
        if (total_nb_function_changes <= 1)
  	out << " function";
        else
  	out << " functions";
        out << "\n";
  
        // variables changes summary
        size_t total_nb_variable_changes = s.num_vars_removed()
  	+ s.num_vars_changed() + s.num_vars_added();
  
        out << indent << "Variables changes summary: ";
        out << s.net_num_vars_removed() << " Removed";
        if (s.num_removed_vars_filtered_out())
  	out << " (" << s.num_removed_vars_filtered_out()
  	    << " filtered out)";
        out << ", ";
  
        out << s.num_vars_changed() - s.num_changed_vars_filtered_out() << " Changed";
        if (s.num_changed_vars_filtered_out())
  	out << " (" << s.num_changed_vars_filtered_out() << " filtered out)";
        out << ", ";
  
        out << s.net_num_vars_added() << " Added";
        if (s.num_added_vars_filtered_out())
  	out << " (" << s.num_added_vars_filtered_out()
  	    << " filtered out)";
        if (total_nb_variable_changes <= 1)
  	out << " variable";
        else
  	out << " variables";
        out << "\n";
      }
  
    // Show statistics about types not reachable from global
    // functions/variables.
    if (ctxt->show_unreachable_types())
      {
        size_t total_nb_unreachable_type_changes =
  	s.num_removed_unreachable_types()
  	+ s.num_changed_unreachable_types()
  	+ s.num_added_unreachable_types();
  
        // Show summary of unreachable types
        out << indent << "Unreachable types summary: "
  	  << s.net_num_removed_unreachable_types()
  	  << " removed";
        if (s.num_removed_unreachable_types_filtered_out())
  	out << " (" << s.num_removed_unreachable_types_filtered_out()
  	    << " filtered out)";
        out << ", ";
  
        out << s.net_num_changed_unreachable_types()
  	  << " changed";
        if (s.num_changed_unreachable_types_filtered_out())
  	out << " (" << s.num_changed_unreachable_types_filtered_out()
  	    << " filtered out)";
        out << ", ";
  
        out << s.net_num_added_unreachable_types()
  	  << " added";
        if (s.num_added_unreachable_types_filtered_out())
  	out << " (" << s.num_added_unreachable_types_filtered_out()
  	    << " filtered out)";
        if (total_nb_unreachable_type_changes <= 1)
  	out << " type";
        else
  	out << " types";
        out << "\n";
      }
  
    if (ctxt->show_symbols_unreferenced_by_debug_info()
        && (s.num_func_syms_removed()
  	  || s.num_func_syms_added()
  	  || s.num_var_syms_removed()
  	  || s.num_var_syms_added()))
      {
        // function symbols changes summary.
  
        if (!ctxt->show_added_symbols_unreferenced_by_debug_info()
  	  && s.num_func_syms_removed() == 0
  	  && s.num_func_syms_added() != 0)
  	// If the only unreferenced function symbol change is function
  	// syms that got added, but we were forbidden to show function
  	// syms being added, do nothing.
  	;
        else
  	{
  	  out << indent
  	      << "Function symbols changes summary: "
  	      << s.net_num_removed_func_syms() << " Removed";
  	  if (s.num_removed_func_syms_filtered_out())
  	    out << " (" << s.num_removed_func_syms_filtered_out()
  		<< " filtered out)";
  	  out << ", ";
  	  out << s.net_num_added_func_syms() << " Added";
  	  if (s.num_added_func_syms_filtered_out())
  	    out << " (" << s.num_added_func_syms_filtered_out()
  		<< " filtered out)";
  	  out << " function symbol";
  	  if (s.num_func_syms_added() + s.num_func_syms_removed() > 1)
  	    out << "s";
  	  out << " not referenced by debug info\n";
  	}
  
        // variable symbol changes summary.
  
        if (!ctxt->show_added_symbols_unreferenced_by_debug_info()
  	  && s.num_var_syms_removed() == 0
  	  && s.num_var_syms_added() != 0)
  	// If the only unreferenced variable symbol change is variable
  	// syms that got added, but we were forbidden to show variable
  	// syms being added, do nothing.
  	;
        else
  	{
  	  out << indent
  	      << "Variable symbols changes summary: "
  	      << s.net_num_removed_var_syms() << " Removed";
  	  if (s.num_removed_var_syms_filtered_out())
  	    out << " (" << s.num_removed_var_syms_filtered_out()
  		<< " filtered out)";
  	  out << ", ";
  	  out << s.net_num_added_var_syms() << " Added";
  	  if (s.num_added_var_syms_filtered_out())
  	    out << " (" << s.num_added_var_syms_filtered_out()
  		<< " filtered out)";
  	  out << " variable symbol";
  	  if (s.num_var_syms_added() + s.num_var_syms_removed() > 1)
  	    out << "s";
  	  out << " not referenced by debug info\n";
  	}
      }
  }
  
  /// Walk the changed functions and variables diff nodes to categorize
  /// redundant nodes.
  void
  corpus_diff::priv::categorize_redundant_changed_sub_nodes()
  {
    diff_sptr diff;
  
    diff_context_sptr ctxt = get_context();
  
    ctxt->forget_visited_diffs();
    for (function_decl_diff_sptrs_type::const_iterator i =
  	 changed_fns_.begin();
         i!= changed_fns_.end();
         ++i)
      {
        diff = *i;
        categorize_redundancy(diff);
      }
  
    for (var_diff_sptrs_type::const_iterator i = sorted_changed_vars_.begin();
         i!= sorted_changed_vars_.end();
         ++i)
      {
        diff_sptr diff = *i;
        categorize_redundancy(diff);
      }
  
    for (diff_sptrs_type::const_iterator i =
  	 changed_unreachable_types_sorted().begin();
         i!= changed_unreachable_types_sorted().end();
         ++i)
      {
        diff_sptr diff = *i;
        categorize_redundancy(diff);
      }
  }
  
  /// Walk the changed functions and variables diff nodes and clear the
  /// redundancy categorization they might carry.
  void
  corpus_diff::priv::clear_redundancy_categorization()
  {
    diff_sptr diff;
    for (function_decl_diff_sptrs_type::const_iterator i = changed_fns_.begin();
         i!= changed_fns_.end();
         ++i)
      {
        diff = *i;
        abigail::comparison::clear_redundancy_categorization(diff);
      }
  
    for (var_diff_sptrs_type::const_iterator i = sorted_changed_vars_.begin();
         i!= sorted_changed_vars_.end();
         ++i)
      {
        diff = *i;
        abigail::comparison::clear_redundancy_categorization(diff);
      }
  }
  
  /// If the user asked to dump the diff tree node (for changed
  /// variables and functions) on the error output stream, then just do
  /// that.
  ///
  /// This function is used for debugging purposes.
  void
  corpus_diff::priv::maybe_dump_diff_tree()
  {
    diff_context_sptr ctxt = get_context();
  
    if (!ctxt->dump_diff_tree()
        || ctxt->error_output_stream() == 0)
      return;
  
    if (!changed_fns_.empty())
      {
        *ctxt->error_output_stream() << "changed functions diff tree: \n\n";
        for (function_decl_diff_sptrs_type::const_iterator i =
  	     changed_fns_.begin();
  	   i != changed_fns_.end();
  	   ++i)
  	{
  	  diff_sptr d = *i;
  	  print_diff_tree(d, *ctxt->error_output_stream());
  	}
      }
  
    if (!sorted_changed_vars_.empty())
      {
        *ctxt->error_output_stream() << "\nchanged variables diff tree: \n\n";
        for (var_diff_sptrs_type::const_iterator i =
  	     sorted_changed_vars_.begin();
  	   i != sorted_changed_vars_.end();
  	   ++i)
  	{
  	  diff_sptr d = *i;
  	  print_diff_tree(d, *ctxt->error_output_stream());
  	}
      }
  
    if (!changed_unreachable_types_sorted().empty())
      {
        *ctxt->error_output_stream() << "\nchanged unreachable "
  	"types diff tree: \n\n";
        for (vector<diff_sptr>::const_iterator i =
  	     changed_unreachable_types_sorted().begin();
  	   i != changed_unreachable_types_sorted().end();
  	   ++i)
  	{
  	  diff_sptr d = *i;
  	  print_diff_tree(d, *ctxt->error_output_stream());
  	}
      }
  }
  
  /// Populate the vector of children node of the @ref corpus_diff type.
  ///
  /// The children node can then later be retrieved using
  /// corpus_diff::children_node().
  void
  corpus_diff::chain_into_hierarchy()
  {
    for (function_decl_diff_sptrs_type::const_iterator i =
  	 changed_functions_sorted().begin();
         i != changed_functions_sorted().end();
         ++i)
      if (diff_sptr d = *i)
        append_child_node(d);
  }
  
  /// Constructor for @ref corpus_diff.
  ///
  /// @param first the first corpus of the diff.
  ///
  /// @param second the second corpus of the diff.
  ///
  /// @param ctxt the diff context to use.  Note that this context
  /// object must stay alive at least during the life time of the
  /// current instance of @ref corpus_diff.  Otherwise memory corruption
  /// issues occur.
  corpus_diff::corpus_diff(corpus_sptr first,
  			 corpus_sptr second,
  			 diff_context_sptr ctxt)
    : priv_(new priv(first, second, ctxt))
  {}
  
  /// Finish building the current instance of @ref corpus_diff.
  void
  corpus_diff::finish_diff_type()
  {
    if (priv_->finished_)
      return;
    chain_into_hierarchy();
    priv_->finished_ = true;
  }
  
  /// @return the first corpus of the diff.
  corpus_sptr
  corpus_diff::first_corpus() const
  {return priv_->first_;}
  
  /// @return the second corpus of the diff.
  corpus_sptr
  corpus_diff::second_corpus() const
  {return priv_->second_;}
  
  /// @return the children nodes of the current instance of corpus_diff.
  const vector<diff*>&
  corpus_diff::children_nodes() const
  {return priv_->children_;}
  
  /// Append a new child node to the vector of children nodes for the
  /// current instance of @ref corpus_diff node.
  ///
  /// Note that the vector of children nodes for the current instance of
  /// @ref corpus_diff node must remain sorted, using
  /// diff_less_than_functor.
  ///
  /// @param d the new child node.  Note that the life time of the
  /// object held by @p d will thus equal the life time of the current
  /// instance of @ref corpus_diff.
  void
  corpus_diff::append_child_node(diff_sptr d)
  {
    ABG_ASSERT(d);
  
    diff_less_than_functor is_less_than;
    bool inserted = false;
    for (vector<diff*>::iterator i = priv_->children_.begin();
         i != priv_->children_.end();
         ++i)
      // Look for the point where to insert the diff child node.
      if (!is_less_than(d.get(), *i))
        {
  	context()->keep_diff_alive(d);
  	priv_->children_.insert(i, d.get());
  	// As we have just inserted 'd' into the vector, the iterator
  	// 'i' is invalidated.  We must *NOT* use it anymore.
  	inserted = true;
  	break;
        }
  
    if (!inserted)
      {
        context()->keep_diff_alive(d);
        // We didn't insert anything to the vector, presumably b/c it was
        // empty or had one element that was "less than" 'd'.  We can thus
        // just append 'd' to the end of the vector.
        priv_->children_.push_back(d.get());
      }
  }
  
  /// @return the bare edit script of the functions changed as recorded
  /// by the diff.
  edit_script&
  corpus_diff::function_changes() const
  {return priv_->fns_edit_script_;}
  
  /// @return the bare edit script of the variables changed as recorded
  /// by the diff.
  edit_script&
  corpus_diff::variable_changes() const
  {return priv_->vars_edit_script_;}
  
  /// Test if the soname of the underlying corpus has changed.
  ///
  /// @return true iff the soname has changed.
  bool
  corpus_diff::soname_changed() const
  {return !priv_->sonames_equal_;}
  
  /// Test if the architecture of the underlying corpus has changed.
  ///
  /// @return true iff the architecture has changed.
  bool
  corpus_diff::architecture_changed() const
  {return !priv_->architectures_equal_;}
  
  /// Getter for the deleted functions of the diff.
  ///
  /// @return the the deleted functions of the diff.
  const string_function_ptr_map&
  corpus_diff::deleted_functions() const
  {return priv_->deleted_fns_;}
  
  /// Getter for the added functions of the diff.
  ///
  /// @return the added functions of the diff.
  const string_function_ptr_map&
  corpus_diff::added_functions()
  {return priv_->added_fns_;}
  
  /// Getter for the functions which signature didn't change, but which
  /// do have some indirect changes in their parms.
  ///
  /// @return a non-sorted map of functions which signature didn't
  /// change, but which do have some indirect changes in their parms.
  /// The key of the map is a unique identifier for the function; it's
  /// usually made of the name and version of the underlying ELF symbol
  /// of the function for corpora that were built from ELF files.
  const string_function_decl_diff_sptr_map&
  corpus_diff::changed_functions()
  {return priv_->changed_fns_map_;}
  
  /// Getter for a sorted vector of functions which signature didn't
  /// change, but which do have some indirect changes in their parms.
  ///
  /// @return a sorted vector of functions which signature didn't
  /// change, but which do have some indirect changes in their parms.
  const function_decl_diff_sptrs_type&
  corpus_diff::changed_functions_sorted()
  {return priv_->changed_fns_;}
  
  /// Getter for the variables that got deleted from the first subject
  /// of the diff.
  ///
  /// @return the map of deleted variable.
  const string_var_ptr_map&
  corpus_diff::deleted_variables() const
  {return priv_->deleted_vars_;}
  
  /// Getter for the added variables of the diff.
  ///
  /// @return the map of added variable.
  const string_var_ptr_map&
  corpus_diff::added_variables() const
  {return priv_->added_vars_;}
  
  /// Getter for the non-sorted map of variables which signature didn't
  /// change but which do have some indirect changes in some sub-types.
  ///
  /// @return the non-sorted map of changed variables.
  const string_var_diff_sptr_map&
  corpus_diff::changed_variables()
  {return priv_->changed_vars_map_;}
  
  /// Getter for the sorted vector of variables which signature didn't
  /// change but which do have some indirect changes in some sub-types.
  ///
  /// @return a sorted vector of changed variables.
  const var_diff_sptrs_type&
  corpus_diff::changed_variables_sorted()
  {return priv_->sorted_changed_vars_;}
  
  /// Getter for function symbols not referenced by any debug info and
  /// that got deleted.
  ///
  /// @return a map of elf function symbols not referenced by any debug
  /// info and that got deleted.
  const string_elf_symbol_map&
  corpus_diff::deleted_unrefed_function_symbols() const
  {return priv_->deleted_unrefed_fn_syms_;}
  
  /// Getter for function symbols not referenced by any debug info and
  /// that got added.
  ///
  /// @return a map of elf function symbols not referenced by any debug
  /// info and that got added.
  const string_elf_symbol_map&
  corpus_diff::added_unrefed_function_symbols() const
  {return priv_->added_unrefed_fn_syms_;}
  
  /// Getter for variable symbols not referenced by any debug info and
  /// that got deleted.
  ///
  /// @return a map of elf variable symbols not referenced by any debug
  /// info and that got deleted.
  const string_elf_symbol_map&
  corpus_diff::deleted_unrefed_variable_symbols() const
  {return priv_->deleted_unrefed_var_syms_;}
  
  /// Getter for variable symbols not referenced by any debug info and
  /// that got added.
  ///
  /// @return a map of elf variable symbols not referenced by any debug
  /// info and that got added.
  const string_elf_symbol_map&
  corpus_diff::added_unrefed_variable_symbols() const
  {return priv_->added_unrefed_var_syms_;}
  
  /// Getter for a map of deleted types that are not reachable from
  /// global functions/variables.
  ///
  /// @return a map that associates pretty representation of deleted
  /// unreachable types and said types.
  const string_type_base_sptr_map&
  corpus_diff::deleted_unreachable_types() const
  {return priv_->deleted_unreachable_types_;}
  
  /// Getter of a sorted vector of deleted types that are not reachable
  /// from global functions/variables.
  ///
  /// @return a sorted vector of deleted types that are not reachable
  /// from global functions/variables.  The types are lexicographically
  /// sorted by considering their pretty representation.
  const vector<type_base_sptr>&
  corpus_diff::deleted_unreachable_types_sorted() const
  {
    if (priv_->deleted_unreachable_types_sorted_.empty())
      if (!priv_->deleted_unreachable_types_.empty())
        sort_string_type_base_sptr_map(priv_->deleted_unreachable_types_,
  				     priv_->deleted_unreachable_types_sorted_);
  
    return priv_->deleted_unreachable_types_sorted_;
  }
  
  /// Getter for a map of added types that are not reachable from global
  /// functions/variables.
  ///
  /// @return a map that associates pretty representation of added
  /// unreachable types and said types.
  const string_type_base_sptr_map&
  corpus_diff::added_unreachable_types() const
  {return priv_->added_unreachable_types_;}
  
  /// Getter of a sorted vector of added types that are not reachable
  /// from global functions/variables.
  ///
  /// @return a sorted vector of added types that are not reachable from
  /// global functions/variables.  The types are lexicographically
  /// sorted by considering their pretty representation.
  const vector<type_base_sptr>&
  corpus_diff::added_unreachable_types_sorted() const
  {
    if (priv_->added_unreachable_types_sorted_.empty())
      if (!priv_->added_unreachable_types_.empty())
        sort_string_type_base_sptr_map(priv_->added_unreachable_types_,
  				     priv_->added_unreachable_types_sorted_);
  
    return priv_->added_unreachable_types_sorted_;
  }
  
  /// Getter for a map of changed types that are not reachable from
  /// global functions/variables.
  ///
  /// @return a map that associates pretty representation of changed
  /// unreachable types and said types.
  const string_diff_sptr_map&
  corpus_diff::changed_unreachable_types() const
  {return priv_->changed_unreachable_types_;}
  
  /// Getter of a sorted vector of changed types that are not reachable
  /// from global functions/variables.
  ///
  /// @return a sorted vector of changed types that are not reachable
  /// from global functions/variables.  The diffs are lexicographically
  /// sorted by considering their pretty representation.
  const vector<diff_sptr>&
  corpus_diff::changed_unreachable_types_sorted() const
  {return priv_->changed_unreachable_types_sorted();}
  
  /// Getter of the diff context of this diff
  ///
  /// @return the diff context for this diff.
  const diff_context_sptr
  corpus_diff::context() const
  {return priv_->get_context();}
  
  /// @return the pretty representation for the current instance of @ref
  /// corpus_diff
  const string&
  corpus_diff::get_pretty_representation() const
  {
    if (priv_->pretty_representation_.empty())
      {
        std::ostringstream o;
        o << "corpus_diff["
  	<< first_corpus()->get_path()
  	<< ", "
  	<< second_corpus()->get_path()
  	<< "]";
        priv_->pretty_representation_ = o.str();
      }
    return priv_->pretty_representation_;
  }
  /// Return true iff the current @ref corpus_diff node carries a
  /// change.
  ///
  /// @return true iff the current diff node carries a change.
  bool
  corpus_diff::has_changes() const
  {
    return (soname_changed()
  	  || architecture_changed()
  	  || !(priv_->deleted_fns_.empty()
  	       && priv_->added_fns_.empty()
  	       && priv_->changed_fns_map_.empty()
  	       && priv_->deleted_vars_.empty()
  	       && priv_->added_vars_.empty()
  	       && priv_->changed_vars_map_.empty()
  	       && priv_->added_unrefed_fn_syms_.empty()
  	       && priv_->deleted_unrefed_fn_syms_.empty()
  	       && priv_->added_unrefed_var_syms_.empty()
  	       && priv_->deleted_unrefed_var_syms_.empty()
  	       && priv_->deleted_unreachable_types_.empty()
  	       && priv_->added_unreachable_types_.empty()
  	       && priv_->changed_unreachable_types_.empty()));
  }
  
  /// Test if the current instance of @ref corpus_diff carries changes
  /// that we are sure are incompatible.  By incompatible change we mean
  /// a change that "breaks" the ABI of the corpus we are looking at.
  ///
  /// In concrete terms, this function considers the following changes
  /// as being ABI incompatible for sure:
  ///
  ///   - a soname change
  ///   - if exported functions or variables got removed
  ///
  /// Note that subtype changes *can* represent changes that break ABI
  /// too.  But they also can be changes that are OK, ABI-wise.
  ///
  /// It's up to the user to provide suppression specifications to say
  /// explicitely which subtype change is OK.  The remaining sub-type
  /// changes are then considered to be ABI incompatible.  But to test
  /// if such ABI incompatible subtype changes are present you need to
  /// use the function @ref corpus_diff::has_net_subtype_changes()
  ///
  /// @return true iff the current instance of @ref corpus_diff carries
  /// changes that we are sure are ABI incompatible.
  bool
  corpus_diff::has_incompatible_changes() const
  {
    const diff_stats& stats = const_cast<corpus_diff*>(this)->
      apply_filters_and_suppressions_before_reporting();
  
    return (soname_changed() || architecture_changed()
  	  || stats.net_num_func_removed() != 0
  	  || (stats.num_func_with_virtual_offset_changes() != 0
  	      // If all reports about functions with sub-type changes
  	      // have been suppressed, then even those about functions
  	      // that are virtual don't matter anymore because the
  	      // user willingly requested to shut them down
  	      && stats.net_num_func_changed() != 0)
  	  || stats.net_num_vars_removed() != 0
  	  || stats.net_num_removed_func_syms() != 0
  	  || stats.net_num_removed_var_syms() != 0
  	  || stats.net_num_removed_unreachable_types() != 0
  	  || stats.net_num_changed_unreachable_types() != 0);
  }
  
  /// Test if the current instance of @ref corpus_diff carries subtype
  /// changes whose reports are not suppressed by any suppression
  /// specification.  In effect, these are deemed incompatible ABI
  /// changes.
  ///
  /// @return true iff the the current instance of @ref corpus_diff
  /// carries subtype changes that are deemed incompatible ABI changes.
  bool
  corpus_diff::has_net_subtype_changes() const
  {
    const diff_stats& stats = const_cast<corpus_diff*>(this)->
        apply_filters_and_suppressions_before_reporting();
  
    return (stats.net_num_func_changed() != 0
  	  || stats.net_num_vars_changed() != 0
  	  || stats.net_num_removed_unreachable_types() != 0
  	  || stats.net_num_changed_unreachable_types() != 0);
  }
  
  /// Test if the current instance of @ref corpus_diff carries changes
  /// whose reports are not suppressed by any suppression specification.
  /// In effect, these are deemed incompatible ABI changes.
  ///
  /// @return true iff the the current instance of @ref corpus_diff
  /// carries subtype changes that are deemed incompatible ABI changes.
  bool
  corpus_diff::has_net_changes() const
  {return  context()->get_reporter()->diff_has_net_changes(this);}
  
  /// Apply the different filters that are registered to be applied to
  /// the diff tree; that includes the categorization filters.  Also,
  /// apply the suppression interpretation filters.
  ///
  /// After the filters are applied, this function calculates some
  /// statistics about the changes carried by the current instance of
  /// @ref corpus_diff.  These statistics are represented by an instance
  /// of @ref corpus_diff::diff_stats.
  ///
  /// This member function is called by the reporting function
  /// corpus_diff::report().
  ///
  /// Note that for a given instance of corpus_diff, this function
  /// applies the filters and suppressions only the first time it is
  /// invoked.  Subsequent invocations just return the instance of
  /// corpus_diff::diff_stats that was cached after the first
  /// invocation.
  ///
  /// @return a reference to the statistics about the changes carried by
  /// the current instance of @ref corpus_diff.
  const corpus_diff::diff_stats&
  corpus_diff::apply_filters_and_suppressions_before_reporting()
  {
    if (priv_->diff_stats_)
      return *priv_->diff_stats_;
  
    apply_suppressions(this);
    priv_->diff_stats_.reset(new diff_stats(context()));
    mark_leaf_diff_nodes();
    priv_->apply_filters_and_compute_diff_stats(*priv_->diff_stats_);
    return *priv_->diff_stats_;
  }
  
  /// A visitor that marks leaf diff nodes by storing them in the
  /// instance of @ref diff_maps returned by
  /// corpus_diff::get_leaf_diffs() invoked on the current instance of
  /// corpus_diff.
  struct leaf_diff_node_marker_visitor : public diff_node_visitor
  {
    /// This is called when the visitor visits a diff node.
    ///
    /// It basically tests if the diff node being visited is a leaf diff
    /// node - that is, it contains local changes.  If it does, then the
    /// node is added to the set of maps that hold leaf diffs in the
    /// current corpus_diff.
    ///
    /// Note that only leaf nodes that are reachable from public
    /// interfaces (global functions or variables) are collected by this
    /// visitor.
    ///
    /// @param d the diff node being visited.
    virtual void
    visit_begin(diff *d)
    {
      if (d->has_local_changes()
  	// A leaf basic (or class/union) type name change makes no
  	// sense when showing just leaf changes.  It only makes sense
  	// when it can explain the details about a non-leaf change.
  	// In other words, it doesn't make sense to say that an "int"
  	// became "unsigned int".  But it does make sense to say that
  	// a typedef changed because its underlying type was 'int' and
  	// is now an "unsigned int".
  	&& !filtering::has_basic_or_class_type_name_change(d)
  	// Similarly, a *local* change describing a type that changed
  	// its nature doesn't make sense.
  	&& !is_distinct_diff(d)
  	// Similarly, a pointer (or reference or array), a typedef or
  	// qualified type change in itself doesn't make sense.  It
  	// would rather make sense to show that pointer change as part
  	// of the variable change whose pointer type changed, for
  	// instance.
  	&& !is_pointer_diff(d)
  	&& !is_reference_diff(d)
  	&& !is_qualified_type_diff(d)
  	&& !is_typedef_diff(d)
  	&& !is_array_diff(d)
  	// Similarly a parameter change in itself doesn't make sense.
  	// It should have already been reported as part of the change
  	// of the function it belongs to.
  	&& !is_fn_parm_diff(d)
  	// An anonymous class or union diff doesn't make sense on its
  	// own.  It must have been described already by the diff of
  	// the enclosing struct or union if 'd' is from an anonymous
  	// data member, or from a typedef change if 'd' is from a
  	// typedef change which underlying type is an anonymous
  	// struct/union.
  	&& !is_anonymous_class_or_union_diff(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
  	// 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))
        {
  	diff_context_sptr ctxt = d->context();
  	const corpus_diff *corpus_diff_node = ctxt->get_corpus_diff().get();
  	ABG_ASSERT(corpus_diff_node);
  
  	if (diff *iface_diff = get_current_topmost_iface_diff())
  	  {
  	    type_or_decl_base_sptr iface = iface_diff->first_subject();
  	    // So, this diff node that is reachable from a global
  	    // function or variable carries a leaf change.  Let's add
  	    // it to the set of of leaf diffs of corpus_diff_node.
  	    const_cast<corpus_diff*>(corpus_diff_node)->
  	      get_leaf_diffs().insert_diff_node(d, iface);
  	  }
        }
    }
  }; // end struct leaf_diff_node_marker_visitor
  
  /// Walks the diff nodes associated to the current corpus diff and
  /// mark those that carry local changes.  They are said to be leaf
  /// diff nodes.
  ///
  /// The marked nodes are available from the
  /// corpus_diff::get_leaf_diffs() function.
  void
  corpus_diff::mark_leaf_diff_nodes()
  {
    if (!has_changes())
      return;
  
    if (!context()->show_leaf_changes_only())
      return;
  
    leaf_diff_node_marker_visitor v;
    context()->forget_visited_diffs();
    bool s = context()->visiting_a_node_twice_is_forbidden();
    context()->forbid_visiting_a_node_twice(true);
    context()->forbid_visiting_a_node_twice_per_interface(true);
    traverse(v);
    context()->forbid_visiting_a_node_twice(s);
    context()->forbid_visiting_a_node_twice_per_interface(false);
  }
  
  /// Get the set of maps that contain leaf nodes.  A leaf node being a
  /// node with a local change.
  ///
  /// @return the set of maps that contain leaf nodes.  A leaf node
  /// being a node with a local change.
  diff_maps&
  corpus_diff::get_leaf_diffs()
  {return priv_->leaf_diffs_;}
  
  /// Get the set of maps that contain leaf nodes.  A leaf node being a
  /// node with a local change.
  ///
  /// @return the set of maps that contain leaf nodes.  A leaf node
  /// being a node with a local change.
  const diff_maps&
  corpus_diff::get_leaf_diffs() const
  {return priv_->leaf_diffs_;}
  
  /// Report the diff in a serialized form.
  ///
  /// @param out the stream to serialize the diff to.
  ///
  /// @param indent the prefix to use for the indentation of this
  /// serialization.
  void
  corpus_diff::report(ostream& out, const string& indent) const
  {
    context()->get_reporter()->report(*this, out, indent);
  }
  
  /// Traverse the diff sub-tree under the current instance corpus_diff.
  ///
  /// @param v the visitor to invoke on each diff node of the sub-tree.
  ///
  /// @return true if the traversing has to keep going on, false otherwise.
  bool
  corpus_diff::traverse(diff_node_visitor& v)
  {
    finish_diff_type();
  
    v.visit_begin(this);
  
    if (!v.visit(this, true))
      {
        v.visit_end(this);
        return false;
      }
  
    for (function_decl_diff_sptrs_type::const_iterator i =
  	 changed_functions_sorted().begin();
         i != changed_functions_sorted().end();
         ++i)
      {
        if (diff_sptr d = *i)
  	{
  	  const diff_context_sptr &ctxt = context();
  	  if (ctxt->visiting_a_node_twice_is_forbidden_per_interface())
  	    ctxt->forget_visited_diffs();
  
  	  v.set_current_topmost_iface_diff(d.get());
  
  	  if (!d->traverse(v))
  	    {
  	      v.visit_end(this);
  	      v.set_current_topmost_iface_diff(0);
  	      return false;
  	    }
  	}
      }
  
    for (var_diff_sptrs_type::const_iterator i =
  	 changed_variables_sorted().begin();
         i != changed_variables_sorted().end();
         ++i)
      {
        if (diff_sptr d = *i)
  	{
  	  const diff_context_sptr &ctxt = context();
  	  if (ctxt->visiting_a_node_twice_is_forbidden_per_interface())
  	    ctxt->forget_visited_diffs();
  
  	  v.set_current_topmost_iface_diff(d.get());
  
  	  if (!d->traverse(v))
  	    {
  	      v.visit_end(this);
  	      v.set_current_topmost_iface_diff(0);
  	      return false;
  	    }
  	}
      }
  
    v.set_current_topmost_iface_diff(0);
  
    // Traverse the changed unreachable type diffs.  These diffs are on
    // types that are not reachable from global functions or variables.
    for (vector<diff_sptr>::const_iterator i =
  	 changed_unreachable_types_sorted().begin();
         i != changed_unreachable_types_sorted().end();
         ++i)
      {
        if (diff_sptr d = *i)
  	{
  	  const diff_context_sptr &ctxt = context();
  	  if (ctxt->visiting_a_node_twice_is_forbidden_per_interface())
  	    ctxt->forget_visited_diffs();
  
  	  if (!d->traverse(v))
  	    {
  	      v.visit_end(this);
  	      return false;
  	    }
  	}
      }
  
    v.visit_end(this);
    return true;
  }
  
  /// Compute the diff between two instances of @ref corpus.
  ///
  /// Note that the two corpora must have been created in the same @ref
  /// environment, otherwise, this function aborts.
  ///
  /// @param f the first @ref corpus to consider for the diff.
  ///
  /// @param s the second @ref corpus to consider for the diff.
  ///
  /// @param ctxt the diff context to use.
  ///
  /// @return the resulting diff between the two @ref corpus.
  corpus_diff_sptr
  compute_diff(const corpus_sptr	f,
  	     const corpus_sptr	s,
  	     diff_context_sptr	ctxt)
  {
    typedef corpus::functions::const_iterator fns_it_type;
    typedef corpus::variables::const_iterator vars_it_type;
    typedef elf_symbols::const_iterator symbols_it_type;
    typedef diff_utils::deep_ptr_eq_functor eq_type;
    typedef vector<type_base_wptr>::const_iterator type_base_wptr_it_type;
  
    ABG_ASSERT(f && s);
  
    // We can only compare two corpora that were built out of the same
    // environment.
    ABG_ASSERT(f->get_environment() == s->get_environment());
  
    if (!ctxt)
      ctxt.reset(new diff_context);
  
    corpus_diff_sptr r(new corpus_diff(f, s, ctxt));
  
    ctxt->set_corpus_diff(r);
  
    r->priv_->sonames_equal_ = f->get_soname() == s->get_soname();
  
    r->priv_->architectures_equal_ =
      f->get_architecture_name() == s->get_architecture_name();
  
    // Compute the diff of publicly defined and exported functions
    diff_utils::compute_diff<fns_it_type, eq_type>(f->get_functions().begin(),
  						 f->get_functions().end(),
  						 s->get_functions().begin(),
  						 s->get_functions().end(),
  						 r->priv_->fns_edit_script_);
  
    // Compute the diff of publicly defined and exported variables.
    diff_utils::compute_diff<vars_it_type, eq_type>
      (f->get_variables().begin(), f->get_variables().end(),
       s->get_variables().begin(), s->get_variables().end(),
       r->priv_->vars_edit_script_);
  
    // Compute the diff of function elf symbols not referenced by debug
    // info.
    diff_utils::compute_diff<symbols_it_type, eq_type>
      (f->get_unreferenced_function_symbols().begin(),
       f->get_unreferenced_function_symbols().end(),
       s->get_unreferenced_function_symbols().begin(),
       s->get_unreferenced_function_symbols().end(),
       r->priv_->unrefed_fn_syms_edit_script_);
  
    // Compute the diff of variable elf symbols not referenced by debug
    // info.
      diff_utils::compute_diff<symbols_it_type, eq_type>
      (f->get_unreferenced_variable_symbols().begin(),
       f->get_unreferenced_variable_symbols().end(),
       s->get_unreferenced_variable_symbols().begin(),
       s->get_unreferenced_variable_symbols().end(),
       r->priv_->unrefed_var_syms_edit_script_);
  
      if (ctxt->show_unreachable_types())
        // Compute the diff of types not reachable from public functions
        // or global variables that are exported.
        diff_utils::compute_diff<type_base_wptr_it_type, eq_type>
  	(f->get_types_not_reachable_from_public_interfaces().begin(),
  	 f->get_types_not_reachable_from_public_interfaces().end(),
  	 s->get_types_not_reachable_from_public_interfaces().begin(),
  	 s->get_types_not_reachable_from_public_interfaces().end(),
  	 r->priv_->unreachable_types_edit_script_);
  
    r->priv_->ensure_lookup_tables_populated();
  
    return r;
  }
  
  // </corpus stuff>
  
  /// Compute the diff between two instances of @ref corpus_group.
  ///
  /// Note that the two corpus_diff must have been created in the same
  /// @ref environment, otherwise, this function aborts.
  ///
  /// @param f the first @ref corpus_group to consider for the diff.
  ///
  /// @param s the second @ref corpus_group to consider for the diff.
  ///
  /// @param ctxt the diff context to use.
  ///
  /// @return the resulting diff between the two @ref corpus_group.
  corpus_diff_sptr
  compute_diff(const corpus_group_sptr&	f,
  	     const corpus_group_sptr&	s,
  	     diff_context_sptr	ctxt)
  {
  
    corpus_sptr c1 = f;
    corpus_sptr c2 = s;
  
    return compute_diff(c1, c2, ctxt);
  }
  
  // <corpus_group stuff>
  
  // </corpus_group stuff>
  // <diff_node_visitor stuff>
  
  /// The private data of the @diff_node_visitor type.
  struct diff_node_visitor::priv
  {
    diff*	topmost_interface_diff;
    visiting_kind kind;
  
    priv()
      : topmost_interface_diff(),
        kind()
    {}
  
    priv(visiting_kind k)
      : topmost_interface_diff(),
        kind(k)
    {}
  }; // end struct diff_node_visitor
  
  /// Default constructor of the @ref diff_node_visitor type.
  diff_node_visitor::diff_node_visitor()
    : priv_(new priv)
  {}
  
  /// Constructor of the @ref diff_node_visitor type.
  ///
  /// @param k how the visiting has to be performed.
  diff_node_visitor::diff_node_visitor(visiting_kind k)
    : priv_(new priv(k))
  {}
  
  /// Getter for the visiting policy of the traversing code while
  /// invoking this visitor.
  ///
  /// @return the visiting policy used by the traversing code when
  /// invoking this visitor.
  visiting_kind
  diff_node_visitor::get_visiting_kind() const
  {return priv_->kind;}
  
  /// Setter for the visiting policy of the traversing code while
  /// invoking this visitor.
  ///
  /// @param v a bit map representing the new visiting policy used by
  /// the traversing code when invoking this visitor.
  void
  diff_node_visitor::set_visiting_kind(visiting_kind v)
  {priv_->kind = v;}
  
  /// Setter for the visiting policy of the traversing code while
  /// invoking this visitor.  This one makes a logical or between the
  /// current policy and the bitmap given in argument and assigns the
  /// current policy to the result.
  ///
  /// @param v a bitmap representing the visiting policy to or with
  /// the current policy.
  void
  diff_node_visitor::or_visiting_kind(visiting_kind v)
  {priv_->kind = priv_->kind | v;}
  
  /// Setter of the diff current topmost interface which is impacted by
  /// the current diff node being visited.
  ///
  /// @param d the current topmost interface diff impacted.
  void
  diff_node_visitor::set_current_topmost_iface_diff(diff* d)
  {priv_->topmost_interface_diff = d;}
  
  /// Getter of the diff current topmost interface which is impacted by
  /// the current diff node being visited.
  ///
  /// @return the current topmost interface diff impacted.
  diff*
  diff_node_visitor::get_current_topmost_iface_diff() const
  {return priv_->topmost_interface_diff;}
  
  /// This is called by the traversing code on a @ref diff node just
  /// before visiting it.  That is, before visiting it and its children
  /// node.
  ///
  /// @param d the diff node to visit.
  void
  diff_node_visitor::visit_begin(diff* /*p*/)
  {}
  
  /// This is called by the traversing code on a @ref diff node just
  /// after visiting it.  That is after visiting it and its children
  /// nodes.
  ///
  /// @param d the diff node that got visited.
  void
  diff_node_visitor::visit_end(diff* /*p*/)
  {}
  
  /// This is called by the traversing code on a @ref corpus_diff node
  /// just before visiting it.  That is, before visiting it and its
  /// children node.
  ///
  /// @param p the corpus_diff node to visit.
  ///
  void
  diff_node_visitor::visit_begin(corpus_diff* /*p*/)
  {}
  
  /// This is called by the traversing code on a @ref corpus_diff node
  /// just after visiting it.  That is after visiting it and its children
  /// nodes.
  ///
  /// @param d the diff node that got visited.
  void
  diff_node_visitor::visit_end(corpus_diff* /*d*/)
  {}
  
  /// Default visitor implementation
  ///
  /// @return true
  bool
  diff_node_visitor::visit(diff*, bool)
  {return true;}
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(distinct_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(var_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(pointer_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(reference_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(qualified_type_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(enum_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(class_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(base_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(scope_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(function_decl_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(type_decl_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(typedef_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(translation_unit_diff* dif, bool pre)
  {
    diff* d = dif;
    visit(d, pre);
  
    return true;
  }
  
  /// Default visitor implementation.
  ///
  /// @return true
  bool
  diff_node_visitor::visit(corpus_diff*, bool)
  {return true;}
  
  // </diff_node_visitor stuff>
  
  // <redundant diff node marking>
  
  // </redundant diff node marking>
  
  // <diff tree category propagation>
  
  /// A visitor to propagate the category of a node up to its parent
  /// nodes.  This visitor doesn't touch the REDUNDANT_CATEGORY or the
  /// SUPPRESSED_CATEGORY because those are propagated using other
  /// specific visitors.
  struct category_propagation_visitor : public diff_node_visitor
  {
    virtual void
    visit_end(diff* d)
    {
      // Has this diff node 'd' been already visited ?
      bool already_visited = d->context()->diff_has_been_visited(d);
  
      // The canonical diff node of the class of equivalence of the diff
      // node 'd'.
      diff* canonical = d->get_canonical_diff();
  
      // If this class of equivalence of diff node is being visited for
      // the first time, then update its canonical node's category too.
      bool update_canonical = !already_visited && canonical;
  
      for (vector<diff*>::const_iterator i = d->children_nodes().begin();
  	 i != d->children_nodes().end();
  	 ++i)
        {
  	// If we are visiting the class of equivalence of 'd' for the
  	// first time, then let's look at the children of 'd' and
  	// propagate their categories to 'd'.
  	//
  	// If the class of equivalence of 'd' has already been
  	// visited, then let's look at the canonical diff nodes of the
  	// children of 'd' and propagate their categories to 'd'.
  	diff* diff = already_visited
  	  ? (*i)->get_canonical_diff()
  	  : *i;
  
  	ABG_ASSERT(diff);
  
  	diff_category c = diff->get_category();
  	// Do not propagate redundant and suppressed categories. Those
  	// are propagated in a specific pass elsewhere.
  	c &= ~(REDUNDANT_CATEGORY
  	       | SUPPRESSED_CATEGORY
  	       | PRIVATE_TYPE_CATEGORY);
  	// Also, if a (class) type has got a harmful name change, do not
  	// propagate harmless name changes coming from its sub-types
  	// (i.e, data members) to the class itself.
  	if (filtering::has_harmful_name_change(d))
  	  c &= ~HARMLESS_DECL_NAME_CHANGE_CATEGORY;
  
  	d->add_to_category(c);
  	if (!already_visited && canonical)
  	  if (update_canonical)
  	    canonical->add_to_category(c);
        }
    }
  };// end struct category_propagation_visitor
  
  /// Visit all the nodes of a given sub-tree.  For each node that has a
  /// particular category set, propagate that category set up to its
  /// parent nodes.
  ///
  /// @param diff_tree the diff sub-tree to walk for categorization
  /// purpose;
  void
  propagate_categories(diff* diff_tree)
  {
    category_propagation_visitor v;
    bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
    diff_tree->context()->forbid_visiting_a_node_twice(true);
    diff_tree->context()->forget_visited_diffs();
    diff_tree->traverse(v);
    diff_tree->context()->forbid_visiting_a_node_twice(s);
  }
  
  /// Visit all the nodes of a given sub-tree.  For each node that has a
  /// particular category set, propagate that category set up to its
  /// parent nodes.
  ///
  /// @param diff_tree the diff sub-tree to walk for categorization
  /// purpose;
  void
  propagate_categories(diff_sptr diff_tree)
  {propagate_categories(diff_tree.get());}
  
  /// Visit all the nodes of a given corpus tree.  For each node that
  /// has a particular category set, propagate that category set up to
  /// its parent nodes.
  ///
  /// @param diff_tree the corpus_diff tree to walk for categorization
  /// purpose;
  void
  propagate_categories(corpus_diff* diff_tree)
  {
    category_propagation_visitor v;
    bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
    diff_tree->context()->forbid_visiting_a_node_twice(false);
    diff_tree->traverse(v);
    diff_tree->context()->forbid_visiting_a_node_twice(s);
  }
  
  /// Visit all the nodes of a given corpus tree.  For each node that
  /// has a particular category set, propagate that category set up to
  /// its parent nodes.
  ///
  /// @param diff_tree the corpus_diff tree to walk for categorization
  /// purpose;
  void
  propagate_categories(corpus_diff_sptr diff_tree)
  {propagate_categories(diff_tree.get());}
  
  /// A tree node visitor that knows how to categorizes a given diff
  /// node in the SUPPRESSED_CATEGORY category and how to propagate that
  /// categorization.
  struct suppression_categorization_visitor : public diff_node_visitor
  {
  
    /// Before visiting the children of the diff node, check if the node
    /// is suppressed by a suppression specification.  If it is, mark
    /// the node as belonging to the SUPPRESSED_CATEGORY category.
    ///
    /// @param p the diff node to visit.
    virtual void
    visit_begin(diff* d)
    {
      bool is_private_type = false;
      if (d->is_suppressed(is_private_type))
        {
  	diff_category c = is_private_type
  	  ? PRIVATE_TYPE_CATEGORY
  	  : SUPPRESSED_CATEGORY;
  	d->add_to_local_and_inherited_categories(c);
  
  	// If a node was suppressed, all the other nodes of its class
  	// of equivalence are suppressed too.
  	diff *canonical_diff = d->get_canonical_diff();
  	if (canonical_diff != d)
  	  canonical_diff->add_to_category(c);
        }
    }
  
    /// After visiting the children nodes of a given diff node,
    /// propagate the SUPPRESSED_CATEGORY from the children nodes to the
    /// diff node, if need be.
    ///
    /// That is, if all children nodes carry a suppressed change the
    /// current node should be marked as suppressed as well.
    ///
    /// In practice, this might be too strong of a condition.  If the
    /// current node carries a local change (i.e, a change not carried
    /// by any of its children node) and if that change is not
    /// suppressed, then the current node should *NOT* be suppressed.
    ///
    /// But right now, the IR doesn't let us know about local vs
    /// children-carried changes.  So we cannot be that precise yet.
    virtual void
    visit_end(diff* d)
    {
      bool has_non_suppressed_child = false;
      bool has_non_empty_child = false;
      bool has_suppressed_child = false;
      bool has_non_private_child = false;
      bool has_private_child = false;
  
      if (// A node to which we can propagate the "SUPPRESSED_CATEGORY"
  	// (or the PRIVATE_TYPE_CATEGORY for the same matter)
  	// category from its children is a node which:
  	//
  	//  1/ hasn't been suppressed already
  	//
  	//  2/ and has no local change (unless it's a pointer,
  	//  reference or qualified diff node).
  	//
  	//  Note that qualified type and typedef diff nodes are a bit
  	//  special.  The local changes of the underlying type are
  	//  considered local for the qualified/typedef type, just like
  	//  for pointer/reference types.  But then the qualified or
  	//  typedef type itself can have local changes of its own, and
  	//  those changes are of the kind LOCAL_NON_TYPE_CHANGE_KIND.
  	//  So a qualified type which have local changes that are
  	//  *NOT* of LOCAL_NON_TYPE_CHANGE_KIND (or that has no local
  	//  changes at all) and which is in the PRIVATE_TYPE_CATEGORY
  	//  or SUPPRESSED_CATEGORY can see these categories be
  	//  propagated.
  	//
  	// Note that all pointer/reference diff node changes are
  	// potentially considered local, i.e, local changes of the
  	// pointed-to-type are considered local to the pointer itself.
  	//
  	// Similarly, changes local to the type of function parameters,
  	// variables (and data members) and classes (that are not of
  	// LOCAL_NON_TYPE_CHANGE_KIND kind) and that have been
  	// suppressed can propagate their SUPPRESSED_CATEGORY-ness to
  	// those kinds of diff node.
  	!(d->get_category() & SUPPRESSED_CATEGORY)
  	&& (!d->has_local_changes()
  	    || is_pointer_diff(d)
  	    || is_reference_diff(d)
  	    || (is_qualified_type_diff(d)
  		&& (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND)))
  	    || (is_typedef_diff(d)
  		&& (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND)))
  	    || (is_function_decl_diff(d)
  		&& (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND)))
  	    || (is_fn_parm_diff(d)
  		&& (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND)))
  	    || (is_function_type_diff(d)
  		&& (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND)))
  	    || (is_var_diff(d)
  		&& (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND)))
  	    ||  (is_class_diff(d)
  		&& (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND)))))
        {
  	// Note that we handle private diff nodes differently from
  	// generally suppressed diff nodes.  E.g, it's not because a
  	// type is private (and suppressed because of that; i.e, in
  	// the category PRIVATE_TYPE_CATEGORY) that a typedef to that
  	// type should also be private and so suppressed.  Private
  	// diff nodes thus have different propagation rules than
  	// generally suppressed rules.
  	for (vector<diff*>::const_iterator i = d->children_nodes().begin();
  	     i != d->children_nodes().end();
  	     ++i)
  	  {
  	    diff* child = *i;
  	    if (child->has_changes())
  	      {
  		has_non_empty_child = true;
  		if (child->get_class_of_equiv_category() & SUPPRESSED_CATEGORY)
  		  has_suppressed_child = true;
  		else if (child->get_class_of_equiv_category()
  			 & PRIVATE_TYPE_CATEGORY)
  		  // Propagation of the PRIVATE_TYPE_CATEGORY is going
  		  // to be handled later below.
  		  ;
  		else
  		  has_non_suppressed_child = true;
  
  		if (child->get_class_of_equiv_category()
  		    & PRIVATE_TYPE_CATEGORY)
  		  has_private_child = true;
  		else if (child->get_class_of_equiv_category()
  			 & SUPPRESSED_CATEGORY)
  		  // Propagation of the SUPPRESSED_CATEGORY has been
  		  // handled above already.
  		  ;
  		else
  		  has_non_private_child = true;
  	      }
  	  }
  
  	if (has_non_empty_child
  	    && has_suppressed_child
  	    && !has_non_suppressed_child)
  	  {
  	    d->add_to_category(SUPPRESSED_CATEGORY);
  	    // If a node was suppressed, all the other nodes of its class
  	    // of equivalence are suppressed too.
  	    diff *canonical_diff = d->get_canonical_diff();
  	    if (canonical_diff != d)
  	      canonical_diff->add_to_category(SUPPRESSED_CATEGORY);
  	  }
  
  	// Note that the private-ness of a an underlying type won't be
  	// propagated to its parent typedef, by virtue of the big "if"
  	// clause at the beginning of this function.  So we don't have
  	// to handle that case here.  So the idiom of defining
  	// typedefs of private (opaque) types will be respected;
  	// meaning that changes to opaque underlying type will be
  	// flagged as private and the typedef will be flagged private
  	// as well, unless the typedef itself has local non-type
  	// changes.  In the later case, changes to the typedef will be
  	// emitted because the typedef won't inherit the privateness
  	// of its underlying type.  So in practise, the typedef
  	// remains public for the purpose of change reporting.
  	if (has_non_empty_child
  	    && has_private_child
  	    && !has_non_private_child)
  	  {
  	    d->add_to_category(PRIVATE_TYPE_CATEGORY);
  	    // If a node was suppressed, all the other nodes of its class
  	    // of equivalence are suppressed too.
  	    diff *canonical_diff = d->get_canonical_diff();
  	    if (canonical_diff != d)
  	      canonical_diff->add_to_category(PRIVATE_TYPE_CATEGORY);
  	  }
  
  	// If the underlying type of a typedef is private and carries
  	// changes (that are implicitely suppressed because it's
  	// private) then the typedef must be suppressed too, so that
  	// those changes to the underlying type are not seen.
  	if (is_typedef_diff(d)
  	    && !d->has_local_changes()
  	    && has_private_child
  	    && has_non_empty_child)
  	  {
  	    d->add_to_category(SUPPRESSED_CATEGORY|PRIVATE_TYPE_CATEGORY);
  	    // If a node was suppressed, all the other nodes of its class
  	    // of equivalence are suppressed too.
  	    diff *canonical_diff = d->get_canonical_diff();
  	    if (canonical_diff != d)
  	      canonical_diff->add_to_category
  		(SUPPRESSED_CATEGORY|PRIVATE_TYPE_CATEGORY);
  	  }
  
  	if (const function_decl_diff *fn_diff = is_function_decl_diff(d))
  	  if (!(d->has_local_changes() & LOCAL_NON_TYPE_CHANGE_KIND))
  	    {
  	      // d is a function diff that carries a local *type*
  	      // change (that means it's a change to the function
  	      // type).  Let's see if the child function type diff
  	      // node is suppressed.  That would mean that we are
  	      // instructed to show details of a diff that is deemed
  	      // suppressed; this means the suppression conflicts with
  	      // a local type change.  In that case, let's follow what
  	      // the user asked and suppress the function altogether,
  	      if (function_type_diff_sptr fn_type_diff = fn_diff->type_diff())
  		if (fn_type_diff->is_suppressed())
  		  {
  		    d->add_to_category(SUPPRESSED_CATEGORY);
  		    // If a node was suppressed, all the other nodes
  		    // of its class of equivalence are suppressed too.
  		    diff *canonical_diff = d->get_canonical_diff();
  		    if (canonical_diff != d)
  		      canonical_diff->add_to_category(SUPPRESSED_CATEGORY);
  		  }
  	  }
        }
    }
  }; //end struct suppression_categorization_visitor
  
  /// Walk a given diff-sub tree and appply the suppressions carried by
  /// the context.  If the suppression applies to a given node than
  /// categorize the node into the SUPPRESSED_CATEGORY category and
  /// propagate that categorization.
  ///
  /// @param diff_tree the diff-sub tree to apply the suppressions to.
  void
  apply_suppressions(diff* diff_tree)
  {
    if (diff_tree && !diff_tree->context()->suppressions().empty())
      {
        // Apply suppressions to functions and variables that have
        // changed sub-types.
        suppression_categorization_visitor v;
        diff_tree->context()->forget_visited_diffs();
        bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
        diff_tree->context()->forbid_visiting_a_node_twice(true);
        diff_tree->traverse(v);
        diff_tree->context()->forbid_visiting_a_node_twice(s);
      }
  }
  
  /// Walk a given diff-sub tree and appply the suppressions carried by
  /// the context.  If the suppression applies to a given node than
  /// categorize the node into the SUPPRESSED_CATEGORY category and
  /// propagate that categorization.
  ///
  /// @param diff_tree the diff-sub tree to apply the suppressions to.
  void
  apply_suppressions(diff_sptr diff_tree)
  {apply_suppressions(diff_tree.get());}
  
  /// Walk a @ref corpus_diff tree and appply the suppressions carried
  /// by the context.  If the suppression applies to a given node then
  /// categorize the node into the SUPPRESSED_CATEGORY category and
  /// propagate that categorization.
  ///
  /// @param diff_tree the diff tree to apply the suppressions to.
  void
  apply_suppressions(const corpus_diff* diff_tree)
  {
    if (diff_tree && !diff_tree->context()->suppressions().empty())
      {
        // First, visit the children trees of changed constructs:
        // changed functions, variables, as well as sub-types of these,
        // and apply suppression specifications to these ...
        suppression_categorization_visitor v;
        diff_tree->context()->forget_visited_diffs();
        bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
        diff_tree->context()->forbid_visiting_a_node_twice(true);
        const_cast<corpus_diff*>(diff_tree)->traverse(v);
        diff_tree->context()->forbid_visiting_a_node_twice(s);
  
        // ... then also visit the set of added and removed functions,
        // variables, symbols, and types not reachable from global
        // functions and variables.
        diff_tree->priv_->
  	apply_supprs_to_added_removed_fns_vars_unreachable_types();
      }
  }
  
  /// Walk a diff tree and appply the suppressions carried by the
  /// context.  If the suppression applies to a given node than
  /// categorize the node into the SUPPRESSED_CATEGORY category and
  /// propagate that categorization.
  ///
  /// @param diff_tree the diff tree to apply the suppressions to.
  void
  apply_suppressions(corpus_diff_sptr  diff_tree)
  {apply_suppressions(diff_tree.get());}
  
  // </diff tree category propagation>
  
  // <diff tree printing stuff>
  
  /// A visitor to print (to an output stream) a pretty representation
  /// of a @ref diff sub-tree or of a complete @ref corpus_diff tree.
  struct diff_node_printer : public diff_node_visitor
  {
    ostream& out_;
    unsigned level_;
  
    /// Emit a certain number of spaces to the output stream associated
    /// to this diff_node_printer.
    ///
    /// @param level half of the numver of spaces to emit.
    void
    do_indent(unsigned level)
    {
      for (unsigned i = 0; i < level; ++i)
        out_ << "  ";
    }
  
    diff_node_printer(ostream& out)
      : diff_node_visitor(DO_NOT_MARK_VISITED_NODES_AS_VISITED),
        out_(out),
        level_(0)
    {}
  
    virtual void
    visit_begin(diff*)
    {
      ++level_;
    }
  
    virtual void
    visit_end(diff*)
    {
      --level_;
    }
  
    virtual void
    visit_begin(corpus_diff*)
    {
      ++level_;
    }
  
    virtual void
    visit_end(corpus_diff*)
    {
      --level_;
    }
  
    virtual bool
    visit(diff* d, bool pre)
    {
      if (!pre)
        // We are post-visiting the diff node D.  Which means, we have
        // printed a pretty representation for it already.  So do
        // nothing now.
        return true;
  
      do_indent(level_);
      out_ << d->get_pretty_representation();
      out_ << "\n";
      do_indent(level_);
      out_ << "{\n";
      do_indent(level_ + 1);
      out_ << "category: "<< d->get_category() << "\n";
      do_indent(level_ + 1);
      out_ << "@: " << std::hex << d << std::dec << "\n";
      do_indent(level_ + 1);
      out_ << "@-canonical: " << std::hex
  	 << d->get_canonical_diff()
  	 << std::dec << "\n";
      do_indent(level_);
      out_ << "}\n";
  
      return true;
    }
  
    virtual bool
    visit(corpus_diff* d, bool pre)
    {
      if (!pre)
        // We are post-visiting the diff node D.  Which means, we have
        // printed a pretty representation for it already.  So do
        // nothing now.
        return true;
  
      // indent
      for (unsigned i = 0; i < level_; ++i)
        out_ << ' ';
      out_ << d->get_pretty_representation();
      out_ << '\n';
      return true;
    }
  }; // end struct diff_printer_visitor
  
  // </ diff tree printing stuff>
  
  /// Emit a textual representation of a @ref diff sub-tree to an
  /// output stream.
  ///
  /// @param diff_tree the sub-tree to emit the textual representation
  /// for.
  ///
  /// @param out the output stream to emit the textual representation
  /// for @p diff_tree to.
  void
  print_diff_tree(diff* diff_tree, ostream& out)
  {
    diff_node_printer p(out);
    bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
    diff_tree->context()->forbid_visiting_a_node_twice(false);
    diff_tree->traverse(p);
    diff_tree->context()->forbid_visiting_a_node_twice(s);
  }
  
  /// Emit a textual representation of a @ref corpus_diff tree to an
  /// output stream.
  ///
  /// @param diff_tree the @ref corpus_diff tree to emit the textual
  /// representation for.
  ///
  /// @param out the output stream to emit the textual representation
  /// for @p diff_tree to.
  void
  print_diff_tree(corpus_diff* diff_tree, std::ostream& out)
  {
    diff_node_printer p(out);
    bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
    diff_tree->context()->forbid_visiting_a_node_twice(false);
    diff_tree->traverse(p);
    diff_tree->context()->forbid_visiting_a_node_twice(s);
  }
  
  /// Emit a textual representation of a @ref diff sub-tree to an
  /// output stream.
  ///
  /// @param diff_tree the sub-tree to emit the textual representation
  /// for.
  ///
  /// @param out the output stream to emit the textual representation
  /// for @p diff_tree to.
  void
  print_diff_tree(diff_sptr diff_tree,
  		std::ostream& o)
  {print_diff_tree(diff_tree.get(), o);}
  
  /// Emit a textual representation of a @ref corpus_diff tree to an
  /// output stream.
  ///
  /// @param diff_tree the @ref corpus_diff tree to emit the textual
  /// representation for.
  ///
  /// @param out the output stream to emit the textual representation
  /// for @p diff_tree to.
  void
  print_diff_tree(corpus_diff_sptr diff_tree,
  		std::ostream& o)
  {print_diff_tree(diff_tree.get(), o);}
  
  // <redundancy_marking_visitor>
  
  /// A tree visitor to categorize nodes with respect to the
  /// REDUNDANT_CATEGORY.  That is, detect if a node is redundant (is
  /// present on several spots of the tree) and mark such nodes
  /// appropriatly.  This visitor also takes care of propagating the
  /// REDUNDANT_CATEGORY of a given node to its parent nodes as
  /// appropriate.
  struct redundancy_marking_visitor : public diff_node_visitor
  {
    bool skip_children_nodes_;
  
    redundancy_marking_visitor()
      : skip_children_nodes_()
    {}
  
    virtual void
    visit_begin(diff* d)
    {
      if (d->to_be_reported())
        {
  	// A diff node that carries a change and that has been already
  	// traversed elsewhere is considered redundant.  So let's mark
  	// it as such and let's not traverse it; that is, let's not
  	// visit its children.
  	if ((d->context()->diff_has_been_visited(d)
  	     || d->get_canonical_diff()->is_traversing())
  	    && d->has_changes())
  	  {
  	    // But if two diff nodes are redundant sibbling that carry
  	    // changes of base types, do not mark them as being
  	    // redundant.  This is to avoid marking nodes as redundant
  	    // in this case:
  	    //
  	    //     int foo(int a, int b);
  	    // compared with:
  	    //     float foo(float a, float b); (in C).
  	    //
  	    // In this case, we want to report all the occurences of
  	    // the int->float change because logically, they are at
  	    // the same level in the diff tree.
  
  	    bool redundant_with_sibling_node = false;
  	    const diff* p = d->parent_node();
  
  	    // If this is a child node of a fn_parm_diff, look through
  	    // the fn_parm_diff node to get the function diff node.
  	    if (p && dynamic_cast<const fn_parm_diff*>(p))
  	      p = p->parent_node();
  
  	    if (p)
  	      for (vector<diff*>::const_iterator s =
  		     p->children_nodes().begin();
  		   s != p->children_nodes().end();
  		   ++s)
  		{
  		  if (*s == d)
  		    continue;
  		  diff* sib = *s;
  		  // If this is a fn_parm_diff, look through the
  		  // fn_parm_diff node to get at the real type node.
  		  if (fn_parm_diff* f = dynamic_cast<fn_parm_diff*>(*s))
  		    sib = f->type_diff().get();
  		  if (sib == d)
  		    continue;
  		  if (sib->get_canonical_diff() == d->get_canonical_diff()
  		      // Sibbling diff nodes that carry base type
  		      // changes ar to be marked as redundant.
  		      && (is_base_diff(sib) || is_distinct_diff(sib)))
  		    {
  		      redundant_with_sibling_node = true;
  		      break;
  		    }
  		}
  	    if (!redundant_with_sibling_node
  		// Changes to basic types should never be considered
  		// redundant.  For instance, if a member of integer
  		// type is changed into a char type in both a struct A
  		// and a struct B, we want to see both changes.
  		&& !has_basic_type_change_only(d)
  		// The same goes for distinct type changes
  		&& !filtering::is_mostly_distinct_diff(d)
  		// Functions with similar *local* changes are never marked
  		// redundant because otherwise one could miss important
  		// similar local changes that are applied to different
  		// functions.
  		&& !is_function_type_diff_with_local_changes(d)
  		// Changes involving variadic parameters of functions
  		// should never be marked redundant because we want to see
  		// them all.
  		&& !is_diff_of_variadic_parameter(d)
  		&& !is_diff_of_variadic_parameter_type(d)
  		// If the canonical diff itself has been filtered out,
  		// then this one is not marked redundant, unless the
  		// canonical diff was already redundant.
  		&& (!d->get_canonical_diff()->is_filtered_out()
  		    || (d->get_canonical_diff()->get_category()
  			& REDUNDANT_CATEGORY))
  		// If the *same* diff node (not one that is merely
  		// equivalent to this one) has already been visited
  		// the do not mark it as beind redundant.  It's only
  		// the other nodes that are equivalent to this one
  		// that must be marked redundant.
  		&& d->context()->diff_has_been_visited(d) != d
  		// If the diff node is a function parameter and is not
  		// a reference/pointer (to a non basic or a non
  		// distinct type diff) then do not mark it as
  		// redundant.
  		//
  		// Children nodes of base class diff nodes are never
  		// redundant either, we want to see them all.
  		&& (is_reference_or_ptr_diff_to_non_basic_nor_distinct_types(d)
  		    || (!is_child_node_of_function_parm_diff(d)
  			&& !is_child_node_of_base_diff(d))))
  	      {
  		d->add_to_category(REDUNDANT_CATEGORY);
  		// As we said in preamble, as this node is marked as
  		// being redundant, let's not visit its children.
  		// This is not an optimization; it's needed for
  		// correctness.  In the case of a diff node involving
  		// a class type that refers to himself, visiting the
  		// children nodes might cause them to be wrongly
  		// marked as redundant.
  		set_visiting_kind(get_visiting_kind()
  				  | SKIP_CHILDREN_VISITING_KIND);
  		skip_children_nodes_ = true;
  	      }
  	  }
        }
      else
        {
  	// If the node is not to be reported, do not look at it children.
  	set_visiting_kind(get_visiting_kind() | SKIP_CHILDREN_VISITING_KIND);
  	skip_children_nodes_ = true;
        }
    }
  
    virtual void
    visit_begin(corpus_diff*)
    {
    }
  
    virtual void
    visit_end(diff* d)
    {
      if (skip_children_nodes_)
        // When visiting this node, we decided to skip its children
        // node.  Now that we are done visiting the node, lets stop
        // avoiding the children nodes visiting for the other tree
        // nodes.
        {
  	set_visiting_kind(get_visiting_kind() & (~SKIP_CHILDREN_VISITING_KIND));
  	skip_children_nodes_ = false;
        }
      else
        {
  	// Propagate the redundancy categorization of the children nodes
  	// to this node.  But if this node has local changes, then it
  	// doesn't inherit redundancy from its children nodes.
  	if (!(d->get_category() & REDUNDANT_CATEGORY)
  	    && (!d->has_local_changes_to_be_reported()
  		// By default, pointer, reference and qualified types
  		// consider that a local changes to their underlying
  		// type is always a local change for themselves.
  		//
  		// This is as if those types don't have local changes
  		// in the same sense as other types.  So we always
  		// propagate redundancy to them, regardless of if they
  		// have local changes or not.
  		//
  		// We also propagate redundancy to typedef types if
  		// these /only/ carry changes to their underlying
  		// type.
  		//
  		// Note that changes to the underlying type of a
  		// typedef is considered local of
  		// LOCAL_TYPE_CHANGE_KIND kind.  The other changes to the
  		// typedef itself are considered local of
  		// LOCAL_NON_TYPE_CHANGE_KIND kind.
  		|| is_pointer_diff(d)
  		|| is_qualified_type_diff(d)
  		|| (is_typedef_diff(d)
  		    && (!(d->has_local_changes()
  			  & LOCAL_NON_TYPE_CHANGE_KIND)))))
  	  {
  	    bool has_non_redundant_child = false;
  	    bool has_non_empty_child = false;
  	    for (vector<diff*>::const_iterator i =
  		   d->children_nodes().begin();
  		 i != d->children_nodes().end();
  		 ++i)
  	      {
  		if ((*i)->has_changes())
  		  {
  		    has_non_empty_child = true;
  		    // Let's see if the current child node '*i' is
  		    // "non-redundant".
  		    //
  		    // A non-redundant node would be a node that
  		    // carries a change to be reported and has not
  		    // been marked as being redundant.
  		    if ((*i)->to_be_reported()
  			&& ((*i)->get_category() & REDUNDANT_CATEGORY) == 0)
  		      has_non_redundant_child = true;
  		  }
  		if (has_non_redundant_child)
  		  break;
  	      }
  
  	    // A diff node for which at least a child node carries a
  	    // change, and for which all the children are redundant is
  	    // deemed redundant too, unless it has local changes.
  	    if (has_non_empty_child
  		&& !has_non_redundant_child)
  	      d->add_to_category(REDUNDANT_CATEGORY);
  	  }
        }
    }
  
    virtual void
    visit_end(corpus_diff*)
    {
    }
  
    virtual bool
    visit(diff*, bool)
    {return true;}
  
    virtual bool
    visit(corpus_diff*, bool)
    {
      return true;
    }
  };// end struct redundancy_marking_visitor
  
  /// A visitor of @ref diff nodes that clears the REDUNDANT_CATEGORY
  /// category out of the nodes.
  struct redundancy_clearing_visitor : public diff_node_visitor
  {
    bool
    visit(corpus_diff*, bool)
    {return true;}
  
    bool
    visit(diff* d, bool)
    {
      // clear the REDUNDANT_CATEGORY out of the current node.
      diff_category c = d->get_category();
      c &= ~REDUNDANT_CATEGORY;
      d->set_category(c);
      return true;
    }
  }; // end struct redundancy_clearing_visitor
  
  /// Walk a given @ref diff sub-tree to categorize each of the nodes
  /// with respect to the REDUNDANT_CATEGORY.
  ///
  /// @param diff_tree the @ref diff sub-tree to walk.
  void
  categorize_redundancy(diff* diff_tree)
  {
    if (diff_tree->context()->show_redundant_changes())
      return;
    redundancy_marking_visitor v;
    bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
    diff_tree->context()->forbid_visiting_a_node_twice(false);
    diff_tree->traverse(v);
    diff_tree->context()->forbid_visiting_a_node_twice(s);
  }
  
  /// Walk a given @ref diff sub-tree to categorize each of the nodes
  /// with respect to the REDUNDANT_CATEGORY.
  ///
  /// @param diff_tree the @ref diff sub-tree to walk.
  void
  categorize_redundancy(diff_sptr diff_tree)
  {categorize_redundancy(diff_tree.get());}
  
  /// Walk a given @ref corpus_diff tree to categorize each of the nodes
  /// with respect to the REDUNDANT_CATEGORY.
  ///
  /// @param diff_tree the @ref corpus_diff tree to walk.
  void
  categorize_redundancy(corpus_diff* diff_tree)
  {
    redundancy_marking_visitor v;
    diff_tree->context()->forget_visited_diffs();
    bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
    diff_tree->context()->forbid_visiting_a_node_twice(false);
    diff_tree->traverse(v);
    diff_tree->context()->forbid_visiting_a_node_twice(s);
  }
  
  /// Walk a given @ref corpus_diff tree to categorize each of the nodes
  /// with respect to the REDUNDANT_CATEGORY.
  ///
  /// @param diff_tree the @ref corpus_diff tree to walk.
  void
  categorize_redundancy(corpus_diff_sptr diff_tree)
  {categorize_redundancy(diff_tree.get());}
  
  // </redundancy_marking_visitor>
  
  /// Walk a given @ref diff sub-tree to clear the REDUNDANT_CATEGORY
  /// out of the category of the nodes.
  ///
  /// @param diff_tree the @ref diff sub-tree to walk.
  void
  clear_redundancy_categorization(diff* diff_tree)
  {
    redundancy_clearing_visitor v;
    bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
    diff_tree->context()->forbid_visiting_a_node_twice(false);
    diff_tree->traverse(v);
    diff_tree->context()->forbid_visiting_a_node_twice(s);
    diff_tree->context()->forget_visited_diffs();
  }
  
  /// Walk a given @ref diff sub-tree to clear the REDUNDANT_CATEGORY
  /// out of the category of the nodes.
  ///
  /// @param diff_tree the @ref diff sub-tree to walk.
  void
  clear_redundancy_categorization(diff_sptr diff_tree)
  {clear_redundancy_categorization(diff_tree.get());}
  
  /// Walk a given @ref corpus_diff tree to clear the REDUNDANT_CATEGORY
  /// out of the category of the nodes.
  ///
  /// @param diff_tree the @ref corpus_diff tree to walk.
  void
  clear_redundancy_categorization(corpus_diff* diff_tree)
  {
    redundancy_clearing_visitor v;
    bool s = diff_tree->context()->visiting_a_node_twice_is_forbidden();
    diff_tree->context()->forbid_visiting_a_node_twice(false);
    diff_tree->traverse(v);
    diff_tree->context()->forbid_visiting_a_node_twice(s);
    diff_tree->context()->forget_visited_diffs();
  }
  
  /// Walk a given @ref corpus_diff tree to clear the REDUNDANT_CATEGORY
  /// out of the category of the nodes.
  ///
  /// @param diff_tree the @ref corpus_diff tree to walk.
  void
  clear_redundancy_categorization(corpus_diff_sptr diff_tree)
  {clear_redundancy_categorization(diff_tree.get());}
  
  /// Apply the @ref diff tree filters that have been associated to the
  /// context of the a given @ref corpus_diff tree.  As a result, the
  /// nodes of the @diff tree are going to be categorized into one of
  /// several of the categories of @ref diff_category.
  ///
  /// @param diff_tree the @ref corpus_diff instance which @ref diff are
  /// to be categorized.
  void
  apply_filters(corpus_diff_sptr diff_tree)
  {
    diff_tree->context()->maybe_apply_filters(diff_tree);
    propagate_categories(diff_tree);
  }
  
  /// Test if a diff node represents the difference between a variadic
  /// parameter type and something else.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return true iff @p d is a diff node that represents the
  /// difference between a variadic parameter type and something else.
  bool
  is_diff_of_variadic_parameter_type(const diff* d)
  {
    if (!d)
      return false;
  
    type_base_sptr t = is_type(d->first_subject());
    if (t && t->get_environment()->is_variadic_parameter_type(t))
      return true;
  
    t = is_type(d->second_subject());
    if (t && t->get_environment()->is_variadic_parameter_type(t))
      return true;
  
    return false;
  }
  
  /// Test if a diff node represents the difference between a variadic
  /// parameter type and something else.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return true iff @p d is a diff node that represents the
  /// difference between a variadic parameter type and something else.
  bool
  is_diff_of_variadic_parameter_type(const diff_sptr& d)
  {return is_diff_of_variadic_parameter_type(d.get());}
  
  /// Test if a diff node represents the difference between a variadic
  /// parameter and something else.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return true iff @p d is a diff node that represents the
  /// difference between a variadic parameter and something else.
  bool
  is_diff_of_variadic_parameter(const diff* d)
  {
    fn_parm_diff* diff =
      dynamic_cast<fn_parm_diff*>(const_cast<abigail::comparison::diff*>(d));
    return (diff && is_diff_of_variadic_parameter_type(diff->type_diff()));
  }
  
  /// Test if a diff node represents the difference between a variadic
  /// parameter and something else.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return true iff @p d is a diff node that represents the
  /// difference between a variadic parameter and something else.
  bool
  is_diff_of_variadic_parameter(const diff_sptr& d)
  {return is_diff_of_variadic_parameter(d.get());}
  
  /// Test if a diff node represents a diff between two basic types.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return true iff @p d is a diff between two basic types.
  const type_decl_diff*
  is_diff_of_basic_type(const diff *d)
  {return dynamic_cast<const type_decl_diff*>(d);}
  
  /// Test if a diff node represents a diff between two basic types, or
  /// between pointers, references or qualified type to basic types.
  ///
  /// @param diff the diff node to consider.
  ///
  /// @param allow_indirect_type if true, then this function looks into
  /// pointer, reference or qualified diff types to see if they "point
  /// to" basic types.
  ///
  /// @return true iff @p d is a diff between two basic types.
  const type_decl_diff*
  is_diff_of_basic_type(const diff* diff, bool allow_indirect_type)
  {
    if (allow_indirect_type)
        diff = peel_pointer_or_qualified_type_diff(diff);
    return is_diff_of_basic_type(diff);
  }
  
  /// If a diff node is about changes between two typedef types, get the
  /// diff node about changes between the underlying types.
  ///
  /// Note that this function walks the tree of underlying diff nodes
  /// returns the first diff node about types that are not typedefs.
  ///
  /// @param dif the dif node to consider.
  ///
  /// @return the underlying diff node of @p dif, or just return @p dif
  /// if it's not a typedef diff node.
  const diff*
  peel_typedef_diff(const diff* dif)
  {
    const typedef_diff *d = 0;
    while ((d = is_typedef_diff(dif)))
      dif = d->underlying_type_diff().get();
    return dif;
  }
  
  /// If a diff node is about changes between two pointer types, get the
  /// diff node about changes between the underlying (pointed-to) types.
  ///
  /// Note that this function walks the tree of underlying diff nodes
  /// returns the first diff node about types that are not pointers.
  ///
  /// @param dif the dif node to consider.
  ///
  /// @return the underlying diff node of @p dif, or just return @p dif
  /// if it's not a pointer diff node.
  const diff*
  peel_pointer_diff(const diff* dif)
  {
    const pointer_diff *d = 0;
    while ((d = is_pointer_diff(dif)))
      dif = d->underlying_type_diff().get();
    return dif;
  }
  
  /// If a diff node is about changes between two reference types, get
  /// the diff node about changes between the underlying (pointed-to)
  /// types.
  ///
  /// Note that this function walks the tree of underlying diff nodes
  /// returns the first diff node about types that are not references.
  ///
  /// @param dif the dif node to consider.
  ///
  /// @return the underlying diff node of @p dif, or just return @p dif
  /// if it's not a reference diff node.
  const diff*
  peel_reference_diff(const diff* dif)
  {
    const reference_diff *d = 0;
    while ((d = is_reference_diff(dif)))
      dif = d->underlying_type_diff().get();
    return dif;
  }
  
  /// If a diff node is about changes between two qualified types, get
  /// the diff node about changes between the underlying (non-qualified)
  /// types.
  ///
  /// Note that this function walks the tree of underlying diff nodes
  /// returns the first diff node about types that are not qualified.
  ///
  /// @param dif the dif node to consider.
  ///
  /// @return the underlying diff node of @p dif, or just return @p dif
  /// if it's not a qualified diff node.
  const diff*
  peel_qualified_diff(const diff* dif)
  {
    const qualified_type_diff *d = 0;
    while ((d = is_qualified_type_diff(dif)))
      dif = d->underlying_type_diff().get();
    return dif;
  }
  
  /// If a diff node is about changes between two pointer, reference or
  /// qualified types, get the diff node about changes between the
  /// underlying types.
  ///
  /// Note that this function walks the tree of underlying diff nodes
  /// returns the first diff node about types that are not pointer,
  /// reference or qualified.
  ///
  /// @param dif the dif node to consider.
  ///
  /// @return the underlying diff node of @p dif, or just return @p dif
  /// if it's not a pointer, reference or qualified diff node.
  const diff*
  peel_pointer_or_qualified_type_diff(const diff*dif)
  {
    while (true)
      {
        if (const pointer_diff *d = is_pointer_diff(dif))
  	dif = peel_pointer_diff(d);
        else if (const reference_diff *d = is_reference_diff(dif))
  	dif = peel_reference_diff(d);
        else if (const qualified_type_diff *d = is_qualified_type_diff(dif))
  	dif = peel_qualified_diff(d);
        else
  	break;
      }
    return dif;
  }
  
  /// If a diff node is about changes between two typedefs or qualified
  /// types, get the diff node about changes between the underlying
  /// types.
  ///
  /// Note that this function walks the tree of underlying diff nodes
  /// returns the first diff node about types that are not typedef or
  /// qualified types.
  ///
  /// @param dif the dif node to consider.
  ///
  /// @return the underlying diff node of @p dif, or just return @p dif
  /// if it's not typedef or qualified diff node.
  const diff*
  peel_typedef_or_qualified_type_diff(const diff *dif)
  {
    while (true)
      {
        if (const typedef_diff *d = is_typedef_diff(dif))
  	dif = peel_typedef_diff(d);
        else if (const qualified_type_diff *d = is_qualified_type_diff(dif))
  	dif = peel_qualified_diff(d);
        else
  	break;
      }
    return dif;
  }
  
  /// Test if a diff node represents a diff between two class or union
  /// types.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return iff @p is a diff between two class or union types then
  /// return the instance of @ref class_or_union_diff that @p derives
  /// from.  Otherwise, return nil.
  const class_or_union_diff*
  is_diff_of_class_or_union_type(const diff *d)
  {return dynamic_cast<const class_or_union_diff*>(d);}
  
  /// Test if a given diff node carries *only* a local type change.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return true iff @p has a change and that change is a local type
  /// change.
  static bool
  has_local_type_change_only(const diff *d)
  {
    if (enum change_kind k = d->has_local_changes())
      if ((k & LOCAL_NON_TYPE_CHANGE_KIND) == 0
  	&& (k & LOCAL_TYPE_CHANGE_KIND) != 0)
        return true;
  
    return false;
  }
  
  /// Test if a diff node is a decl diff that only carries a basic type
  /// change on its type diff sub-node.
  ///
  ///Note that that pointers/references/qualified types diffs to basic
  /// type diffs are considered as having basic type change only.
  ///
  /// @param d the diff node to consider.
  ///
  /// @return true iff @p d is a decl diff that only carries a basic
  /// type change on its type diff sub-node.
  bool
  has_basic_type_change_only(const diff *d)
  {
    if (is_diff_of_basic_type(d, true) && d->has_changes())
      return true;
    else if (const var_diff * v = dynamic_cast<const var_diff*>(d))
      return (has_local_type_change_only(v)
  	    && is_diff_of_basic_type(v->type_diff().get(), true));
    else if (const fn_parm_diff * p = dynamic_cast<const fn_parm_diff*>(d))
      return (has_local_type_change_only(p)
  	    && is_diff_of_basic_type(p->type_diff().get(), true));
    else if (const function_decl_diff* f =
  	   dynamic_cast<const function_decl_diff*>(d))
      return (has_local_type_change_only(f)
  	    && f->type_diff()
  	    && is_diff_of_basic_type(f->type_diff()->return_type_diff().get(),
  				     true));
    return false;
  }
  }// end namespace comparison
  } // end namespace abigail
  