// -*- mode: C++ -*-
//
// Copyright (C) 2013-2020 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail).  This library is
// free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3, or (at your option) any
// later version.

// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Lesser Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this program; see the file COPYING-LGPLV3.  If
// not, see <http://www.gnu.org/licenses/>.
//
//Author: Dodji Seketeli

/// @file
///
/// Definitions for the Internal Representation artifacts of libabigail.

#include <cxxabi.h>
#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
#include <typeinfo>
#include <sstream>

#include "abg-cxx-compat.h"
#include "abg-internal.h"
// <headers defining libabigail's API go under here>
ABG_BEGIN_EXPORT_DECLARATIONS

#include "abg-interned-str.h"
#include "abg-ir.h"
#include "abg-corpus.h"
#include "abg-corpus-priv.h"

ABG_END_EXPORT_DECLARATIONS
// </headers defining libabigail's API>

#include "abg-tools-utils.h"
#include "abg-comp-filter.h"
#include "abg-ir-priv.h"

namespace
{
/// This internal type is a tree walker that walks the sub-tree of a
/// type and sets the environment of the type (including its sub-type)
/// to a new environment.
class environment_setter : public abigail::ir::ir_node_visitor
{
  const abigail::ir::environment* env_;

public:
  environment_setter(const abigail::ir::environment* env)
    : env_(env)
  {}

  /// This function is called on each sub-tree node that is a
  /// declaration.  Note that it's also called on some types because
  /// most types that have a declarations also inherit the type @ref
  /// decl_base.
  ///
  /// @param d the declaration being visited.
  bool
  visit_begin(abigail::ir::decl_base* d)
  {
    if (const abigail::ir::environment* env = d->get_environment())
      {
	ABG_ASSERT(env == env_);
	return false;
      }
    else
      d->set_environment(env_);

    return true;

  }

  /// This function is called on each sub-tree node that is a type.
  ///
  /// @param t the type being visited.
  bool
  visit_begin(abigail::ir::type_base* t)
  {
    if (abigail::ir::environment* env = t->get_environment())
      {
	ABG_ASSERT(env == env_);
	return false;
      }
    else
      {
	ABG_ASSERT(!t->get_environment());
	t->set_environment(env_);
      }
    return true;
  }
};

/// This internal type is a tree walking that is used to set the
/// qualified name of a tree of decls and types.  It used by the
/// function update_qualified_name().
class qualified_name_setter : public abigail::ir::ir_node_visitor
{

public:
  bool
  do_update(abigail::ir::decl_base* d);

  bool
  visit_begin(abigail::ir::decl_base* d);

  bool
  visit_begin(abigail::ir::type_base* d);
}; // end class qualified_name_setter

}// end anon namespace

namespace abigail
{

// Inject.
using std::string;
using std::list;
using std::vector;
using abg_compat::unordered_map;
using abg_compat::dynamic_pointer_cast;
using abg_compat::static_pointer_cast;

/// Convenience typedef for a map of string -> string*.
typedef unordered_map<string, string*> pool_map_type;

/// The type of the private data structure of type @ref
/// intered_string_pool.
struct interned_string_pool::priv
{
  pool_map_type map;
}; //end struc struct interned_string_pool::priv

/// Default constructor.
interned_string_pool::interned_string_pool()
  : priv_(new priv)
{
  priv_->map[""] = 0;
}

/// Test if the interned string pool already contains a string with a
/// given value.
///
/// @param s the string to test for.
///
/// @return true if the pool contains a string with the value @p s.
bool
interned_string_pool::has_string(const char* s) const
{return priv_->map.find(s) != priv_->map.end();}

/// Get a pointer to the interned string which has a given value.
///
/// @param s the value of the interned string to look for.
///
/// @return a pointer to the raw string of characters which has the
/// value of @p s.  Or null if no string with value @p s was interned.
const char*
interned_string_pool::get_string(const char* s) const
{
  unordered_map<string, string*>::const_iterator i =
    priv_->map.find(s);
  if (i == priv_->map.end())
    return 0;
  if (i->second)
    return i->second->c_str();
  return "";
}

/// Create an interned string with a given value.
///
/// @param str_value the value of the interned string to create.
///
/// @return the new created instance of @ref interned_string created.
interned_string
interned_string_pool::create_string(const std::string& str_value)
{
  string*& result = priv_->map[str_value];
  if (!result && !str_value.empty())
    result = new string(str_value);
  return interned_string(result);
}

/// Destructor.
interned_string_pool::~interned_string_pool()
{
  for (pool_map_type::iterator i = priv_->map.begin();
       i != priv_->map.end();
       ++i)
    if (i->second)
      delete i->second;
}

/// Equality operator.
///
/// @param l the instance of std::string on the left-hand-side of the
/// equality operator.
///
/// @param r the instance of @ref interned_string on the
/// right-hand-side of the equality operator.
///
/// @return true iff the two string are equal.
bool
operator==(const std::string& l, const interned_string& r)
{return r.operator==(l);}

bool
operator!=(const std::string& l, const interned_string& r)
{return !(l == r);}

/// Streaming operator.
///
/// Streams an instance of @ref interned_string to an output stream.
///
/// @param o the destination output stream.
///
/// @param s the instance of @ref interned_string to stream out.
///
/// @return the output stream this function just streamed to.
std::ostream&
operator<<(std::ostream& o, const interned_string& s)
{
  o << static_cast<std::string>(s);
  return o;
}

/// Concatenation operator.
///
/// Concatenate two instances of @ref interned_string, builds an
/// instance of std::string with the resulting string and return it.
///
/// @param s1 the first string to consider.
///
/// @param s2 the second string to consider.
///
/// @return the resuting concatenated string.
std::string
operator+(const interned_string& s1,const std::string& s2)
{return static_cast<std::string>(s1) + s2;}

/// Concatenation operator.
///
/// Concatenate two instances of @ref interned_string, builds an
/// instance of std::string with the resulting string and return it.
///
/// @param s1 the first string to consider.
///
/// @param s2 the second string to consider.
///
/// @return the resuting concatenated string.
std::string
operator+(const std::string& s1, const interned_string& s2)
{return s1 + static_cast<std::string>(s2);}

namespace ir
{

static size_t
hash_as_canonical_type_or_constant(const type_base *t);

/// @brief the location of a token represented in its simplest form.
/// Instances of this type are to be stored in a sorted vector, so the
/// type must have proper relational operators.
class expanded_location
{
  string	path_;
  unsigned	line_;
  unsigned	column_;

  expanded_location();

public:

  friend class location_manager;

  expanded_location(const string& path, unsigned line, unsigned column)
  : path_(path), line_(line), column_(column)
  {}

  bool
  operator==(const expanded_location& l) const
  {
    return (path_ == l.path_
	    && line_ == l.line_
	    && column_ && l.column_);
  }

  bool
  operator<(const expanded_location& l) const
  {
    if (path_ < l.path_)
      return true;
    else if (path_ > l.path_)
      return false;

    if (line_ < l.line_)
      return true;
    else if (line_ > l.line_)
      return false;

    return column_ < l.column_;
  }
};

/// Expand the location into a tripplet path, line and column number.
///
/// @param path the output parameter where this function sets the
/// expanded path.
///
/// @param line the output parameter where this function sets the
/// expanded line.
///
/// @param column the ouptut parameter where this function sets the
/// expanded column.
void
location::expand(std::string& path, unsigned& line, unsigned& column) const
{
  ABG_ASSERT(get_location_manager());
  get_location_manager()->expand_location(*this, path, line, column);
}


/// Expand the location into a string.
///
/// @return the string representing the location.
string
location::expand(void) const
{
  string path, result;
  unsigned line = 0, column = 0;
  expand(path, line, column);

  std::ostringstream o;
  o << path << ":" << line << ":" << column;
  return o.str();
}

struct location_manager::priv
{
  /// This sorted vector contains the expanded locations of the tokens
  /// coming from a given ABI Corpus.  The index of a given expanded
  /// location in the table gives us an integer that is used to build
  /// instance of location types.
  std::vector<expanded_location> locs;
};

location_manager::location_manager()
{priv_ = shared_ptr<location_manager::priv>(new location_manager::priv);}

/// Insert the triplet representing a source locus into our internal
/// vector of location triplet.  Return an instance of location type,
/// built from an integral type that represents the index of the
/// source locus triplet into our source locus table.
///
/// @param file_path the file path of the source locus
/// @param line the line number of the source location
/// @param col the column number of the source location
location
location_manager::create_new_location(const std::string&	file_path,
				      size_t			line,
				      size_t			col)
{
  expanded_location l(file_path, line, col);

  // Just append the new expanded location to the end of the vector
  // and return its index.  Note that indexes start at 1.
  priv_->locs.push_back(l);
  return location(priv_->locs.size(), this);
}

/// Given an instance of location type, return the triplet
/// {path,line,column} that represents the source locus.  Note that
/// the location must have been previously created from the function
/// location_manager::create_new_location, otherwise this function yields
/// unexpected results, including possibly a crash.
///
/// @param location the instance of location type to expand
/// @param path the resulting path of the source locus
/// @param line the resulting line of the source locus
/// @param column the resulting colum of the source locus
void
location_manager::expand_location(const location&	location,
				  std::string&		path,
				  unsigned&		line,
				  unsigned&		column) const
{
  if (location.value_ == 0)
    return;
  expanded_location &l = priv_->locs[location.value_ - 1];
  path = l.path_;
  line = l.line_;
  column = l.column_;
}

typedef unordered_map<function_type_sptr,
		      bool,
		      function_type::hash,
		      type_shared_ptr_equal> fn_type_ptr_map;

// <type_maps stuff>

struct type_maps::priv
{
  mutable istring_type_base_wptrs_map_type	basic_types_;
  mutable istring_type_base_wptrs_map_type	class_types_;
  mutable istring_type_base_wptrs_map_type	union_types_;
  mutable istring_type_base_wptrs_map_type	enum_types_;
  mutable istring_type_base_wptrs_map_type	typedef_types_;
  mutable istring_type_base_wptrs_map_type	qualified_types_;
  mutable istring_type_base_wptrs_map_type	pointer_types_;
  mutable istring_type_base_wptrs_map_type	reference_types_;
  mutable istring_type_base_wptrs_map_type	array_types_;
  mutable istring_type_base_wptrs_map_type	subrange_types_;
  mutable istring_type_base_wptrs_map_type	function_types_;
  mutable vector<type_base_wptr>		sorted_types_;
}; // end struct type_maps::priv

type_maps::type_maps()
  : priv_(new priv)
{}

/// Test if the type_maps is empty.
///
/// @return true iff the type_maps is empty.
bool
type_maps::empty() const
{
  return (basic_types().empty()
	  && class_types().empty()
	  && union_types().empty()
	  && enum_types().empty()
	  && typedef_types().empty()
	  && qualified_types().empty()
	  && pointer_types().empty()
	  && reference_types().empty()
	  && array_types().empty()
	  && subrange_types().empty()
	  && function_types().empty());
}

/// Getter for the map that associates the name of a basic type to the
/// vector instances of type_decl_sptr that represents that type.
const istring_type_base_wptrs_map_type&
type_maps::basic_types() const
{return priv_->basic_types_;}

/// Getter for the map that associates the name of a basic type to the
/// vector of instances of @ref type_decl_sptr that represents that
/// type.
istring_type_base_wptrs_map_type&
type_maps::basic_types()
{return priv_->basic_types_;}

/// Getter for the map that associates the name of a class type to the
/// vector of instances of @ref class_decl_sptr that represents that
/// type.
const istring_type_base_wptrs_map_type&
type_maps::class_types() const
{return priv_->class_types_;}

/// Getter for the map that associates the name of a class type to the
/// vector of instances of @ref class_decl_sptr that represents that
/// type.
istring_type_base_wptrs_map_type&
type_maps::class_types()
{return priv_->class_types_;}

/// Getter for the map that associates the name of a union type to the
/// vector of instances of @ref union_decl_sptr that represents that
/// type.
istring_type_base_wptrs_map_type&
type_maps::union_types()
{return priv_->union_types_;}

/// Getter for the map that associates the name of a union type to the
/// vector of instances of @ref union_decl_sptr that represents that
/// type.
const istring_type_base_wptrs_map_type&
type_maps::union_types() const
{return priv_->union_types_;}

/// Getter for the map that associates the name of an enum type to the
/// vector of instances of @ref enum_type_decl_sptr that represents
/// that type.
istring_type_base_wptrs_map_type&
type_maps::enum_types()
{return priv_->enum_types_;}

/// Getter for the map that associates the name of an enum type to the
/// vector of instances of @ref enum_type_decl_sptr that represents
/// that type.
const istring_type_base_wptrs_map_type&
type_maps::enum_types() const
{return priv_->enum_types_;}

/// Getter for the map that associates the name of a typedef to the
/// vector of instances of @ref typedef_decl_sptr that represents tha
/// type.
istring_type_base_wptrs_map_type&
type_maps::typedef_types()
{return priv_->typedef_types_;}

/// Getter for the map that associates the name of a typedef to the
/// vector of instances of @ref typedef_decl_sptr that represents tha
/// type.
const istring_type_base_wptrs_map_type&
type_maps::typedef_types() const
{return priv_->typedef_types_;}

/// Getter for the map that associates the name of a qualified type to
/// the vector of instances of @ref qualified_type_def_sptr.
istring_type_base_wptrs_map_type&
type_maps::qualified_types()
{return priv_->qualified_types_;}

/// Getter for the map that associates the name of a qualified type to
/// the vector of instances of @ref qualified_type_def_sptr.
const istring_type_base_wptrs_map_type&
type_maps::qualified_types() const
{return priv_->qualified_types_;}

/// Getter for the map that associates the name of a pointer type to
/// the vector of instances of @ref pointer_type_def_sptr that
/// represents that type.
istring_type_base_wptrs_map_type&
type_maps::pointer_types()
{return priv_->pointer_types_;}

/// Getter for the map that associates the name of a pointer type to
/// the vector of instances of @ref pointer_type_def_sptr that
/// represents that type.
const istring_type_base_wptrs_map_type&
type_maps::pointer_types() const
{return priv_->pointer_types_;}

/// Getter for the map that associates the name of a reference type to
/// the vector of instances of @ref reference_type_def_sptr that
/// represents that type.
istring_type_base_wptrs_map_type&
type_maps::reference_types()
{return priv_->reference_types_;}

/// Getter for the map that associates the name of a reference type to
/// the vector of instances of @ref reference_type_def_sptr that
/// represents that type.
const istring_type_base_wptrs_map_type&
type_maps::reference_types() const
{return priv_->reference_types_;}

/// Getter for the map that associates the name of an array type to
/// the vector of instances of @ref array_type_def_sptr that
/// represents that type.
istring_type_base_wptrs_map_type&
type_maps::array_types()
{return priv_->array_types_;}

/// Getter for the map that associates the name of an array type to
/// the vector of instances of @ref array_type_def_sptr that
/// represents that type.
const istring_type_base_wptrs_map_type&
type_maps::array_types() const
{return priv_->array_types_;}

/// Getter for the map that associates the name of a subrange type to
/// the vector of instances of @ref array_type_def::subrange_sptr that
/// represents that type.
istring_type_base_wptrs_map_type&
type_maps::subrange_types()
{return priv_->subrange_types_;}

/// Getter for the map that associates the name of a subrange type to
/// the vector of instances of @ref array_type_def::subrange_sptr that
/// represents that type.
const istring_type_base_wptrs_map_type&
type_maps::subrange_types() const
{return priv_->subrange_types_;}

/// Getter for the map that associates the name of a function type to
/// the vector of instances of @ref function_type_sptr that represents
/// that type.
const istring_type_base_wptrs_map_type&
type_maps::function_types() const
{return priv_->function_types_;}

/// Getter for the map that associates the name of a function type to
/// the vector of instances of @ref function_type_sptr that represents
/// that type.
istring_type_base_wptrs_map_type&
type_maps::function_types()
{return priv_->function_types_;}

/// A comparison functor to compare/sort types based on their pretty
/// representations.
struct type_name_comp
{
  /// Comparison operator for two instances of @ref type_base.
  ///
  /// This compares the two types by lexicographically comparing their
  /// pretty representation.
  ///
  /// @param l the left-most type to compare.
  ///
  /// @param r the right-most type to compare.
  ///
  /// @return true iff @p l < @p r.
  bool
  operator()(type_base *l, type_base *r) const
  {
    if (l == 0 && r == 0)
      return false;

    string l_repr = get_pretty_representation(l);
    string r_repr = get_pretty_representation(r);
    return l_repr < r_repr;
  }

  /// Comparison operator for two instances of @ref type_base.
  ///
  /// This compares the two types by lexicographically comparing their
  /// pretty representation.
  ///
  /// @param l the left-most type to compare.
  ///
  /// @param r the right-most type to compare.
  ///
  /// @return true iff @p l < @p r.
  bool
  operator()(const type_base_sptr &l, const type_base_sptr &r) const
  {return operator()(l.get(), r.get());}

  /// Comparison operator for two instances of @ref type_base.
  ///
  /// This compares the two types by lexicographically comparing their
  /// pretty representation.
  ///
  /// @param l the left-most type to compare.
  ///
  /// @param r the right-most type to compare.
  ///
  /// @return true iff @p l < @p r.
  bool
  operator()(const type_base_wptr &l, const type_base_wptr &r) const
  {return operator()(type_base_sptr(l), type_base_sptr(r));}
}; // end struct type_name_comp

/// Compare two types by comparing their canonical types if present.
///
/// If the canonical types are not present (because the types have not
/// yet been canonicalized, for instance) then the types are compared
/// structurally.
///
/// @param l the first type to take into account in the comparison.
///
/// @param r the second type to take into account in the comparison.
template<typename T>
bool
try_canonical_compare(const T *l, const T *r)
{
  if (const type_base *lc = l->get_naked_canonical_type())
    if (const type_base *rc = r->get_naked_canonical_type())
      return lc == rc;
  return equals(*l, *r, 0);
}

/// Getter of all types types sorted by their pretty representation.
///
/// @return a sorted vector of all types sorted by their pretty
/// representation.
const vector<type_base_wptr>&
type_maps::get_types_sorted_by_name() const
{
  if (priv_->sorted_types_.empty())
    {
      istring_type_base_wptrs_map_type::const_iterator i;
      vector<type_base_wptr>::const_iterator j;

      for (i = basic_types().begin(); i != basic_types().end(); ++i)
	for (j = i->second.begin(); j != i->second.end(); ++j)
	  priv_->sorted_types_.push_back(*j);

      for (i = class_types().begin(); i != class_types().end(); ++i)
	for (j = i->second.begin(); j != i->second.end(); ++j)
	  priv_->sorted_types_.push_back(*j);

      for (i = union_types().begin(); i != union_types().end(); ++i)
	for (j = i->second.begin(); j != i->second.end(); ++j)
	  priv_->sorted_types_.push_back(*j);

      for (i = enum_types().begin(); i != enum_types().end(); ++i)
	for (j = i->second.begin(); j != i->second.end(); ++j)
	  priv_->sorted_types_.push_back(*j);

      for (i = typedef_types().begin(); i != typedef_types().end(); ++i)
	for (j = i->second.begin(); j != i->second.end(); ++j)
	  priv_->sorted_types_.push_back(*j);

      type_name_comp comp;
      sort(priv_->sorted_types_.begin(), priv_->sorted_types_.end(), comp);
    }

  return priv_->sorted_types_;
}

// </type_maps stuff>

// <translation_unit stuff>

/// Constructor of translation_unit.
///
/// @param env the environment of this translation unit.  Please note
/// that the life time of the environment must be greater than the
/// life time of the translation unit because the translation uses
/// resources that are allocated in the environment.
///
/// @param path the location of the translation unit.
///
/// @param address_size the size of addresses in the translation unit,
/// in bits.
translation_unit::translation_unit(const environment*	env,
				   const std::string&	path,
				   char		address_size)
  : priv_(new priv(env))
{
  priv_->path_ = path;
  priv_->address_size_ = address_size;
}

/// Getter of the the global scope of the translation unit.
///
/// @return the global scope of the current translation unit.  If
/// there is not global scope allocated yet, this function creates one
/// and returns it.
const scope_decl_sptr&
translation_unit::get_global_scope() const
{
  return const_cast<translation_unit*>(this)->get_global_scope();
}

/// Getter of the the global scope of the translation unit.
///
/// @return the global scope of the current translation unit.  If
/// there is not global scope allocated yet, this function creates one
/// and returns it.
scope_decl_sptr&
translation_unit::get_global_scope()
{
  if (!priv_->global_scope_)
    {
      priv_->global_scope_.reset
	(new global_scope(const_cast<translation_unit*>(this)));
      // The global scope must be out of the same environment as its
      // translation unit.
      priv_->global_scope_->
	set_environment(const_cast<environment*>(get_environment()));
      priv_->global_scope_->set_translation_unit
	(const_cast<translation_unit*>(this));
    }
  return priv_->global_scope_;
}

/// Getter of the types of the current @ref translation_unit.
///
/// @return the maps of the types of the translation unit.
const type_maps&
translation_unit::get_types() const
{return priv_->types_;}

/// Getter of the types of the current @ref translation_unit.
///
/// @return the maps of the types of the translation unit.
type_maps&
translation_unit::get_types()
{return priv_->types_;}

/// Get the vector of function types that are used in the current
/// translation unit.
///
/// @return the vector of function types that are used in the current
/// translation unit.
const vector<function_type_sptr>&
translation_unit::get_live_fn_types() const
{return priv_->live_fn_types_;}

/// Getter of the environment of the current @ref translation_unit.
///
/// @return the translation unit of the current translation unit.
const environment*
translation_unit::get_environment() const
{return priv_->env_;}

/// Getter of the environment of the current @ref translation_unit.
///
/// @return the translation unit of the current translation unit.
environment*
translation_unit::get_environment()
{return const_cast<environment*>(priv_->env_);}

/// Setter of the environment of the current @ref translation_unit.
///
/// @param env the environment.
void
translation_unit::set_environment(const environment* env)
{priv_->env_ = env;}

/// Getter of the language of the source code of the translation unit.
///
/// @return the language of the source code.
translation_unit::language
translation_unit::get_language() const
{return priv_->language_;}

/// Setter of the language of the source code of the translation unit.
///
/// @param l the new language.
void
translation_unit::set_language(language l)
{priv_->language_ = l;}


/// Get the path of the current translation unit.
///
/// This path is relative to the build directory of the translation
/// unit as returned by translation_unit::get_compilation_dir_path.
///
/// @return the relative path of the compilation unit associated to
/// the current instance of translation_unit.
//
const std::string&
translation_unit::get_path() const
{return priv_->path_;}

/// Set the path associated to the current instance of
/// translation_unit.
///
/// This path is relative to the build directory of the translation
/// unit as returned by translation_unit::get_compilation_dir_path.
///
/// @param a_path the new relative path to set.
void
translation_unit::set_path(const string& a_path)
{priv_->path_ = a_path;}


/// Get the path of the directory that was 'current' when the
/// translation unit was compiled.
///
/// Note that the path returned by translation_unit::get_path is
/// relative to the path returned by this function.
///
/// @return the compilation directory for the current translation
/// unit.
const std::string&
translation_unit::get_compilation_dir_path() const
{return priv_->comp_dir_path_;}

/// Set the path of the directory that was 'current' when the
/// translation unit was compiled.
///
/// Note that the path returned by translation_unit::get_path is
/// relative to the path returned by this function.
///
/// @param the compilation directory for the current translation unit.
void
translation_unit::set_compilation_dir_path(const std::string& d)
{priv_->comp_dir_path_ = d;}

/// Get the concatenation of the build directory and the relative path
/// of the translation unit.
///
/// @return the absolute path of the translation unit.
const std::string&
translation_unit::get_absolute_path() const
{
  if (priv_->abs_path_.empty())
    {
      string path;
      if (!priv_->path_.empty())
	{
	  if (!priv_->comp_dir_path_.empty())
	    {
	      path = priv_->comp_dir_path_;
	      path += "/";
	    }
	  path += priv_->path_;
	}
      priv_->abs_path_ = path;
    }

  return priv_->abs_path_;
}

/// Set the corpus this translation unit is a member of.
///
/// Note that adding a translation unit to a @ref corpus automatically
/// triggers a call to this member function.
///
/// @param corpus the corpus.
void
translation_unit::set_corpus(corpus* c)
{priv_->corp = c;}

/// Get the corpus this translation unit is a member of.
///
/// @return the parent corpus, or nil if this doesn't belong to any
/// corpus yet.
corpus*
translation_unit::get_corpus()
{return priv_->corp;}

/// Get the corpus this translation unit is a member of.
///
/// @return the parent corpus, or nil if this doesn't belong to any
/// corpus yet.
const corpus*
translation_unit::get_corpus() const
{return const_cast<translation_unit*>(this)->get_corpus();}

/// Getter of the location manager for the current translation unit.
///
/// @return a reference to the location manager for the current
/// translation unit.
location_manager&
translation_unit::get_loc_mgr()
{return priv_->loc_mgr_;}

/// const Getter of the location manager.
///
/// @return a const reference to the location manager for the current
/// translation unit.
const location_manager&
translation_unit::get_loc_mgr() const
{return priv_->loc_mgr_;}

/// Tests whether if the current translation unit contains ABI
/// artifacts or not.
///
/// @return true iff the current translation unit is empty.
bool
translation_unit::is_empty() const
{return get_global_scope()->is_empty();}

/// Getter of the address size in this translation unit.
///
/// @return the address size, in bits.
char
translation_unit::get_address_size() const
{return priv_->address_size_;}

/// Setter of the address size in this translation unit.
///
/// @param a the new address size in bits.
void
translation_unit::set_address_size(char a)
{priv_->address_size_= a;}

/// Getter of the 'is_constructed" flag.  It says if the translation
/// unit is fully constructed or not.
///
/// This flag is important for cases when comparison might depend on
/// if the translation unit is fully built or not.  For instance, when
/// reading types from DWARF, the virtual methods of a class are not
/// necessarily fully constructed until we have reached the end of the
/// translation unit.  In that case, before we've reached the end of
/// the translation unit, we might not take virtual functions into
/// account when comparing classes.
///
/// @return true if the translation unit is constructed.
bool
translation_unit::is_constructed() const
{return priv_->is_constructed_;}

/// Setter of the 'is_constructed" flag.  It says if the translation
/// unit is fully constructed or not.
///
/// This flag is important for cases when comparison might depend on
/// if the translation unit is fully built or not.  For instance, when
/// reading types from DWARF, the virtual methods of a class are not
/// necessarily fully constructed until we have reached the end of the
/// translation unit.  In that case, before we've reached the end of
/// the translation unit, we might not take virtual functions into
/// account when comparing classes.
///
/// @param f true if the translation unit is constructed.
void
translation_unit::set_is_constructed(bool f)
{priv_->is_constructed_ = f;}

/// Compare the current translation unit against another one.
///
/// @param other the other tu to compare against.
///
/// @return true if the two translation units are equal, false
/// otherwise.
bool
translation_unit::operator==(const translation_unit& other)const
{
  if (get_address_size() != other.get_address_size())
    return false;

  return *get_global_scope() == *other.get_global_scope();
}

/// Inequality operator.
///
/// @param o the instance of @ref translation_unit to compare the
/// current instance against.
///
/// @return true iff the current instance is different from @p o.
bool
translation_unit::operator!=(const translation_unit& o) const
{return ! operator==(o);}

/// Ensure that the life time of a function type is bound to the life
/// time of the current translation unit.
///
/// @param ftype the function time which life time to bind to the life
/// time of the current instance of @ref translation_unit.  That is,
/// it's onlyh when the translation unit is destroyed that the
/// function type can be destroyed to.
void
translation_unit::bind_function_type_life_time(function_type_sptr ftype) const
{
  const environment* env = get_environment();
  ABG_ASSERT(env);

  const_cast<translation_unit*>(this)->priv_->live_fn_types_.push_back(ftype);

  interned_string repr = get_type_name(ftype);
  const_cast<translation_unit*>(this)->get_types().function_types()[repr].
    push_back(ftype);

  // The function type must be out of the same environment as its
  // translation unit.
  if (const environment* e = ftype->get_environment())
    ABG_ASSERT(env == e);
  ftype->set_environment(const_cast<environment*>(env));

  if (const translation_unit* existing_tu = ftype->get_translation_unit())
    ABG_ASSERT(existing_tu == this);
  else
    ftype->set_translation_unit(const_cast<translation_unit*>(this));
}

/// This implements the ir_traversable_base::traverse virtual
/// function.
///
/// @param v the visitor used on the member nodes of the translation
/// unit during the traversal.
///
/// @return true if the entire type IR tree got traversed, false
/// otherwise.
bool
translation_unit::traverse(ir_node_visitor& v)
{return get_global_scope()->traverse(v);}

translation_unit::~translation_unit()
{}

/// Converts a translation_unit::language enumerator into a string.
///
/// @param l the language enumerator to translate.
///
/// @return the resulting string.
string
translation_unit_language_to_string(translation_unit::language l)
{
  switch (l)
    {
    case translation_unit::LANG_UNKNOWN:
      return "LANG_UNKNOWN";
    case translation_unit::LANG_Cobol74:
      return "LANG_Cobol74";
    case translation_unit::LANG_Cobol85:
      return "LANG_Cobol85";
    case translation_unit::LANG_C89:
      return "LANG_C89";
    case translation_unit::LANG_C99:
      return "LANG_C99";
    case translation_unit::LANG_C11:
      return "LANG_C11";
    case translation_unit::LANG_C:
      return "LANG_C";
    case translation_unit::LANG_C_plus_plus_11:
      return "LANG_C_plus_plus_11";
    case translation_unit::LANG_C_plus_plus_14:
      return "LANG_C_plus_plus_14";
    case translation_unit::LANG_C_plus_plus:
      return "LANG_C_plus_plus";
    case translation_unit::LANG_ObjC:
      return "LANG_ObjC";
    case translation_unit::LANG_ObjC_plus_plus:
      return "LANG_ObjC_plus_plus";
    case translation_unit::LANG_Fortran77:
      return "LANG_Fortran77";
    case translation_unit::LANG_Fortran90:
      return "LANG_Fortran90";
    case translation_unit::LANG_Fortran95:
      return "LANG_Fortran95";
    case translation_unit::LANG_Ada83:
      return "LANG_Ada83";
    case translation_unit::LANG_Ada95:
      return "LANG_Ada95";
    case translation_unit::LANG_Pascal83:
      return "LANG_Pascal83";
    case translation_unit::LANG_Modula2:
      return "LANG_Modula2";
    case translation_unit::LANG_Java:
      return "LANG_Java";
    case translation_unit::LANG_PL1:
      return "LANG_PL1";
    case translation_unit::LANG_UPC:
      return "LANG_UPC";
    case translation_unit::LANG_D:
      return "LANG_D";
    case translation_unit::LANG_Python:
      return "LANG_Python";
    case translation_unit::LANG_Go:
      return "LANG_Go";
    case translation_unit::LANG_Mips_Assembler:
      return "LANG_Mips_Assembler";
    default:
      return "LANG_UNKNOWN";
    }

  return "LANG_UNKNOWN";
}

/// Parse a string representing a language into a
/// translation_unit::language enumerator into a string.
///
/// @param l the string representing the language.
///
/// @return the resulting translation_unit::language enumerator.
translation_unit::language
string_to_translation_unit_language(const string& l)
{
  if (l == "LANG_Cobol74")
    return translation_unit::LANG_Cobol74;
  else if (l == "LANG_Cobol85")
    return translation_unit::LANG_Cobol85;
  else if (l == "LANG_C89")
    return translation_unit::LANG_C89;
  else if (l == "LANG_C99")
    return translation_unit::LANG_C99;
  else if (l == "LANG_C11")
    return translation_unit::LANG_C11;
  else if (l == "LANG_C")
    return translation_unit::LANG_C;
  else if (l == "LANG_C_plus_plus_11")
    return translation_unit::LANG_C_plus_plus_11;
  else if (l == "LANG_C_plus_plus_14")
    return translation_unit::LANG_C_plus_plus_14;
  else if (l == "LANG_C_plus_plus")
    return translation_unit::LANG_C_plus_plus;
  else if (l == "LANG_ObjC")
    return translation_unit::LANG_ObjC;
  else if (l == "LANG_ObjC_plus_plus")
    return translation_unit::LANG_ObjC_plus_plus;
  else if (l == "LANG_Fortran77")
    return translation_unit::LANG_Fortran77;
  else if (l == "LANG_Fortran90")
    return translation_unit::LANG_Fortran90;
    else if (l == "LANG_Fortran95")
    return translation_unit::LANG_Fortran95;
  else if (l == "LANG_Ada83")
    return translation_unit::LANG_Ada83;
  else if (l == "LANG_Ada95")
    return translation_unit::LANG_Ada95;
  else if (l == "LANG_Pascal83")
    return translation_unit::LANG_Pascal83;
  else if (l == "LANG_Modula2")
    return translation_unit::LANG_Modula2;
  else if (l == "LANG_Java")
    return translation_unit::LANG_Java;
  else if (l == "LANG_PL1")
    return translation_unit::LANG_PL1;
  else if (l == "LANG_UPC")
    return translation_unit::LANG_UPC;
  else if (l == "LANG_D")
    return translation_unit::LANG_D;
  else if (l == "LANG_Python")
    return translation_unit::LANG_Python;
  else if (l == "LANG_Go")
    return translation_unit::LANG_Go;
  else if (l == "LANG_Mips_Assembler")
    return translation_unit::LANG_Mips_Assembler;

  return translation_unit::LANG_UNKNOWN;
}

/// Test if a language enumerator designates the C language.
///
/// @param l the language enumerator to consider.
///
/// @return true iff @p l designates the C language.
bool
is_c_language(translation_unit::language l)
{
  return (l == translation_unit::LANG_C89
	  || l == translation_unit::LANG_C99
	  || l == translation_unit::LANG_C11
	  || l == translation_unit::LANG_C);
}

/// Test if a language enumerator designates the C++ language.
///
/// @param l the language enumerator to consider.
///
/// @return true iff @p l designates the C++ language.
bool
is_cplus_plus_language(translation_unit::language l)
{
  return (l == translation_unit::LANG_C_plus_plus_03
	  || l == translation_unit::LANG_C_plus_plus_11
	  || l == translation_unit::LANG_C_plus_plus_14
	  || l == translation_unit::LANG_C_plus_plus);
}

/// Test if a language enumerator designates the Java language.
///
/// @param l the language enumerator to consider.
///
/// @return true iff @p l designates the Java language.
bool
is_java_language(translation_unit::language l)
{return l == translation_unit::LANG_Java;}

/// Test if a language enumerator designates the Ada language.
///
/// @param l the language enumerator to consider.
///
/// @return true iff @p l designates the Ada language.
bool
is_ada_language(translation_unit::language l)
{
  return (l == translation_unit::LANG_Ada83
	 || l == translation_unit::LANG_Ada95);
}

/// A deep comparison operator for pointers to translation units.
///
/// @param l the first translation unit to consider for the comparison.
///
/// @param r the second translation unit to consider for the comparison.
///
/// @return true if the two translation units are equal, false otherwise.
bool
operator==(const translation_unit_sptr& l, const translation_unit_sptr& r)
{
  if (l.get() == r.get())
    return true;

  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// A deep inequality operator for pointers to translation units.
///
/// @param l the first translation unit to consider for the comparison.
///
/// @param r the second translation unit to consider for the comparison.
///
/// @return true iff the two translation units are different.
bool
operator!=(const translation_unit_sptr& l, const translation_unit_sptr& r)
{return !operator==(l, r);}

// </translation_unit stuff>

// <elf_symbol stuff>
struct elf_symbol::priv
{
  const environment*	env_;
  size_t		index_;
  size_t		size_;
  string		name_;
  elf_symbol::type	type_;
  elf_symbol::binding	binding_;
  elf_symbol::version	version_;
  elf_symbol::visibility visibility_;
  bool			is_defined_;
  // This flag below says if the symbol is a common elf symbol.  In
  // relocatable files, a common symbol is a symbol defined in a
  // section of kind SHN_COMMON.
  //
  // Note that a symbol of kind STT_COMMON is also considered a common
  // symbol.  Here is what the gABI says about STT_COMMON and
  // SHN_COMMON:
  //
  //     Symbols with type STT_COMMON label uninitialized common
  //     blocks. In relocatable objects, these symbols are not
  //     allocated and must have the special section index SHN_COMMON
  //     (see below). In shared objects and executables these symbols
  //     must be allocated to some section in the defining object.
  //
  //     In relocatable objects, symbols with type STT_COMMON are
  //     treated just as other symbols with index SHN_COMMON. If the
  //     link-editor allocates space for the SHN_COMMON symbol in an
  //     output section of the object it is producing, it must
  //     preserve the type of the output symbol as STT_COMMON.
  //
  //     When the dynamic linker encounters a reference to a symbol
  //     that resolves to a definition of type STT_COMMON, it may (but
  //     is not required to) change its symbol resolution rules as
  //     follows: instead of binding the reference to the first symbol
  //     found with the given name, the dynamic linker searches for
  //     the first symbol with that name with type other than
  //     STT_COMMON. If no such symbol is found, it looks for the
  //     STT_COMMON definition of that name that has the largest size.
  bool			is_common_;
  bool			is_linux_string_cst_;
  elf_symbol_wptr	main_symbol_;
  elf_symbol_wptr	next_alias_;
  elf_symbol_wptr	next_common_instance_;
  string		id_string_;

  priv()
    : env_(),
      index_(),
      size_(),
      type_(elf_symbol::NOTYPE_TYPE),
      binding_(elf_symbol::GLOBAL_BINDING),
      visibility_(elf_symbol::DEFAULT_VISIBILITY),
      is_defined_(false),
      is_common_(false),
      is_linux_string_cst_(false)
  {}

  priv(const environment*		e,
       size_t				i,
       size_t				s,
       const string&			n,
       elf_symbol::type		t,
       elf_symbol::binding		b,
       bool				d,
       bool				c,
       const elf_symbol::version&	ve,
       elf_symbol::visibility		vi,
       bool				is_linux_string_cst)
    : env_(e),
      index_(i),
      size_(s),
      name_(n),
      type_(t),
      binding_(b),
      version_(ve),
      visibility_(vi),
      is_defined_(d),
      is_common_(c),
      is_linux_string_cst_(is_linux_string_cst)
  {
    if (!is_common_)
      is_common_ = type_ == COMMON_TYPE;
  }
}; // end struct elf_symbol::priv

/// Default constructor of the @ref elf_symbol type.
///
/// Note that this constructor is private, so client code cannot use
/// it to create instances of @ref elf_symbol.  Rather, client code
/// should use the @ref elf_symbol::create() function to create
/// instances of @ref elf_symbol instead.
elf_symbol::elf_symbol()
  : priv_(new priv)
{}

/// Constructor of the @ref elf_symbol type.
///
/// Note that this constructor is private, so client code cannot use
/// it to create instances of @ref elf_symbol.  Rather, client code
/// should use the @ref elf_symbol::create() function to create
/// instances of @ref elf_symbol instead.
///
/// @param e the environment we are operating from.
///
/// @param i the index of the symbol in the (ELF) symbol table.
///
/// @param s the size of the symbol.
///
/// @param n the name of the symbol.
///
/// @param t the type of the symbol.
///
/// @param b the binding of the symbol.
///
/// @param d true if the symbol is defined, false otherwise.
///
/// @param c true if the symbol is a common symbol, false otherwise.
///
/// @param ve the version of the symbol.
///
/// @param vi the visibility of the symbol.
///
/// @param is_linux_string_cst true if the symbol is a Linux Kernel
/// string constant defined in the __ksymtab_strings section.
elf_symbol::elf_symbol(const environment*	e,
		       size_t			i,
		       size_t			s,
		       const string&		n,
		       type			t,
		       binding			b,
		       bool			d,
		       bool			c,
		       const version&		ve,
		       visibility		vi,
		       bool			is_linux_string_cst)
  : priv_(new priv(e, i, s, n, t, b, d,
		   c, ve, vi, is_linux_string_cst))
{}

/// Factory of instances of @ref elf_symbol.
///
/// This is the function to use to create instances of @ref elf_symbol.
///
/// @return a (smart) pointer to a newly created instance of @ref
/// elf_symbol.
elf_symbol_sptr
elf_symbol::create()
{
  elf_symbol_sptr e(new elf_symbol());
  e->priv_->main_symbol_ = e;
  return e;
}

/// Factory of instances of @ref elf_symbol.
///
/// This is the function to use to create instances of @ref elf_symbol.
///
/// @param e the environment we are operating from.
///
/// @param i the index of the symbol in the (ELF) symbol table.
///
/// @param s the size of the symbol.
///
/// @param n the name of the symbol.
///
/// @param t the type of the symbol.
///
/// @param b the binding of the symbol.
///
/// @param d true if the symbol is defined, false otherwise.
///
/// @param c true if the symbol is a common symbol.
///
/// @param ve the version of the symbol.
///
/// @param vi the visibility of the symbol.
///
/// @param is_linux_string_cst if true, it means the symbol represents
/// a string constant from a linux kernel binary.
///
/// @return a (smart) pointer to a newly created instance of @ref
/// elf_symbol.
elf_symbol_sptr
elf_symbol::create(const environment*	e,
		   size_t		i,
		   size_t		s,
		   const string&	n,
		   type		t,
		   binding		b,
		   bool		d,
		   bool		c,
		   const version&	ve,
		   visibility		vi,
		   bool		is_linux_string_cst)
{
  elf_symbol_sptr sym(new elf_symbol(e, i, s, n, t, b, d, c, ve,
				     vi, is_linux_string_cst));
  sym->priv_->main_symbol_ = sym;
  return sym;
}

/// Test textual equality between two symbols.
///
/// Textual equality means that the aliases of the compared symbols
/// are not taken into account.  Only the name, type, and version of
/// the symbols are compared.
///
/// @return true iff the two symbols are textually equal.
static bool
textually_equals(const elf_symbol&l,
		 const elf_symbol&r)
{
  bool equals = (l.get_name() == r.get_name()
		 && l.get_type() == r.get_type()
		 && l.is_public() == r.is_public()
		 && l.is_defined() == r.is_defined()
		 && l.is_common_symbol() == r.is_common_symbol()
		 && l.get_version() == r.get_version());

  if (equals && l.is_variable())
    // These are variable symbols.  Let's compare their symbol size.
    // The symbol size in this case is the size taken by the storage
    // of the variable.  If that size changes, then it's an ABI
    // change.
    equals = l.get_size() == r.get_size();

  return equals;
}

/// Getter of the environment used by the current instance of @ref
/// elf_symbol.
///
/// @return the enviroment used by the current instance of @ref elf_symbol.
const environment*
elf_symbol::get_environment() const
{return priv_->env_;}

/// Setter of the environment used by the current instance of @ref
/// elf_symbol.
///
/// @param The new enviroment used by the current instance of @ref
/// elf_symbol.
void
elf_symbol::set_environment(const environment* e) const
{priv_->env_ = e;}

/// Getter for the index
///
/// @return the index of the symbol.
size_t
elf_symbol::get_index() const
{return priv_->index_;}

/// Setter for the index.
///
/// @param s the new index.
void
elf_symbol::set_index(size_t s)
{priv_->index_ = s;}

/// Test if the ELF symbol is for a string constant of a Linux binary
/// defined in the __ksymtab_strings symbol table.
///
/// @return true iff ELF symbol is for a string constant of a Linux
/// binary defined in the __ksymtab_strings symbol table.
bool
elf_symbol::get_is_linux_string_cst() const
{return priv_->is_linux_string_cst_;}

/// Getter for the name of the @ref elf_symbol.
///
/// @return a reference to the name of the @ref symbol.
const string&
elf_symbol::get_name() const
{return priv_->name_;}

/// Setter for the name of the current intance of @ref elf_symbol.
///
/// @param n the new name.
void
elf_symbol::set_name(const string& n)
{
  priv_->name_ = n;
  priv_->id_string_.clear();
}

/// Getter for the type of the current instance of @ref elf_symbol.
///
/// @return the type of the elf symbol.
elf_symbol::type
elf_symbol::get_type() const
{return priv_->type_;}

/// Setter for the type of the current instance of @ref elf_symbol.
///
/// @param t the new symbol type.
void
elf_symbol::set_type(type t)
{priv_->type_ = t;}

/// Getter of the size of the symbol.
///
/// @return the size of the symbol, in bytes.
size_t
elf_symbol::get_size() const
{return priv_->size_;}

/// Setter of the size of the symbol.
///
/// @param size the new size of the symbol, in bytes.
void
elf_symbol::set_size(size_t size)
{priv_->size_ = size;}

/// Getter for the binding of the current instance of @ref elf_symbol.
///
/// @return the binding of the symbol.
elf_symbol::binding
elf_symbol::get_binding() const
{return priv_->binding_;}

/// Setter for the binding of the current instance of @ref elf_symbol.
///
/// @param b the new binding.
void
elf_symbol::set_binding(binding b)
{priv_->binding_ = b;}

/// Getter for the version of the current instanc of @ref elf_symbol.
///
/// @return the version of the elf symbol.
elf_symbol::version&
elf_symbol::get_version() const
{return priv_->version_;}

/// Setter for the version of the current instance of @ref elf_symbol.
///
/// @param v the new version of the elf symbol.
void
elf_symbol::set_version(const version& v)
{
  priv_->version_ = v;
  priv_->id_string_.clear();
}

/// Setter of the visibility of the current instance of @ref
/// elf_symbol.
///
/// @param v the new visibility of the elf symbol.
void
elf_symbol::set_visibility(visibility v)
{priv_->visibility_ = v;}

/// Getter of the visibility of the current instance of @ref
/// elf_symbol.
///
/// @return the visibility of the elf symbol.
elf_symbol::visibility
elf_symbol::get_visibility() const
{return priv_->visibility_;}

/// Test if the current instance of @ref elf_symbol is defined or not.
///
/// @return true if the current instance of @ref elf_symbol is
/// defined, false otherwise.
bool
elf_symbol::is_defined() const
{return priv_->is_defined_;}

/// Sets a flag saying if the current instance of @ref elf_symbol is
/// defined
///
/// @param b the new value of the flag.
void
elf_symbol::is_defined(bool d)
{priv_->is_defined_ = d;}

/// Test if the current instance of @ref elf_symbol is public or not.
///
/// This tests if the symbol is defined, has default or protected
///visibility, and either:
///		- has global binding
///		- has weak binding
///		- or has a GNU_UNIQUE binding.
///
/// return true if the current instance of @ref elf_symbol is public,
/// false otherwise.
bool
elf_symbol::is_public() const
{
  return (is_defined()
	  && (get_binding() == GLOBAL_BINDING
	      || get_binding() == WEAK_BINDING
	      || get_binding() == GNU_UNIQUE_BINDING)
	  && (get_visibility() == DEFAULT_VISIBILITY
	      || get_visibility() == PROTECTED_VISIBILITY));
}

/// Test if the current instance of @ref elf_symbol is a function
/// symbol or not.
///
/// @return true if the current instance of @ref elf_symbol is a
/// function symbol, false otherwise.
bool
elf_symbol::is_function() const
{return get_type() == FUNC_TYPE || get_type() == GNU_IFUNC_TYPE;}

/// Test if the current instance of @ref elf_symbol is a variable
/// symbol or not.
///
/// @return true if the current instance of @ref elf_symbol is a
/// variable symbol, false otherwise.
bool
elf_symbol::is_variable() const
{return get_type() == OBJECT_TYPE || get_type() == TLS_TYPE;}

/// @name Elf symbol aliases
///
/// An alias A for an elf symbol S is a symbol that is defined at the
/// same address as S.  S is chained to A through the
/// elf_symbol::get_next_alias() method.
///
/// When there are several aliases to a symbol, the main symbol is the
/// the first symbol found in the symbol table for a given address.
///
/// The alias chain is circular.  That means if S is the main symbol
/// and A is the alias, S is chained to A and A
/// is chained back to the main symbol S.  The last alias in an alias
///chain is always chained to the main symbol.
///
/// Thus, when looping over the aliases of an elf_symbol A, detecting
/// an alias that is equal to the main symbol should logically be a
/// loop exit condition.
///
/// Accessing and adding aliases for instances of elf_symbol is done
/// through the member functions below.

/// @{

/// Get the main symbol of an alias chain.
///
///@return the main symbol.
const elf_symbol_sptr
elf_symbol::get_main_symbol() const
{return elf_symbol_sptr(priv_->main_symbol_);}

/// Get the main symbol of an alias chain.
///
///@return the main symbol.
elf_symbol_sptr
elf_symbol::get_main_symbol()
{return elf_symbol_sptr(priv_->main_symbol_);}

/// Tests whether this symbol is the main symbol.
///
/// @return true iff this symbol is the main symbol.
bool
elf_symbol::is_main_symbol() const
{return get_main_symbol().get() == this;}

/// Get the next alias of the current symbol.
///
///@return the alias, or NULL if there is no alias.
elf_symbol_sptr
elf_symbol::get_next_alias() const
{return priv_->next_alias_.lock();}


/// Check if the current elf_symbol has an alias.
///
///@return true iff the current elf_symbol has an alias.
bool
elf_symbol::has_aliases() const
{return bool(get_next_alias());}

/// Get the number of aliases to this elf symbol
///
/// @return the number of aliases to this elf symbol.
int
elf_symbol::get_number_of_aliases() const
{
  int result = 0;

  for (elf_symbol_sptr a = get_next_alias();
       a && a.get() != get_main_symbol().get();
       a = a->get_next_alias())
    ++result;

  return result;
}

/// Add an alias to the current elf symbol.
///
/// @param alias the new alias.  Note that this elf_symbol should *NOT*
/// have aliases prior to the invocation of this function.
void
elf_symbol::add_alias(const elf_symbol_sptr& alias)
{
  if (!alias)
    return;

  ABG_ASSERT(!alias->has_aliases());
  ABG_ASSERT(is_main_symbol());

  if (has_aliases())
    {
      elf_symbol_sptr last_alias;
      for (elf_symbol_sptr a = get_next_alias();
	   a && !a->is_main_symbol();
	   a = a->get_next_alias())
	{
	  if (a->get_next_alias()->is_main_symbol())
	    {
	      ABG_ASSERT(last_alias == 0);
	      last_alias = a;
	    }
	}
      ABG_ASSERT(last_alias);

      last_alias->priv_->next_alias_ = alias;
    }
  else
    priv_->next_alias_ = alias;

  alias->priv_->next_alias_ = get_main_symbol();
  alias->priv_->main_symbol_ = get_main_symbol();
}

/// Return true if the symbol is a common one.
///
/// @return true iff the symbol is common.
bool
elf_symbol::is_common_symbol() const
{return priv_->is_common_;}

/// Return true if this common common symbol has other common instances.
///
/// A common instance of a given common symbol is another common
/// symbol with the same name.  Those exist in relocatable files.  The
/// linker normally allocates all the instances into a common block in
/// the final output file.
///
/// Note that the current object must be a common symbol, otherwise,
/// this function aborts.
///
/// @return true iff the current common symbol has other common
/// instances.
bool
elf_symbol::has_other_common_instances() const
{
  ABG_ASSERT(is_common_symbol());
  return bool(get_next_common_instance());
}

/// Get the next common instance of the current common symbol.
///
/// A common instance of a given common symbol is another common
/// symbol with the same name.  Those exist in relocatable files.  The
/// linker normally allocates all the instances into a common block in
/// the final output file.
///
/// @return the next common instance, or nil if there is not any.
elf_symbol_sptr
elf_symbol::get_next_common_instance() const
{return priv_->next_common_instance_.lock();}

/// Add a common instance to the current common elf symbol.
///
/// Note that this symbol must be the main symbol.  Being the main
/// symbol means being the first common symbol to appear in the symbol
/// table.
///
/// @param common the other common instance to add.
void
elf_symbol::add_common_instance(const elf_symbol_sptr& common)
{
  if (!common)
    return;

  ABG_ASSERT(!common->has_other_common_instances());
  ABG_ASSERT(is_common_symbol());
  ABG_ASSERT(is_main_symbol());

  if (has_other_common_instances())
    {
      elf_symbol_sptr last_common_instance;
      for (elf_symbol_sptr c = get_next_common_instance();
	   c && (c.get() != get_main_symbol().get());
	   c = c->get_next_common_instance())
	{
	  if (c->get_next_common_instance().get() == get_main_symbol().get())
	    {
	      ABG_ASSERT(last_common_instance == 0);
	      last_common_instance = c;
	    }
	}
      ABG_ASSERT(last_common_instance);

      last_common_instance->priv_->next_common_instance_ = common;
    }
  else
    priv_->next_common_instance_ = common;

  common->priv_->next_common_instance_ = get_main_symbol();
  common->priv_->main_symbol_ = get_main_symbol();
}

/// Get a string that is representative of a given elf_symbol.
///
/// If the symbol has a version, then the ID string is the
/// concatenation of the name of the symbol, the '@' character, and
/// the version of the symbol.  If the version is the default version
/// of the symbol then the '@' character is replaced by a "@@" string.
///
/// Otherwise, if the symbol does not have any version, this function
/// returns the name of the symbol.
///
/// @return a the ID string.
const string&
elf_symbol::get_id_string() const
{
  if (priv_->id_string_.empty())
    {
      string s = get_name ();

      if (!get_version().is_empty())
	{
	  if (get_version().is_default())
	    s += "@@";
	  else
	    s += "@";
	  s += get_version().str();
	}
      priv_->id_string_ = s;
    }

  return priv_->id_string_;
}

/// From the aliases of the current symbol, lookup one with a given name.
///
/// @param name the name of symbol alias we are looking for.
///
/// @return the symbol alias that has the name @p name, or nil if none
/// has been found.
elf_symbol_sptr
elf_symbol::get_alias_from_name(const string& name) const
{
  if (name == get_name())
    return elf_symbol_sptr(priv_->main_symbol_);

   for (elf_symbol_sptr a = get_next_alias();
	a && a.get() != get_main_symbol().get();
	a = a->get_next_alias())
     if (a->get_name() == name)
       return a;

   return elf_symbol_sptr();
}

/// In the list of aliases of a given elf symbol, get the alias that
/// equals this current symbol.
///
/// @param other the elf symbol to get the potential aliases from.
///
/// @return the alias of @p other that texually equals the current
/// symbol, or nil if no alias textually equals the current symbol.
elf_symbol_sptr
elf_symbol::get_alias_which_equals(const elf_symbol& other) const
{
  for (elf_symbol_sptr a = other.get_next_alias();
       a && a.get() != a->get_main_symbol().get();
       a = a->get_next_alias())
    if (textually_equals(*this, *a))
      return a;
  return elf_symbol_sptr();
}

/// Return a comma separated list of the id of the current symbol as
/// well as the id string of its aliases.
///
/// @param syms a map of all the symbols of the corpus the current
/// symbol belongs to.
///
/// @param include_symbol_itself if set to true, then the name of the
/// current symbol is included in the list of alias names that is emitted.
///
/// @return the string.
string
elf_symbol::get_aliases_id_string(const string_elf_symbols_map_type& syms,
				  bool include_symbol_itself) const
{
  string result;

  if (include_symbol_itself)
      result = get_id_string();

  vector<elf_symbol_sptr> aliases;
  compute_aliases_for_elf_symbol(*this, syms, aliases);
  if (!aliases.empty() && include_symbol_itself)
    result += ", ";

  for (vector<elf_symbol_sptr>::const_iterator i = aliases.begin();
       i != aliases.end();
       ++i)
    {
      if (i != aliases.begin())
	result += ", ";
      result += (*i)->get_id_string();
    }
  return result;
}

/// Return a comma separated list of the id of the current symbol as
/// well as the id string of its aliases.
///
/// @param include_symbol_itself if set to true, then the name of the
/// current symbol is included in the list of alias names that is emitted.
///
/// @return the string.
string
elf_symbol::get_aliases_id_string(bool include_symbol_itself) const
{
  vector<elf_symbol_sptr> aliases;
  if (include_symbol_itself)
    aliases.push_back(get_main_symbol());

  for (elf_symbol_sptr a = get_next_alias();
       a && a.get() != get_main_symbol().get();
       a = a->get_next_alias())
    aliases.push_back(a);

  string result;
  for (vector<elf_symbol_sptr>::const_iterator i = aliases.begin();
       i != aliases.end();
       ++i)
    {
      if (i != aliases.begin())
	result += ", ";
      result += (*i)->get_id_string();
    }

  return result;
}

/// Given the ID of a symbol, get the name and the version of said
/// symbol.
///
/// @param id the symbol ID to consider.
///
/// @param name the symbol name extracted from the ID.  This is set
/// only if the function returned true.
///
/// @param ver the symbol version extracted from the ID.
bool
elf_symbol::get_name_and_version_from_id(const string&	id,
					 string&	name,
					 string&	ver)
{
  name.clear(), ver.clear();

  string::size_type i = id.find("@");
  if (i == string::npos)
    {
      name = id;
      return true;
    }

  name = id.substr(0, i);
  ++i;

  if (i >= id.size())
    return true;

  string::size_type j = id.find("@", i);
  if (j == string::npos)
    j = i;
  else
    ++j;

  if (j >= id.size())
    {
      ver = "";
      return true;
    }

  ver = id.substr(j);
  return true;
}

///@}

/// Test if two main symbols are textually equal, or, if they have
/// aliases that are textually equal.
///
/// @param other the symbol to compare against.
///
/// @return true iff the current instance of elf symbol equals the @p
/// other.
bool
elf_symbol::operator==(const elf_symbol& other) const
{
  bool are_equal = textually_equals(*this, other);
  if (!are_equal)
    are_equal = bool(get_alias_which_equals(other));
  return are_equal;
}

/// Test if the current symbol aliases another one.
///
/// @param o the other symbol to test against.
///
/// @return true iff the current symbol aliases @p o.
bool
elf_symbol::does_alias(const elf_symbol& o) const
{
  if (*this == o)
    return true;

  if (get_main_symbol() == o.get_main_symbol())
    return true;

  for (elf_symbol_sptr a = get_next_alias();
       a && !a->is_main_symbol();
       a = a->get_next_alias())
    {
      if (o == *a)
	return true;
    }
  return false;
}

/// Equality operator for smart pointers to elf_symbol.
///
/// @param lhs the first elf symbol to consider.
///
/// @param rhs the second elf symbol to consider.
///
/// @return true iff @p lhs equals @p rhs.
bool
operator==(const elf_symbol_sptr& lhs, const elf_symbol_sptr& rhs)
{
  if (!!lhs != !!rhs)
    return false;

  if (!lhs)
    return true;

  return *lhs == *rhs;
}

/// Inequality operator for smart pointers to elf_symbol.
///
/// @param lhs the first elf symbol to consider.
///
/// @param rhs the second elf symbol to consider.
///
/// @return true iff @p lhs is different from @p rhs.
bool
operator!=(const elf_symbol_sptr& lhs, const elf_symbol_sptr& rhs)
{return !operator==(lhs, rhs);}

/// Test if two symbols alias.
///
/// @param s1 the first symbol to consider.
///
/// @param s2 the second symbol to consider.
///
/// @return true if @p s1 aliases @p s2.
bool
elf_symbols_alias(const elf_symbol& s1, const elf_symbol& s2)
{return s1.does_alias(s2) || s2.does_alias(s1);}

void
compute_aliases_for_elf_symbol(const elf_symbol& sym,
			       const string_elf_symbols_map_type& symtab,
			       vector<elf_symbol_sptr>& aliases)
{

  if (elf_symbol_sptr a = sym.get_next_alias())
    for (; a && !a->is_main_symbol(); a = a->get_next_alias())
      aliases.push_back(a);
  else
    for (string_elf_symbols_map_type::const_iterator i = symtab.begin();
	 i != symtab.end();
	 ++i)
      for (elf_symbols::const_iterator j = i->second.begin();
	   j != i->second.end();
	   ++j)
	{
	  if (**j == sym)
	    for (elf_symbol_sptr s = (*j)->get_next_alias();
		 s && !s->is_main_symbol();
		 s = s->get_next_alias())
	      aliases.push_back(s);
	  else
	    for (elf_symbol_sptr s = (*j)->get_next_alias();
		 s && !s->is_main_symbol();
		 s = s->get_next_alias())
	      if (*s == sym)
		aliases.push_back(*j);
	}
}

/// Test if two symbols alias.
///
/// @param s1 the first symbol to consider.
///
/// @param s2 the second symbol to consider.
///
/// @return true if @p s1 aliases @p s2.
bool
elf_symbols_alias(const elf_symbol* s1, const elf_symbol* s2)
{
  if (!!s1 != !!s2)
    return false;
  if (s1 == s2)
    return true;
  return elf_symbols_alias(*s1, *s2);
}

/// Test if two symbols alias.
///
/// @param s1 the first symbol to consider.
///
/// @param s2 the second symbol to consider.
///
/// @return true if @p s1 aliases @p s2.
bool
elf_symbols_alias(const elf_symbol_sptr s1, const elf_symbol_sptr s2)
{return elf_symbols_alias(s1.get(), s2.get());}

/// Serialize an instance of @ref symbol_type and stream it to a given
/// output stream.
///
/// @param o the output stream to serialize the symbole type to.
///
/// @param t the symbol type to serialize.
std::ostream&
operator<<(std::ostream& o, elf_symbol::type t)
{
  string repr;

  switch (t)
    {
    case elf_symbol::NOTYPE_TYPE:
      repr = "unspecified symbol type";
      break;
    case elf_symbol::OBJECT_TYPE:
      repr = "variable symbol type";
      break;
    case elf_symbol::FUNC_TYPE:
      repr = "function symbol type";
      break;
    case elf_symbol::SECTION_TYPE:
      repr = "section symbol type";
      break;
    case elf_symbol::FILE_TYPE:
      repr = "file symbol type";
      break;
    case elf_symbol::COMMON_TYPE:
      repr = "common data object symbol type";
      break;
    case elf_symbol::TLS_TYPE:
      repr = "thread local data object symbol type";
      break;
    case elf_symbol::GNU_IFUNC_TYPE:
      repr = "indirect function symbol type";
      break;
    default:
      {
	std::ostringstream s;
	s << "unknown symbol type (" << (char)t << ')';
	repr = s.str();
      }
      break;
    }

  o << repr;
  return o;
}

/// Serialize an instance of @ref symbol_binding and stream it to a
/// given output stream.
///
/// @param o the output stream to serialize the symbole type to.
///
/// @param b the symbol binding to serialize.
std::ostream&
operator<<(std::ostream& o, elf_symbol::binding b)
{
  string repr;

  switch (b)
    {
    case elf_symbol::LOCAL_BINDING:
      repr = "local binding";
      break;
    case elf_symbol::GLOBAL_BINDING:
      repr = "global binding";
      break;
    case elf_symbol::WEAK_BINDING:
      repr = "weak binding";
      break;
    case elf_symbol::GNU_UNIQUE_BINDING:
      repr = "GNU unique binding";
      break;
    default:
      {
	std::ostringstream s;
	s << "unknown binding (" << (unsigned char) b << ")";
	repr = s.str();
      }
      break;
    }

  o << repr;
  return o;
}

/// Serialize an instance of @ref elf_symbol::visibility and stream it
/// to a given output stream.
///
/// @param o the output stream to serialize the symbole type to.
///
/// @param v the symbol visibility to serialize.
std::ostream&
operator<<(std::ostream& o, elf_symbol::visibility v)
{
  string repr;

  switch (v)
    {
    case elf_symbol::DEFAULT_VISIBILITY:
      repr = "default visibility";
      break;
    case elf_symbol::PROTECTED_VISIBILITY:
      repr = "protected visibility";
      break;
    case elf_symbol::HIDDEN_VISIBILITY:
      repr = "hidden visibility";
      break;
    case elf_symbol::INTERNAL_VISIBILITY:
      repr = "internal visibility";
      break;
    default:
      {
	std::ostringstream s;
	s << "unknown visibility (" << (unsigned char) v << ")";
	repr = s.str();
      }
      break;
    }

  o << repr;
  return o;
}

/// Convert a string representing a symbol type into an
/// elf_symbol::type.
///
///@param s the string to convert.
///
///@param t the resulting elf_symbol::type.
///
/// @return true iff the conversion completed successfully.
bool
string_to_elf_symbol_type(const string& s, elf_symbol::type& t)
{
  if (s == "no-type")
    t = elf_symbol::NOTYPE_TYPE;
  else if (s == "object-type")
    t = elf_symbol::OBJECT_TYPE;
  else if (s == "func-type")
    t = elf_symbol::FUNC_TYPE;
  else if (s == "section-type")
    t = elf_symbol::SECTION_TYPE;
  else if (s == "file-type")
    t = elf_symbol::FILE_TYPE;
  else if (s == "common-type")
    t = elf_symbol::COMMON_TYPE;
  else if (s == "tls-type")
    t = elf_symbol::TLS_TYPE;
  else if (s == "gnu-ifunc-type")
    t = elf_symbol::GNU_IFUNC_TYPE;
  else
    return false;

  return true;
}

/// Convert a string representing a an elf symbol binding into an
/// elf_symbol::binding.
///
/// @param s the string to convert.
///
/// @param b the resulting elf_symbol::binding.
///
/// @return true iff the conversion completed successfully.
bool
string_to_elf_symbol_binding(const string& s, elf_symbol::binding& b)
{
    if (s == "local-binding")
      b = elf_symbol::LOCAL_BINDING;
    else if (s == "global-binding")
      b = elf_symbol::GLOBAL_BINDING;
    else if (s == "weak-binding")
      b = elf_symbol::WEAK_BINDING;
    else if (s == "gnu-unique-binding")
      b = elf_symbol::GNU_UNIQUE_BINDING;
    else
      return false;

    return true;
}

/// Convert a string representing a an elf symbol visibility into an
/// elf_symbol::visibility.
///
/// @param s the string to convert.
///
/// @param b the resulting elf_symbol::visibility.
///
/// @return true iff the conversion completed successfully.
bool
string_to_elf_symbol_visibility(const string& s, elf_symbol::visibility& v)
{
  if (s == "default-visibility")
    v = elf_symbol::DEFAULT_VISIBILITY;
  else if (s == "protected-visibility")
    v = elf_symbol::PROTECTED_VISIBILITY;
  else if (s == "hidden-visibility")
    v = elf_symbol::HIDDEN_VISIBILITY;
  else if (s == "internal-visibility")
    v = elf_symbol::INTERNAL_VISIBILITY;
  else
    return false;

  return true;
}

/// Test if the type of an ELF symbol denotes a function symbol.
///
/// @param t the type of the ELF symbol.
///
/// @return true iff elf symbol type @p t denotes a function symbol
/// type.
bool
elf_symbol_is_function(elf_symbol::type t)
{return t == elf_symbol::FUNC_TYPE;}

/// Test if the type of an ELF symbol denotes a function symbol.
///
/// @param t the type of the ELF symbol.
///
/// @return true iff elf symbol type @p t denotes a function symbol
/// type.
bool
elf_symbol_is_variable(elf_symbol::type t)
{return t == elf_symbol::OBJECT_TYPE;}

// <elf_symbol::version stuff>

struct elf_symbol::version::priv
{
  string	version_;
  bool		is_default_;

  priv()
    : is_default_(false)
  {}

  priv(const string& v,
       bool d)
    : version_(v),
      is_default_(d)
  {}
}; // end struct elf_symbol::version::priv

elf_symbol::version::version()
  : priv_(new priv)
{}

/// @param v the name of the version.
///
/// @param is_default true if this is a default version.
elf_symbol::version::version(const string& v,
			     bool is_default)
  : priv_(new priv(v, is_default))
{}

elf_symbol::version::version(const elf_symbol::version& v)
  : priv_(new priv(v.str(), v.is_default()))
{
}

/// Cast the version_type into a string that is its name.
///
/// @return the name of the version.
elf_symbol::version::operator const string&() const
{return priv_->version_;}

/// Getter for the version name.
///
/// @return the version name.
const string&
elf_symbol::version::str() const
{return priv_->version_;}

/// Setter for the version name.
///
/// @param s the version name.
void
elf_symbol::version::str(const string& s)
{priv_->version_ = s;}

/// Getter for the 'is_default' property of the version.
///
/// @return true iff this is a default version.
bool
elf_symbol::version::is_default() const
{return priv_->is_default_;}

/// Setter for the 'is_default' property of the version.
///
/// @param f true if this is the default version.
void
elf_symbol::version::is_default(bool f)
{priv_->is_default_ = f;}

bool
elf_symbol::version::is_empty() const
{return str().empty();}

/// Compares the current version against another one.
///
/// @param o the other version to compare the current one to.
///
/// @return true iff the current version equals @p o.
bool
elf_symbol::version::operator==(const elf_symbol::version& o) const
{return str() == o.str();}

/// Inequality operator.
///
/// @param o the version to compare against the current one.
///
/// @return true iff both versions are different.
bool
elf_symbol::version::operator!=(const version& o) const
{return !operator==(o);}

/// Assign a version to the current one.
///
/// @param o the other version to assign to this one.
///
/// @return a reference to the assigned version.
elf_symbol::version&
elf_symbol::version::operator=(const elf_symbol::version& o)
{
  str(o.str());
  is_default(o.is_default());
  return *this;
}

// </elf_symbol::version stuff>

// </elf_symbol stuff>

// <class dm_context_rel stuff>
struct dm_context_rel::priv
{
  bool is_laid_out_;
  size_t offset_in_bits_;
  var_decl* anonymous_data_member_;

  priv(bool is_static = false)
    : is_laid_out_(!is_static),
      offset_in_bits_(0),
      anonymous_data_member_()
  {}

  priv(bool is_laid_out, size_t offset_in_bits)
    : is_laid_out_(is_laid_out),
      offset_in_bits_(offset_in_bits),
      anonymous_data_member_()
  {}
}; //end struct dm_context_rel::priv

dm_context_rel::dm_context_rel()
  : context_rel(),
    priv_(new priv)
{}

dm_context_rel::dm_context_rel(scope_decl* s,
			       bool is_laid_out,
			       size_t offset_in_bits,
			       access_specifier a,
			       bool is_static)
  : context_rel(s, a, is_static),
    priv_(new priv(is_laid_out, offset_in_bits))
{}

dm_context_rel::dm_context_rel(scope_decl* s)
  : context_rel(s),
    priv_(new priv())
{}

bool
dm_context_rel::get_is_laid_out() const
{return priv_->is_laid_out_;}

void
dm_context_rel::set_is_laid_out(bool f)
{priv_->is_laid_out_ = f;}

size_t
dm_context_rel::get_offset_in_bits() const
{return priv_->offset_in_bits_;}

void
dm_context_rel::set_offset_in_bits(size_t o)
{priv_->offset_in_bits_ = o;}

bool
dm_context_rel::operator==(const dm_context_rel& o) const
{
  if (!context_rel::operator==(o))
    return false;

  return (priv_->is_laid_out_ == o.priv_->is_laid_out_
	    && priv_->offset_in_bits_ == o.priv_->offset_in_bits_);
}

bool
dm_context_rel::operator!=(const dm_context_rel& o) const
{return !operator==(o);}

/// Return a non-nil value if this data member context relationship
/// has an anonymous data member.  That means, if the data member this
/// relation belongs to is part of an anonymous data member.
///
/// @return the containing anonymous data member of this data member
/// relationship.  Nil if there is none.
const var_decl*
dm_context_rel::get_anonymous_data_member() const
{return priv_->anonymous_data_member_;}

/// Set the containing anonymous data member of this data member
/// context relationship. That means that the data member this
/// relation belongs to is part of an anonymous data member.
///
/// @param anon_dm the containing anonymous data member of this data
/// member relationship.  Nil if there is none.
void
dm_context_rel::set_anonymous_data_member(var_decl* anon_dm)
{priv_->anonymous_data_member_ = anon_dm;}

dm_context_rel::~dm_context_rel()
{}
// </class dm_context_rel stuff>

// <environment stuff>

/// Convenience typedef for a map of interned_string -> bool.
typedef unordered_map<interned_string,
		      bool, hash_interned_string> interned_string_bool_map_type;

/// The private data of the @ref environment type.
struct environment::priv
{
  canonical_types_map_type	 canonical_types_;
  mutable vector<type_base_sptr> sorted_canonical_types_;
  type_base_sptr		 void_type_;
  type_base_sptr		 variadic_marker_type_;
  unordered_set<const class_or_union*>	classes_being_compared_;
  unordered_set<const function_type*>	fn_types_being_compared_;
  vector<type_base_sptr>	 extra_live_types_;
  interned_string_pool		 string_pool_;
  bool				 canonicalization_is_done_;
  bool				 do_on_the_fly_canonicalization_;
  bool				 decl_only_class_equals_definition_;

  priv()
    : canonicalization_is_done_(),
      do_on_the_fly_canonicalization_(true),
      decl_only_class_equals_definition_(false)
  {}
};// end struct environment::priv

/// Default constructor of the @ref environment type.
environment::environment()
  :priv_(new priv)
{}

/// Destructor for the @ref environment type.
environment::~environment()
{}

/// Getter the map of canonical types.
///
/// @return the map of canonical types.  The key of the map is the
/// hash of the canonical type and its value if the canonical type.
environment::canonical_types_map_type&
environment::get_canonical_types_map()
{return priv_->canonical_types_;}

/// Getter the map of canonical types.
///
/// @return the map of canonical types.  The key of the map is the
/// hash of the canonical type and its value if the canonical type.
const environment::canonical_types_map_type&
environment::get_canonical_types_map() const
{return const_cast<environment*>(this)->get_canonical_types_map();}

/// Helper to detect if a type is either a reference, a pointer, or a
/// qualified type.
static bool
is_ptr_ref_or_qual_type(const type_base *t)
{
  if (is_pointer_type(t)
      || is_reference_type(t)
      || is_qualified_type(t))
    return true;
  return false;
}

/// A functor to sort decls somewhat topologically.  That is, types
/// are sorted in a way that makes the ones that are defined "first"
/// to come first.
///
/// The topological criteria is a lexicographic sort of the definition
/// location of the type.  For types that have no location (or the
/// same location), it's their qualified name that is used for the
/// lexicographic sort.
struct decl_topo_comp
{

  /// The "Less Than" comparison operator of this functor.
  ///
  /// @param f the first decl to be considered for the comparison.
  ///
  /// @param s the second decl to be considered for the comparison.
  ///
  /// @return true iff @p f is less than @p s.
  bool
  operator()(const decl_base *f,
	     const decl_base *s)
  {
    if (!!f != !!s)
      return f && !s;

    if (!f)
      return false;

    location fl = f->get_location();
    location sl = s->get_location();
    if (fl.get_value() != sl.get_value())
      return fl.get_value() < sl.get_value();

    // We reach this point if location data is useless.
    return (get_pretty_representation(f, true)
	    < get_pretty_representation(s, true));
  }

  /// The "Less Than" comparison operator of this functor.
  ///
  /// @param f the first decl to be considered for the comparison.
  ///
  /// @param s the second decl to be considered for the comparison.
  ///
  /// @return true iff @p f is less than @p s.
  bool
  operator()(const decl_base_sptr &f,
	     const decl_base_sptr &s)
  {return operator()(f.get(), s.get());}

}; // end struct decl_topo_comp

/// A functor to sort types somewhat topologically.  That is, types
/// are sorted in a way that makes the ones that are defined "first"
/// to come first.
///
/// The topological criteria is a lexicographic sort of the definition
/// location of the type.  For types that have no location, it's their
/// qualified name that is used for the lexicographic sort.
struct type_topo_comp
{
  /// The "Less Than" comparison operator of this functor.
  ///
  /// @param f the first type to be considered for the comparison.
  ///
  /// @param s the second type to be considered for the comparison.
  ///
  /// @return true iff @p f is less than @p s.
  bool
  operator()(const type_base_sptr &f,
	     const type_base_sptr &s)
  {return operator()(f.get(), s.get());}

  /// The "Less Than" comparison operator of this functor.
  ///
  /// @param f the first type to be considered for the comparison.
  ///
  /// @param s the second type to be considered for the comparison.
  ///
  /// @return true iff @p f is less than @p s.
  bool
  operator()(const type_base *f,
	     const type_base *s)
  {
    bool f_is_ptr_ref_or_qual = is_ptr_ref_or_qual_type(f);
    bool s_is_ptr_ref_or_qual = is_ptr_ref_or_qual_type(s);

    if (f_is_ptr_ref_or_qual != s_is_ptr_ref_or_qual)
      return !f_is_ptr_ref_or_qual && s_is_ptr_ref_or_qual;

    if (f_is_ptr_ref_or_qual && s_is_ptr_ref_or_qual)
      {
	string s1 = get_pretty_representation(f, true);
	string s2 = get_pretty_representation(s, true);
	if (s1 == s2)
	  if (qualified_type_def * q = is_qualified_type(f))
	    if (q->get_cv_quals() == qualified_type_def::CV_NONE)
	      if (!is_qualified_type(s))
		// We are looking at two types that are the result of
		// an optimization that happens during the IR
		// construction.  Namely, type f is a cv-qualified
		// type with no qualifier (no const, no volatile, no
		// nothing, we call it an empty-qualified type).
		// These are the result of an optimization which
		// removes "redundant qualifiers" from some types.
		// For instance, consider a "const reference".  The
		// const there is redundant because a reference is
		// always const.  So as a result of the optimizaton
		// that type is going to be transformed into an
		// empty-qualified reference. If we don't make that
		// optimization, then we risk having spurious change
		// reports down the road.  But then, as a consequence
		// of that optimization, we need to sort the
		// empty-qualified type and its non-qualified variant
		// e.g, to ensure stability in the abixml output; both
		// types are logically equal, but here, we decide that
		// the empty-qualified one is topologically "less
		// than" the non-qualified counterpart.
		//
		// So here, type f is an empty-qualified type and type
		// s is its non-qualified variant.  We decide that f
		// is topologically less than s.
		return true;
	return (s1 < s2);
      }

    decl_base *fd = is_decl(f);
    decl_base *sd = is_decl(s);

    if (!!fd != !!sd)
      return fd && !sd;

    if (!fd)
      {
	type_base *peeled_f = peel_pointer_or_reference_type(f);
	type_base *peeled_s = peel_pointer_or_reference_type(s);

	fd = is_decl(peeled_f);
	sd = is_decl(peeled_s);

	if (!!fd != !!sd)
	  return fd && !sd;

	if (!fd)
	  return (get_pretty_representation(f, true)
		  < get_pretty_representation(s, true));
      }

    // From this point, fd and sd should be non-nil
    decl_topo_comp decl_comp;
    return decl_comp(fd, sd);
  }
}; //end struct type_topo_comp

/// Get a @ref type_decl that represents a "void" type for the current
/// environment.
///
/// @return the @ref type_decl that represents a "void" type.
const type_base_sptr&
environment::get_void_type() const
{
  if (!priv_->void_type_)
    priv_->void_type_.reset(new type_decl(const_cast<environment*>(this),
					       intern("void"),
					       0, 0, location()));
  return priv_->void_type_;
}

/// Get a @ref type_decl instance that represents a the type of a
/// variadic function parameter.
///
/// @return the Get a @ref type_decl instance that represents a the
/// type of a variadic function parameter.
const type_base_sptr&
environment::get_variadic_parameter_type() const
{
  if (!priv_->variadic_marker_type_)
    priv_->variadic_marker_type_.
      reset(new type_decl(const_cast<environment*>(this),
			  intern("variadic parameter type"),
			  0, 0, location()));
  return priv_->variadic_marker_type_;
}

/// Test if the canonicalization of types created out of the current
/// environment is done.
///
/// @return true iff the canonicalization of types created out of the current
/// environment is done.
bool
environment::canonicalization_is_done() const
{return priv_->canonicalization_is_done_;}

/// Set a flag saying if the canonicalization of types created out of
/// the current environment is done or not.
///
/// Note that this function must only be called by internal code of
/// the library that creates ABI artifacts (e.g, read an abi corpus
/// from elf or from our own xml format and creates representations of
/// types out of it) and thus needs to canonicalize types to speed-up
/// further type comparison.
///
/// @param f the new value of the flag.
void
environment::canonicalization_is_done(bool f)
{priv_->canonicalization_is_done_ = f;}

/// Getter for the "on-the-fly-canonicalization" flag.
///
/// @return true iff @ref OnTheFlyCanonicalization
/// "on-the-fly-canonicalization" is to be performed during
/// comparison.
bool
environment::do_on_the_fly_canonicalization() const
{return priv_->do_on_the_fly_canonicalization_;}

/// Setter for the "on-the-fly-canonicalization" flag.
///
/// @param f If this is true then @ref OnTheFlyCanonicalization
/// "on-the-fly-canonicalization" is to be performed during
/// comparison.
void
environment::do_on_the_fly_canonicalization(bool f)
{priv_->do_on_the_fly_canonicalization_ = f;}

/// Getter of the "decl-only-class-equals-definition" flag.
///
/// Usually, a declaration-only class named 'struct foo' compares
/// equal to any class definition named "struct foo'.  This is at
/// least true for C++.
///
/// In C, though, because there can be multiple definitions of 'struct
/// foo' in the binary, a declaration-only "struct foo" might be
/// considered to *NOT* resolve to any of the struct foo defined.  In
/// that case, the declaration-only "struct foo" is considered
/// different from the definitions.
///
/// This flag controls the behaviour of the comparison of an
/// unresolved decl-only class against a definition of the same name.
///
/// If set to false, the the declaration equals the definition.  If
/// set to false, then the decalration is considered different from
/// the declaration.
///
/// @return the value of the "decl-only-class-equals-definition" flag.
bool
environment::decl_only_class_equals_definition() const
{return priv_->decl_only_class_equals_definition_;}

/// Setter of the "decl-only-class-equals-definition" flag.
///
/// Usually, a declaration-only class named 'struct foo' compares
/// equal to any class definition named "struct foo'.  This is at
/// least true for C++.
///
/// In C, though, because there can be multiple definitions of 'struct
/// foo' in the binary, a declaration-only "struct foo" might be
/// considered to *NOT* resolve to any of the struct foo defined.  In
/// that case, the declaration-only "struct foo" is considered
/// different from the definitions.
///
/// This flag controls the behaviour of the comparison of an
/// unresolved decl-only class against a definition of the same name.
///
/// If set to false, the the declaration equals the definition.  If
/// set to false, then the decalration is considered different from
/// the declaration.
///
/// @param the new value of the "decl-only-class-equals-definition"
/// flag.
void
environment::decl_only_class_equals_definition(bool f) const
{priv_->decl_only_class_equals_definition_ = f;}

/// Test if a given type is a void type as defined in the current
/// environment.
///
/// @param t the type to consider.
///
/// @return true iff @p t is a void type as defined in the current
/// environment.
bool
environment::is_void_type(const type_base_sptr& t) const
{
  if (!t)
    return false;
  return t.get() == get_void_type().get();
}

/// Test if a given type is a void type as defined in the current
/// environment.
///
/// @param t the type to consider.
///
/// @return true iff @p t is a void type as defined in the current
/// environment.
bool
environment::is_void_type(const type_base* t) const
{
  if (!t)
    return false;
  return t == get_void_type().get();
}

/// Test if a type is a variadic parameter type as defined in the
/// current environment.
///
/// @param t the type to consider.
///
/// @return true iff @p t is a variadic parameter type as defined in
/// the current environment.
bool
environment::is_variadic_parameter_type(const type_base* t) const
{
  if (!t)
    return false;
  return t == get_variadic_parameter_type().get();
}

/// Test if a type is a variadic parameter type as defined in the
/// current environment.
///
/// @param t the type to consider.
///
/// @return true iff @p t is a variadic parameter type as defined in
/// the current environment.
bool
environment::is_variadic_parameter_type(const type_base_sptr& t) const
{return is_variadic_parameter_type(t.get());}

/// Do intern a string.
///
/// If a value of this string already exists in the interned string
/// pool of the current environment, then this function returns a new
/// interned_string pointing to that already existing string.
/// Otherwise, a new string is created, stored in the interned string
/// pool and a new interned_string instance is created to point to
/// that new intrerned string, and it's return.
///
/// @param s the value of the string to intern.
///
/// @return the interned string.
interned_string
environment::intern(const string& s) const
{return const_cast<environment*>(this)->priv_->string_pool_.create_string(s);}

// </environment stuff>

// <type_or_decl_base stuff>

/// The private data of @ref type_or_decl_base.
struct type_or_decl_base::priv
{
  // This holds the kind of dynamic type of particular instance.
  // Yes, this is part of the implementation of a "poor man" runtime
  // type identification.  We are doing this because profiling shows
  // that using dynamic_cast in some places is really to slow and is
  // constituting a hotspot.  This poor man's implementation made
  // things be much faster.
  enum type_or_decl_kind	kind_;
  // This holds the runtime type instance pointer of particular
  // instance.  In other words, this is the "this pointer" of the
  // dynamic type of a particular instance.
  void*			rtti_;
  // This holds a pointer to either the type_base sub-object (if the
  // current instance is a type) or the decl_base sub-object (if the
  // current instance is a decl).  This is used by the is_decl() and
  // is_type() functions, which also show up during profiling as
  // hotspots, due to their use of dynamic_cast.
  void*			type_or_decl_ptr_;
  bool				hashing_started_;
  const environment*		env_;
  translation_unit*		translation_unit_;

  /// Constructor of the type_or_decl_base::priv private type.
  ///
  /// @param e the environment in which the ABI artifact was created.
  ///
  /// @param k the identifier of the runtime type of the current
  /// instance of ABI artifact.
  priv(const environment* e = 0,
       enum type_or_decl_kind k = ABSTRACT_TYPE_OR_DECL)
    : kind_(k),
      rtti_(),
      type_or_decl_ptr_(),
      hashing_started_(),
      env_(e),
      translation_unit_()
  {}

  enum type_or_decl_kind
  kind() const
  {return kind_;}

  void
  kind (enum type_or_decl_kind k)
  {kind_ |= k;}
}; // end struct type_or_decl_base::priv

/// bitwise "OR" operator for the type_or_decl_base::type_or_decl_kind
/// bitmap type.
type_or_decl_base::type_or_decl_kind
operator|(type_or_decl_base::type_or_decl_kind l,
	  type_or_decl_base::type_or_decl_kind r)
{
  return static_cast<type_or_decl_base::type_or_decl_kind>
    (static_cast<unsigned>(l) | static_cast<unsigned>(r));
}

/// bitwise "|=" operator for the type_or_decl_base::type_or_decl_kind
/// bitmap type.
type_or_decl_base::type_or_decl_kind&
operator|=(type_or_decl_base::type_or_decl_kind& l,
	   type_or_decl_base::type_or_decl_kind r)
{
  l = l | r;
  return l;
}

/// bitwise "AND" operator for the
/// type_or_decl_base::type_or_decl_kind bitmap type.
type_or_decl_base::type_or_decl_kind
operator&(type_or_decl_base::type_or_decl_kind l,
	  type_or_decl_base::type_or_decl_kind r)
{
  return static_cast<type_or_decl_base::type_or_decl_kind>
    (static_cast<unsigned>(l) & static_cast<unsigned>(r));
}

/// bitwise "A&=" operator for the
/// type_or_decl_base::type_or_decl_kind bitmap type.
type_or_decl_base::type_or_decl_kind&
operator&=(type_or_decl_base::type_or_decl_kind& l,
	  type_or_decl_base::type_or_decl_kind r)
{
  l = l & r;
  return l;
}

/// Default constructor of @ref type_or_decl_base.
type_or_decl_base::type_or_decl_base()
  :priv_(new priv)
{}

/// Constructor of @ref type_or_decl_base.
///
/// @param the environment the current ABI artifact is constructed
/// from.
///
/// @param k the runtime identifier bitmap of the type being built.
type_or_decl_base::type_or_decl_base(const environment* e,
				     enum type_or_decl_kind k)
  :priv_(new priv(e, k))
{}

/// Copy constructor of @ref type_or_decl_base.
type_or_decl_base::type_or_decl_base(const type_or_decl_base& o)
{*priv_ = *o.priv_;}

/// The destructor of the @ref type_or_decl_base type.
type_or_decl_base::~type_or_decl_base()
{}

/// Getter for the "kind" property of @ref type_or_decl_base type.
///
/// This property holds the identifier bitmap of the runtime type of
/// an ABI artifact.
///
/// @return the runtime type identifier bitmap of the current ABI
/// artifact.
enum type_or_decl_base::type_or_decl_kind
type_or_decl_base::kind() const
{return priv_->kind();}

/// Setter for the "kind" property of @ref type_or_decl_base type.
///
/// This property holds the identifier bitmap of the runtime type of
/// an ABI artifact.
///
/// @param the runtime type identifier bitmap of the current ABI
/// artifact.
void
type_or_decl_base::kind(enum type_or_decl_kind k)
{priv_->kind(k);}

/// Getter of the pointer to the runtime type sub-object of the
/// current instance.
///
/// @return the pointer to the runtime type sub-object of the current
/// instance.
const void*
type_or_decl_base::runtime_type_instance() const
{return priv_->rtti_;}

/// Getter of the pointer to the runtime type sub-object of the
/// current instance.
///
/// @return the pointer to the runtime type sub-object of the current
/// instance.
void*
type_or_decl_base::runtime_type_instance()
{return priv_->rtti_;}

/// Setter of the pointer to the runtime type sub-object of the
/// current instance.
///
/// @param i the new pointer to the runtime type sub-object of the
/// current instance.
void
type_or_decl_base::runtime_type_instance(void* i)
{
  priv_->rtti_ = i;
  if (type_base* t = dynamic_cast<type_base*>(this))
    priv_->type_or_decl_ptr_ = t;
  else if (decl_base *d = dynamic_cast<decl_base*>(this))
    priv_->type_or_decl_ptr_ = d;
}

/// Getter of the pointer to either the type_base sub-object of the
/// current instance if it's a type, or to the decl_base sub-object of
/// the current instance if it's a decl.
///
/// @return the pointer to either the type_base sub-object of the
/// current instance if it's a type, or to the decl_base sub-object of
/// the current instance if it's a decl.
const void*
type_or_decl_base::type_or_decl_base_pointer() const
{return const_cast<type_or_decl_base*>(this)->type_or_decl_base_pointer();}

/// Getter of the pointer to either the type_base sub-object of the
/// current instance if it's a type, or to the decl_base sub-object of
/// the current instance if it's a decl.
///
/// @return the pointer to either the type_base sub-object of the
/// current instance if it's a type, or to the decl_base sub-object of
/// the current instance if it's a decl.
void*
type_or_decl_base::type_or_decl_base_pointer()
{return priv_->type_or_decl_ptr_;}

/// Getter for the 'hashing_started' property.
///
/// @return the 'hashing_started' property.
bool
type_or_decl_base::hashing_started() const
{return priv_->hashing_started_;}

/// Setter for the 'hashing_started' property.
///
/// @param b the value to set the 'hashing_property' to.
void
type_or_decl_base::hashing_started(bool b) const
{priv_->hashing_started_ = b;}

/// Setter of the environment of the current ABI artifact.
///
/// This just sets the environment artifact of the current ABI
/// artifact, not on its sub-trees.  If you want to set the
/// environment of an ABI artifact including its sub-tree, use the
/// abigail::ir::set_environment_for_artifact() function.
///
/// @param env the new environment.
void
type_or_decl_base::set_environment(const environment* env)
{priv_->env_ = env;}

/// Getter of the environment of the current ABI artifact.
///
/// @return the environment of the artifact.
const environment*
type_or_decl_base::get_environment() const
{return priv_->env_;}

/// Getter of the environment of the current ABI artifact.
///
/// @return the environment of the artifact.
environment*
type_or_decl_base::get_environment()
{return const_cast<environment*>(priv_->env_);}

/// Get the @ref corpus this ABI artifact belongs to.
///
/// @return the corpus this ABI artifact belongs to, or nil if it
/// belongs to none for now.
corpus*
type_or_decl_base::get_corpus()
{
  translation_unit* tu = get_translation_unit();
  if (!tu)
    return 0;
  return tu->get_corpus();
}


/// Get the @ref corpus this ABI artifact belongs to.
///
/// @return the corpus this ABI artifact belongs to, or nil if it
/// belongs to none for now.
const corpus*
type_or_decl_base::get_corpus() const
{return const_cast<type_or_decl_base*>(this)->get_corpus();}

/// Set the @ref translation_unit this ABI artifact belongs to.
///
/// Note that adding an ABI artifact to a containining on should
/// invoke this member function.
void
type_or_decl_base::set_translation_unit(translation_unit* tu)
{priv_->translation_unit_ = tu;}


/// Get the @ref translation_unit this ABI artifact belongs to.
///
/// @return the translation unit this ABI artifact belongs to, or nil
/// if belongs to none for now.
translation_unit*
type_or_decl_base::get_translation_unit()
{return priv_->translation_unit_;}

/// Get the @ref translation_unit this ABI artifact belongs to.
///
/// @return the translation unit this ABI artifact belongs to, or nil
/// if belongs to none for now.
const translation_unit*
type_or_decl_base::get_translation_unit() const
{return const_cast<type_or_decl_base*>(this)->get_translation_unit();}

/// Assignment operator for @ref type_or_decl_base.
///
/// @param o the other instance to assign the current instance to.
///
/// return a reference to the assigned instance of @ref
/// type_or_decl_base.
type_or_decl_base&
type_or_decl_base::operator=(const type_or_decl_base& o)
{
  *priv_ = *o.priv_;
  return *this;
}

/// Traverse the the ABI artifact.
///
/// @param v the visitor used to traverse the sub-tree nodes of the
/// artifact.
bool
type_or_decl_base::traverse(ir_node_visitor&)
{return true;}

/// Set the environment of a given ABI artifact, including recursively
/// setting the environment on the sub-trees of the artifact.
///
/// @param artifact the artifact to set the environment for.
///
/// @param env the new environment.
void
set_environment_for_artifact(type_or_decl_base* artifact,
			     const environment* env)
{
  ABG_ASSERT(artifact && env);

  ::environment_setter s(env);
  artifact->traverse(s);
}

/// Set the environment of a given ABI artifact, including recursively
/// setting the environment on the sub-trees of the artifact.
///
/// @param artifact the artifact to set the environment for.
///
/// @param env the new environment.
void
set_environment_for_artifact(type_or_decl_base_sptr artifact,
			     const environment* env)
{set_environment_for_artifact(artifact.get(), env);}

/// Non-member equality operator for the @type_or_decl_base type.
///
/// @param lr the left-hand operand of the equality.
///
/// @param rr the right-hand operatnr of the equality.
///
/// @return true iff @p lr equals @p rr.
bool
operator==(const type_or_decl_base& lr, const type_or_decl_base& rr)
{
  const type_or_decl_base* l = &lr;
  const type_or_decl_base* r = &rr;

  const decl_base* dl = dynamic_cast<const decl_base*>(l),
    *dr = dynamic_cast<const decl_base*>(r);

  if (!!dl != !!dr)
    return false;

  if (dl && dr)
    return *dl == *dr;

  const type_base* tl = dynamic_cast<const type_base*>(l),
    *tr = dynamic_cast<const type_base*>(r);

  if (!!tl != !!tr)
    return false;

  if (tl && tr)
    return *tl == *tr;

  return false;
}

/// Non-member equality operator for the @type_or_decl_base type.
///
/// @param l the left-hand operand of the equality.
///
/// @param r the right-hand operatnr of the equality.
///
/// @return true iff @p l equals @p r.
bool
operator==(const type_or_decl_base_sptr& l, const type_or_decl_base_sptr& r)
{
  if (!! l != !!r)
    return false;

  if (!l)
    return true;

  return *r == *l;
}

/// Non-member inequality operator for the @type_or_decl_base type.
///
/// @param l the left-hand operand of the equality.
///
/// @param r the right-hand operator of the equality.
///
/// @return true iff @p l is different from @p r.
bool
operator!=(const type_or_decl_base_sptr& l, const type_or_decl_base_sptr& r)
{return !operator==(l, r);}

// </type_or_decl_base stuff>

// <Decl definition>

struct decl_base::priv
{
  bool			in_pub_sym_tab_;
  bool			is_anonymous_;
  bool			is_artificial_;
  bool			has_anonymous_parent_;
  location		location_;
  context_rel		*context_;
  interned_string	name_;
  interned_string	qualified_parent_name_;
  // This temporary qualified name is the cache used for the qualified
  // name before the type associated to this decl (if applicable) is
  // canonicalized.  Once the type is canonicalized, the cached use is
  // the data member qualified_parent_name_ above.
  interned_string	temporary_qualified_name_;
  // This is the fully qualified name of the decl.  It contains the
  // name of the decl and the qualified name of its scope.  So if in
  // the parent scopes of the decl, there is one anonymous struct,
  // somewhere in the name, there is going to by an
  // __anonymous_struct__ string, even if the anonymous struct is not
  // the direct containing scope of this decl.
  interned_string	qualified_name_;
  // Unline qualified_name_, scoped_name_ contains the name of the
  // decl and the name of its scope; not the qualified name of the
  // scope.
    interned_string	scoped_name_;
  interned_string	linkage_name_;
  visibility		visibility_;
  decl_base_sptr	declaration_;
  decl_base_wptr	definition_of_declaration_;
  decl_base*		naked_definition_of_declaration_;
  bool			is_declaration_only_;

  priv()
    : in_pub_sym_tab_(false),
      is_anonymous_(true),
      is_artificial_(false),
      has_anonymous_parent_(false),
      context_(),
      visibility_(VISIBILITY_DEFAULT),
      naked_definition_of_declaration_(),
      is_declaration_only_(false)
  {}

  priv(interned_string name, const location& locus,
       interned_string linkage_name, visibility vis)
    : in_pub_sym_tab_(false),
      location_(locus),
      context_(),
      name_(name),
      qualified_name_(name),
      linkage_name_(linkage_name),
      visibility_(vis),
      naked_definition_of_declaration_(),
      is_declaration_only_(false)
  {
    is_anonymous_ = name_.empty();
    has_anonymous_parent_ = false;
  }

  priv(const location& l)
    : in_pub_sym_tab_(false),
      is_anonymous_(true),
      has_anonymous_parent_(false),
      location_(l),
      context_(),
      visibility_(VISIBILITY_DEFAULT),
      naked_definition_of_declaration_(),
      is_declaration_only_(false)
  {}

  ~priv()
  {
    delete context_;
  }
};// end struct decl_base::priv

/// Constructor for the @ref decl_base type.
///
/// @param e the environment the current @ref decl_base is being
/// created in.
///
/// @param name the name of the declaration.
///
/// @param locus the location where to find the declaration in the
/// source code.
///
/// @param linkage_name the linkage name of the declaration.
///
/// @param vis the visibility of the declaration.
decl_base::decl_base(const environment* e,
		     const string&	name,
		     const location&	locus,
		     const string&	linkage_name,
		     visibility	vis)
  : type_or_decl_base(e, ABSTRACT_DECL_BASE),
    priv_(new priv(e->intern(name), locus, e->intern(linkage_name), vis))
{
}

/// Constructor.
///
/// @param e the environment this instance of @ref decl_base is
/// created in.
///
/// @param name the name of the declaration being constructed.
///
/// @param locus the source location of the declaration being constructed.
///
/// @param linkage_name the linkage name of the declaration being
/// constructed.
///
/// @param vis the visibility of the declaration being constructed.
decl_base::decl_base(const environment* e,
		     const interned_string& name,
		     const location& locus,
		     const interned_string& linkage_name,
		     visibility vis)
  : type_or_decl_base(e, ABSTRACT_DECL_BASE),
    priv_(new priv(name, locus, linkage_name, vis))
{}

/// Constructor for the @ref decl_base type.
///
///@param environment the environment this instance of @ref decl_base
/// is being constructed in.
///
/// @param l the location where to find the declaration in the source
/// code.
decl_base::decl_base(const environment* e, const location& l)
  : type_or_decl_base(e, ABSTRACT_DECL_BASE),
    priv_(new priv(l))
{}

decl_base::decl_base(const decl_base& d)
  : type_or_decl_base(d)
{
  priv_->in_pub_sym_tab_ = d.priv_->in_pub_sym_tab_;
  priv_->location_ = d.priv_->location_;
  priv_->name_ = d.priv_->name_;
  priv_->qualified_parent_name_ = d.priv_->qualified_parent_name_;
  priv_->qualified_name_ = d.priv_->qualified_name_;
  priv_->linkage_name_ = d.priv_->linkage_name_;
  priv_->context_ = d.priv_->context_;
  priv_->visibility_ = d.priv_->visibility_;
}

/// Getter for the qualified name.
///
/// Unlike decl_base::get_qualified_name() this doesn't try to update
/// the qualified name.
///
/// @return the qualified name.
const interned_string&
decl_base::peek_qualified_name() const
{return priv_->qualified_name_;}

/// Clear the qualified name of this decl.
///
/// This is useful to ensure that the cache for the qualified name of
/// the decl is refreshed right after type canonicalization, for
/// instance.
void
decl_base::clear_qualified_name()
{priv_->qualified_name_.clear();}

/// Setter for the qualified name.
///
/// @param n the new qualified name.
void
decl_base::set_qualified_name(const interned_string& n) const
{priv_->qualified_name_ = n;}

/// Getter of the temporary qualified name of the current declaration.
///
/// This temporary qualified name is used as a qualified name cache by
/// the type for which this is the declaration (when applicable)
/// before the type is canonicalized.  Once the type is canonicalized,
/// it's the result of decl_base::peek_qualified_name() that becomes
/// the qualified name cached.
///
/// @return the temporary qualified name.
const interned_string&
decl_base::peek_temporary_qualified_name() const
{return priv_->temporary_qualified_name_;}

/// Setter for the temporary qualified name of the current
/// declaration.
///
///@param n the new temporary qualified name.
///
/// This temporary qualified name is used as a qualified name cache by
/// the type for which this is the declaration (when applicable)
/// before the type is canonicalized.  Once the type is canonicalized,
/// it's the result of decl_base::peek_qualified_name() that becomes
/// the qualified name cached.
void
decl_base::set_temporary_qualified_name(const interned_string& n) const
{priv_->temporary_qualified_name_ = n;}

///Getter for the context relationship.
///
///@return the context relationship for the current decl_base.
const context_rel*
decl_base::get_context_rel() const
{return priv_->context_;}

///Getter for the context relationship.
///
///@return the context relationship for the current decl_base.
context_rel*
decl_base::get_context_rel()
{return priv_->context_;}

void
decl_base::set_context_rel(context_rel *c)
{priv_->context_ = c;}

/// Get the hash of a decl.  If the hash hasn't been computed yet,
/// compute it ans store its value; otherwise, just return the hash.
///
/// @return the hash of the decl.
size_t
decl_base::get_hash() const
{
  size_t result = 0;

  if (const type_base* t = dynamic_cast<const type_base*>(this))
    {
      type_base::dynamic_hash hash;
      result = hash(t);
    }
  else
    // If we reach this point, it mean we are missing a virtual
    // overload for decl_base::get_hash.  Add it!
    abort();

  return result;
}

/// Test if the decl is defined in a ELF symbol table as a public
/// symbol.
///
/// @return true iff the decl is defined in a ELF symbol table as a
/// public symbol.
bool
decl_base::get_is_in_public_symbol_table() const
{return priv_->in_pub_sym_tab_;}

/// Set the flag saying if this decl is from a symbol that is in
/// a public symbols table, defined as public (global or weak).
///
/// @param f the new flag value.
void
decl_base::set_is_in_public_symbol_table(bool f)
{priv_->in_pub_sym_tab_ = f;}

/// Get the location of a given declaration.
///
/// The location is an abstraction for the tripplet {file path,
/// line, column} that defines where the declaration appeared in the
/// source code.
///
/// To get the value of the tripplet {file path, line, column} from
/// the @ref location, you need to use the
/// location_manager::expand_location() method.
///
/// The instance of @ref location_manager that you want is
/// accessible from the instance of @ref translation_unit that the
/// current instance of @ref decl_base belongs to, via a call to
/// translation_unit::get_loc_mgr().
///
/// @return the location of the current instance of @ref decl_base.
const location&
decl_base::get_location() const
{return priv_->location_;}

/// Set the location for a given declaration.
///
/// The location is an abstraction for the tripplet {file path,
/// line, column} that defines where the declaration appeared in the
/// source code.
///
/// To create a location from a tripplet {file path, line, column},
/// you need to use the method @ref
/// location_manager::create_new_location().
///
/// The instance of @ref location_manager that you want is
/// accessible from the instance of @ref translation_unit that the
/// current instance of @ref decl_base belongs to, via a call to
/// translation_unit::get_loc_mgr().
void
decl_base::set_location(const location& l)
{priv_->location_ = l;}

/// Setter for the name of the decl.
///
/// @param n the new name to set.
void
decl_base::set_name(const string& n)
{
  priv_->name_ = get_environment()->intern(n);
  priv_->is_anonymous_ = n.empty();
}

/// Test if the current declaration is anonymous.
///
/// Being anonymous means that the declaration was created without a
/// name.  This can usually happen for enum or struct types.
///
/// @return true iff the type is anonymous.
bool
decl_base::get_is_anonymous() const
{return priv_->is_anonymous_;}

/// Set the "is_anonymous" flag of the current declaration.
///
/// Being anonymous means that the declaration was created without a
/// name.  This can usually happen for enum or struct types.
///
/// @param f the new value of the flag.
void
decl_base::set_is_anonymous(bool f)
{priv_->is_anonymous_ = f;}

/// Getter of the flag that says if the declaration is artificial.
///
/// Being artificial means the parameter was not explicitely
/// mentionned in the source code, but was rather artificially created
/// by the compiler.
///
/// @return true iff the declaration is artificial.
bool
decl_base::get_is_artificial() const
{return priv_->is_artificial_;}

/// Setter of the flag that says if the declaration is artificial.
///
/// Being artificial means the parameter was not explicitely
/// mentionned in the source code, but was rather artificially created
/// by the compiler.
///
/// @param f the new value of the flag that says if the declaration is
/// artificial.
void
decl_base::set_is_artificial(bool f)
{priv_->is_artificial_ = f;}

/// Get the "has_anonymous_parent" flag of the current declaration.
///
/// Having an anoymous parent means having a anonymous parent scope
/// (containing type or namespace) which is either direct or indirect.
///
/// @return true iff the current decl has a direct or indirect scope
/// which is anonymous.
bool
decl_base::get_has_anonymous_parent() const
{return priv_->has_anonymous_parent_;}

/// Set the "has_anonymous_parent" flag of the current declaration.
///
/// Having an anonymous parent means having a anonymous parent scope
/// (containing type or namespace) which is either direct or indirect.
///
/// @param f set the flag which says if the current decl has a direct
/// or indirect scope which is anonymous.
void
decl_base::set_has_anonymous_parent(bool f) const
{priv_->has_anonymous_parent_ = f;}

/// @return the logical "OR" of decl_base::get_is_anonymous() and
/// decl_base::get_has_anonymous_parent().
bool
decl_base::get_is_anonymous_or_has_anonymous_parent() const
{return get_is_anonymous() || get_has_anonymous_parent();}

/// Getter for the mangled name.
///
/// @return the new mangled name.
const interned_string&
decl_base::get_linkage_name() const
{return priv_->linkage_name_;}

/// Setter for the linkage name.
///
/// @param m the new linkage name.
void
decl_base::set_linkage_name(const string& m)
{
  const environment* env = get_environment();
  ABG_ASSERT(env);
  priv_->linkage_name_ = env->intern(m);
}

/// Getter for the visibility of the decl.
///
/// @return the new visibility.
decl_base::visibility
decl_base::get_visibility() const
{return priv_->visibility_;}

/// Setter for the visibility of the decl.
///
/// @param v the new visibility.
void
decl_base::set_visibility(visibility v)
{priv_->visibility_ = v;}

/// Return the type containing the current decl, if any.
///
/// @return the type that contains the current decl, or NULL if there
/// is none.
scope_decl*
decl_base::get_scope() const
{
  if (priv_->context_)
    return priv_->context_->get_scope();
  return 0;
}

/// Return a copy of the qualified name of the parent of the current
/// decl.
///
/// @return the newly-built qualified name of the of the current decl.
const interned_string&
decl_base::get_qualified_parent_name() const
{return priv_->qualified_parent_name_;}

/// Getter for the name of the current decl.
///
/// @return the name of the current decl.
const interned_string&
decl_base::get_name() const
{return priv_->name_;}

/// Compute the qualified name of the decl.
///
/// @param qn the resulting qualified name.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
void
decl_base::get_qualified_name(interned_string& qn, bool internal) const
{qn = get_qualified_name(internal);}

/// Get the pretty representatin of the current declaration.
///
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @param qualified_name if true, names emitted in the pretty
/// representation are fully qualified.
///
/// @return the default pretty representation for a decl.  This is
/// basically the fully qualified name of the decl optionally prefixed
/// with a meaningful string to add context for the user.
string
decl_base::get_pretty_representation(bool internal,
				     bool qualified_name) const
{
  if (qualified_name)
    return get_qualified_name(internal);
  return get_name();
}

/// Return the qualified name of the decl.
///
/// This is the fully qualified name of the decl.  It's made of the
/// concatenation of the name of the decl with the qualified name of
/// its scope.
///
/// Note that the value returned by this function is computed by @ref
/// update_qualified_name when the decl is added to its scope.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the resulting qualified name.
const interned_string&
decl_base::get_qualified_name(bool /*internal*/) const
{return priv_->qualified_name_;}

/// Return the scoped name of the decl.
///
/// This is made of the concatenation of the name of the decl with the
/// name of its scope.  It doesn't contain the qualified name of its
/// scope, unlike what is returned by decl_base::get_qualified_name.
///
/// Note that the value returned by this function is computed by @ref
/// update_qualified_name when the decl is added to its scope.
///
/// @return the scoped name of the decl.
const interned_string&
decl_base::get_scoped_name() const
{return priv_->scoped_name_;}

/// If this @ref decl_base is a definition, get its earlier
/// declaration.
///
/// @return the earlier declaration of the class, if any.
const decl_base_sptr
decl_base::get_earlier_declaration() const
{return priv_->declaration_;}

/// set the earlier declaration of this @ref decl_base definition.
///
/// @param d the earlier declaration to set.  Note that it's set only
/// if it's a pure declaration.
void
decl_base::set_earlier_declaration(const decl_base_sptr& d)
{
  if (d && d->get_is_declaration_only())
    priv_->declaration_ = d;
}


/// If this @ref decl_base is declaration-only, get its definition, if
/// any.
///
/// @return the definition of this decl-only @ref decl_base.
const decl_base_sptr
decl_base::get_definition_of_declaration() const
{return priv_->definition_of_declaration_.lock();}

///  If this @ref decl_base is declaration-only, get its definition,
///  if any.
///
/// Note that this function doesn't return a smart pointer, but rather
/// the underlying pointer managed by the smart pointer.  So it's as
/// fast as possible.  This getter is to be used in code paths that
/// are proven to be performance hot spots; especially, when comparing
/// sensitive types like enums, classes or unions.  Those are compared
/// extremely frequently and thus, their access to the definition of
/// declaration must be fast.
///
/// @return the definition of the declaration.
const decl_base*
decl_base::get_naked_definition_of_declaration() const
{return priv_->naked_definition_of_declaration_;}

/// Test if a @ref decl_base is a declaration-only decl.
///
/// @return true iff the current @ref decl_base is declaration-only.
bool
decl_base::get_is_declaration_only() const
{return priv_->is_declaration_only_;}

/// Set a flag saying if the @ref enum_type_decl is a declaration-only
/// @ref enum_type_decl.
///
/// @param f true if the @ref enum_type_decl is a declaration-only
/// @ref enum_type_decl.
void
decl_base::set_is_declaration_only(bool f)
{
  bool update_types_lookup_map = !f && priv_->is_declaration_only_;

  priv_->is_declaration_only_ = f;

  if (update_types_lookup_map)
    if (scope_decl* s = get_scope())
      {
	scope_decl::declarations::iterator i;
	if (s->find_iterator_for_member(this, i))
	  maybe_update_types_lookup_map(*i);
	else
	  ABG_ASSERT_NOT_REACHED;
      }
}

change_kind
operator|(change_kind l, change_kind r)
{
  return static_cast<change_kind>(static_cast<unsigned>(l)
				  | static_cast<unsigned>(r));
}

change_kind
operator&(change_kind l, change_kind r)
{
  return static_cast<change_kind>(static_cast<unsigned>(l)
				  & static_cast<unsigned>(r));
}

change_kind&
operator|=(change_kind& l, change_kind r)
{
  l = l | r;
  return l;
}

change_kind&
operator&=(change_kind& l, change_kind r)
{
  l = l & r;
  return l;
}

/// Compares two instances of @ref decl_base.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff it's non-null and if the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const decl_base& l, const decl_base& r, change_kind* k)
{
  bool result = true;
  const interned_string &l_linkage_name = l.get_linkage_name();
  const interned_string &r_linkage_name = r.get_linkage_name();
  if (!l_linkage_name.empty() && !r_linkage_name.empty())
    {
      if (l_linkage_name != r_linkage_name)
	{
	  // Linkage names are different.  That usually means the two
	  // decls are different, unless we are looking at two
	  // function declarations which have two different symbols
	  // that are aliases of each other.
	  const function_decl *f1 = is_function_decl(&l),
	    *f2 = is_function_decl(&r);
	  if (f1 && f2 && function_decls_alias(*f1, *f2))
	    ;// The two functions are aliases, so they are not different.
	  else
	    {
	      result = false;
	      if (k)
		*k |= LOCAL_NON_TYPE_CHANGE_KIND;
	      else
		return false;
	    }
	}
    }

  // This is the name of the decls that we want to compare.
  interned_string ln = l.get_qualified_name(), rn = r.get_qualified_name();

  /// If both of the current decls have an anonymous scope then let's
  /// compare their name component by component by properly handling
  /// anonymous scopes. That's the slow path.
  ///
  /// Otherwise, let's just compare their name, the obvious way.
  /// That's the fast path because in that case the names are
  /// interned_string and comparing them is much faster.
  bool decls_are_same = (ln == rn);
  if (!decls_are_same
      && l.get_is_anonymous()
      && !l.get_has_anonymous_parent()
      && r.get_is_anonymous()
      && !r.get_has_anonymous_parent()
      && (l.get_qualified_parent_name() == r.get_qualified_parent_name()))
    // Both decls are anonymous and their scope are *NOT* anonymous.
    // So we consider the decls to have equivalent names (both
    // anonymous, remember).  We are still in the fast path here.
    decls_are_same = true;

  if (!decls_are_same
      && l.get_has_anonymous_parent()
      && r.get_has_anonymous_parent())
    // This is the slow path as we are comparing the decl qualified
    // names component by component, properly handling anonymous
    // scopes.
    decls_are_same = tools_utils::decl_names_equal(ln, rn);

  if (!decls_are_same)
    {
      result = false;
      if (k)
	*k |= LOCAL_NON_TYPE_CHANGE_KIND;
      else
	return false;
    }

  if (is_member_decl(l) && is_member_decl(r))
    {
      context_rel* r1 = const_cast<context_rel*>(l.get_context_rel());
      context_rel *r2 = const_cast<context_rel*>(r.get_context_rel());

      access_specifier la = no_access, ra = no_access;
      bool member_types_or_functions =
	((is_type(l) && is_type(r))
	 || (is_function_decl(l) && is_function_decl(r)));

      if (member_types_or_functions)
	{
	  // Access specifiers on member types in DWARF is not
	  // reliable; in the same DSO, the same struct can be either
	  // a class or a struct, and the access specifiers of its
	  // member types are not necessarily given, so they
	  // effectively can be considered differently, again, in the
	  // same DSO.  So, here, let's avoid considering those!
	  // during comparison.
	  la = r1->get_access_specifier();
	  ra = r2->get_access_specifier();
	  r1->set_access_specifier(no_access);
	  r2->set_access_specifier(no_access);
	}

      bool rels_are_different = *r1 != *r2;

      if (member_types_or_functions)
	{
	  // restore the access specifiers.
	  r1->set_access_specifier(la);
	  r2->set_access_specifier(ra);
	}

      if (rels_are_different)
	{
	  result = false;
	  if (k)
	    *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	  else
	    return false;
	}
    }

  return result;
}

/// Return true iff the two decls have the same name.
///
/// This function doesn't test if the scopes of the the two decls are
/// equal.
///
/// Note that this virtual function is to be implemented by classes
/// that extend the \p decl_base class.
bool
decl_base::operator==(const decl_base& other) const
{return equals(*this, other, 0);}

/// Inequality operator.
///
/// @param other to other instance of @ref decl_base to compare the
/// current instance to.
///
/// @return true iff the current instance of @ref decl_base is
/// different from @p other.
bool
decl_base::operator!=(const decl_base& other) const
{return !operator==(other);}

/// Destructor of the @ref decl_base type.
decl_base::~decl_base()
{delete priv_;}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the member nodes of the translation
/// unit during the traversal.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
decl_base::traverse(ir_node_visitor&)
{
  // Do nothing in the base class.
  return true;
}

/// Setter of the scope of the current decl.
///
/// Note that the decl won't hold a reference on the scope.  It's
/// rather the scope that holds a reference on its members.
void
decl_base::set_scope(scope_decl* scope)
{
  if (!priv_->context_)
    priv_->context_ = new context_rel(scope);
  else
    priv_->context_->set_scope(scope);
}

// </decl_base definition>

/// Streaming operator for the decl_base::visibility.
///
/// @param o the output stream to serialize the visibility to.
///
/// @param v the visibility to serialize.
///
/// @return the output stream.
std::ostream&
operator<<(std::ostream& o, decl_base::visibility v)
{
  string r;
  switch (v)
    {
    case decl_base::VISIBILITY_NONE:
      r = "none";
      break;
    case decl_base::VISIBILITY_DEFAULT:
      r = "default";
      break;
    case decl_base::VISIBILITY_PROTECTED:
      r = "protected";
      break;
    case decl_base::VISIBILITY_HIDDEN:
      r = "hidden";
      break;
    case decl_base::VISIBILITY_INTERNAL:
      r = "internal";
      break;
    }
  return o;
}

/// Streaming operator for decl_base::binding.
///
/// @param o the output stream to serialize the visibility to.
///
/// @param b the binding to serialize.
///
/// @return the output stream.
std::ostream&
operator<<(std::ostream& o, decl_base::binding b)
{
  string r;
  switch (b)
    {
    case decl_base::BINDING_NONE:
      r = "none";
      break;
    case decl_base::BINDING_LOCAL:
      r = "local";
      break;
    case decl_base::BINDING_GLOBAL:
      r = "global";
      break;
    case decl_base::BINDING_WEAK:
      r = "weak";
      break;
    }
  o << r;
  return o;
}

/// Turn equality of shared_ptr of decl_base into a deep equality;
/// that is, make it compare the pointed to objects, not just the
/// pointers.
///
/// @param l the shared_ptr of decl_base on left-hand-side of the
/// equality.
///
/// @param r the shared_ptr of decl_base on right-hand-side of the
/// equality.
///
/// @return true if the decl_base pointed to by the shared_ptrs are
/// equal, false otherwise.
bool
operator==(const decl_base_sptr& l, const decl_base_sptr& r)
{
  if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// Inequality operator of shared_ptr of @ref decl_base.
///
/// This is a deep equality operator, that is, it compares the
/// pointed-to objects, rather than just the pointers.
///
/// @param l the left-hand-side operand.
///
/// @param r the right-hand-side operand.
///
/// @return true iff @p l is different from @p r.
bool
operator!=(const decl_base_sptr& l, const decl_base_sptr& r)
{return !operator==(l, r);}

/// Turn equality of shared_ptr of type_base into a deep equality;
/// that is, make it compare the pointed to objects too.
///
/// @param l the shared_ptr of type_base on left-hand-side of the
/// equality.
///
/// @param r the shared_ptr of type_base on right-hand-side of the
/// equality.
///
/// @return true if the type_base pointed to by the shared_ptrs are
/// equal, false otherwise.
bool
operator==(const type_base_sptr& l, const type_base_sptr& r)
{
    if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// Turn inequality of shared_ptr of type_base into a deep equality;
/// that is, make it compare the pointed to objects..
///
/// @param l the shared_ptr of type_base on left-hand-side of the
/// equality.
///
/// @param r the shared_ptr of type_base on right-hand-side of the
/// equality.
///
/// @return true iff the type_base pointed to by the shared_ptrs are
/// different.
bool
operator!=(const type_base_sptr& l, const type_base_sptr& r)
{return !operator==(l, r);}

/// Tests if a declaration has got a scope.
///
/// @param d the declaration to consider.
///
/// @return true if the declaration has got a scope, false otherwise.
bool
has_scope(const decl_base& d)
{return (d.get_scope());}

/// Tests if a declaration has got a scope.
///
/// @param d the declaration to consider.
///
/// @return true if the declaration has got a scope, false otherwise.
bool
has_scope(const decl_base_sptr d)
{return has_scope(*d.get());}

/// Tests if a declaration is a class member.
///
/// @param d the declaration to consider.
///
/// @return true if @p d is a class member, false otherwise.
bool
is_member_decl(const decl_base_sptr d)
{return is_at_class_scope(d) || is_method_decl(d);}

/// Tests if a declaration is a class member.
///
/// @param d the declaration to consider.
///
/// @return true if @p d is a class member, false otherwise.
bool
is_member_decl(const decl_base* d)
{return is_at_class_scope(d) || is_method_decl(d);}

/// Tests if a declaration is a class member.
///
/// @param d the declaration to consider.
///
/// @return true if @p d is a class member, false otherwise.
bool
is_member_decl(const decl_base& d)
{return is_at_class_scope(d) || is_method_decl(d);}

/// Test if a declaration is a @ref scope_decl.
///
/// @param d the declaration to take in account.
///
/// @return the a pointer to the @ref scope_decl sub-object of @p d,
/// if d is a @ref scope_decl.
scope_decl*
is_scope_decl(decl_base* d)
{return dynamic_cast<scope_decl*>(d);}

/// Test if a declaration is a @ref scope_decl.
///
/// @param d the declaration to take in account.
///
/// @return the a pointer to the @ref scope_decl sub-object of @p d,
/// if d is a @ref scope_decl.
scope_decl_sptr
is_scope_decl(const decl_base_sptr& d)
{return dynamic_pointer_cast<scope_decl>(d);}

/// Tests if a type is a class member.
///
/// @param t the type to consider.
///
/// @return true if @p t is a class member type, false otherwise.
bool
is_member_type(const type_base_sptr& t)
{
  decl_base_sptr d = get_type_declaration(t);
  return is_member_decl(d);
}

/// Test if a type is user-defined.
///
/// A type is considered user-defined if it's a
/// struct/class/union/enum that is *NOT* artificial.
///
/// @param t the type to consider.
///
/// @return true iff the type @p t is user-defined.
bool
is_user_defined_type(const type_base* t)
{
  if (t == 0)
    return false;

  t = peel_qualified_or_typedef_type(t);
  decl_base *d = is_decl(t);

  if ((is_class_or_union_type(t) || is_enum_type(t))
      && d && !d->get_is_artificial())
    return true;

  return false;
}

/// Test if a type is user-defined.
///
/// A type is considered user-defined if it's a
/// struct/class/union/enum.
///
///
/// @param t the type to consider.
///
/// @return true iff the type @p t is user-defined.
bool
is_user_defined_type(const type_base_sptr& t)
{return is_user_defined_type(t.get());}

/// Gets the access specifier for a class member.
///
/// @param d the declaration of the class member to consider.  Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @return the access specifier for the class member @p d.
access_specifier
get_member_access_specifier(const decl_base& d)
{
  ABG_ASSERT(is_member_decl(d));

  const context_rel* c = d.get_context_rel();
  ABG_ASSERT(c);

  return c->get_access_specifier();
}

/// Gets the access specifier for a class member.
///
/// @param d the declaration of the class member to consider.  Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @return the access specifier for the class member @p d.
access_specifier
get_member_access_specifier(const decl_base_sptr& d)
{return get_member_access_specifier(*d);}

/// Sets the access specifier for a class member.
///
/// @param d the class member to set the access specifier for.  Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @param a the new access specifier to set the class member to.
void
set_member_access_specifier(decl_base& d,
			    access_specifier a)
{
  ABG_ASSERT(is_member_decl(d));

  context_rel* c = d.get_context_rel();
  ABG_ASSERT(c);

  c->set_access_specifier(a);
}

/// Sets the access specifier for a class member.
///
/// @param d the class member to set the access specifier for.  Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @param a the new access specifier to set the class member to.
void
set_member_access_specifier(const decl_base_sptr& d,
			    access_specifier a)
{set_member_access_specifier(*d, a);}

/// Gets a flag saying if a class member is static or not.
///
/// @param d the declaration for the class member to consider. Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @return true if the class member @p d is static, false otherwise.
bool
get_member_is_static(const decl_base&d)
{
  ABG_ASSERT(is_member_decl(d));

  const context_rel* c = d.get_context_rel();
  ABG_ASSERT(c);

  return c->get_is_static();
}

/// Gets a flag saying if a class member is static or not.
///
/// @param d the declaration for the class member to consider. Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @return true if the class member @p d is static, false otherwise.
bool
get_member_is_static(const decl_base* d)
{return get_member_is_static(*d);}

/// Gets a flag saying if a class member is static or not.
///
/// @param d the declaration for the class member to consider.  Note
/// that this must be a class member otherwise the function aborts the
/// current process.
///
/// @return true if the class member @p d is static, false otherwise.
bool
get_member_is_static(const decl_base_sptr& d)
{return get_member_is_static(*d);}

/// Test if a var_decl is a data member.
///
/// @param v the var_decl to consider.
///
/// @return true if @p v is data member, false otherwise.
bool
is_data_member(const var_decl& v)
{return is_at_class_scope(v);}

/// Test if a var_decl is a data member.
///
/// @param v the var_decl to consider.
///
/// @return true if @p v is data member, false otherwise.
bool
is_data_member(const var_decl* v)
{return is_data_member(*v);}

/// Test if a var_decl is a data member.
///
/// @param v the var_decl to consider.
///
/// @return true if @p v is data member, false otherwise.
bool
is_data_member(const var_decl_sptr d)
{return is_at_class_scope(d);}

/// Test if a decl is a data member.
///
/// @param d the decl to consider.
///
/// @return a pointer to the data member iff @p d is a data member, or
/// a null pointer.
var_decl_sptr
is_data_member(const decl_base_sptr& d)
{
  if (var_decl_sptr v = is_var_decl(d))
    {
      if (is_data_member(v))
	return v;
    }
  return var_decl_sptr();
}

/// Test if a decl is a data member.
///
/// @param d the decl to consider.
///
/// @return a pointer to the data member iff @p d is a data member, or
/// a null pointer.
var_decl_sptr
is_data_member(const type_or_decl_base_sptr& d)
{
  if (var_decl_sptr v = is_var_decl(d))
    {
      if (is_data_member(v))
	return v;
    }
  return var_decl_sptr();
}

/// Test if a decl is a data member.
///
/// @param d the decl to consider.
///
/// @return a pointer to the data member iff @p d is a data member, or
/// a null pointer.
var_decl*
is_data_member(const type_or_decl_base* d)
{
 if (var_decl *v = is_var_decl(d))
    if (is_data_member(v))
      return v;
  return 0;
}

/// Test if a decl is a data member.
///
/// @param d the decl to consider.
///
/// @return a pointer to the data member iff @p d is a data member, or
/// a null pointer.
var_decl*
is_data_member(const decl_base *d)
{
  if (var_decl *v = is_var_decl(d))
    if (is_data_member(v))
      return v;
  return 0;
}

/// Get the first non-anonymous data member of a given anonymous data
/// member.
///
/// E.g:
///
///   struct S
///   {
///     union // <-- for this anonymous data member, the function
///           // returns a.
///     {
///       int a;
///       charb;
///     };
///   };
///
/// @return anon_dm the anonymous data member to consider.
///
/// @return the first non-anonymous data member of @p anon_dm.  If no
/// data member was found then this function returns @p anon_dm.
const var_decl_sptr
get_first_non_anonymous_data_member(const var_decl_sptr anon_dm)
{
  if (!anon_dm || !is_anonymous_data_member(anon_dm))
    return anon_dm;

  class_or_union_sptr klass = anonymous_data_member_to_class_or_union(anon_dm);
 var_decl_sptr first = *klass->get_non_static_data_members().begin();

 if (is_anonymous_data_member(first))
   return get_first_non_anonymous_data_member(first);

 return first;
}

/// In the context of a given class or union, this function returns
/// the data member that is located after a given data member.
///
/// @param klass the class or union to consider.
///
/// @param the data member to consider.
///
/// @return the data member that is located right after @p
/// data_member.
const var_decl_sptr
get_next_data_member(const class_or_union_sptr &klass,
		     const var_decl_sptr &data_member)
{
  if (!klass ||!data_member)
    return var_decl_sptr();

  for (class_or_union::data_members::const_iterator it =
	 klass->get_non_static_data_members().begin();
       it != klass->get_non_static_data_members().end();
       ++it)
    if (**it == *data_member)
      {
	++it;
	if (it != klass->get_non_static_data_members().end())
	  return get_first_non_anonymous_data_member(*it);
	break;
      }

  return var_decl_sptr();
}

/// Test if a decl is an anonymous data member.
///
/// @param d the decl to consider.
///
/// @return true iff @p d is an anonymous data member.
bool
is_anonymous_data_member(const decl_base& d)
{return is_anonymous_data_member(&d);}

/// Test if a decl is an anonymous data member.
///
/// @param d the decl to consider.
///
/// @return the var_decl representing the data member iff @p d is an
/// anonymous data member.
const var_decl*
is_anonymous_data_member(const type_or_decl_base* d)
{
  if (const var_decl* v = is_data_member(d))
    {
      if (is_anonymous_data_member(v))
	return v;
    }
  return 0;
}

/// Test if a decl is an anonymous data member.
///
/// @param d the decl to consider.
///
/// @return a non-nil pointer to the @ref var_decl denoted by @p d if
/// it's an anonymous data member.  Otherwise returns a nil pointer.
const var_decl*
is_anonymous_data_member(const decl_base* d)
{
  if (const var_decl* v = is_data_member(d))
    {
      if (is_anonymous_data_member(v))
	return v;
    }
  return 0;
}

/// Test if a decl is an anonymous data member.
///
/// @param d the decl to consider.
///
/// @return a non-nil pointer to the @ref var_decl denoted by @p d if
/// it's an anonymous data member.  Otherwise returns a nil pointer.
var_decl_sptr
is_anonymous_data_member(const type_or_decl_base_sptr& d)
{
  if (var_decl_sptr v = is_data_member(d))
    {
      if (is_anonymous_data_member(v))
	return v;
    }
  return var_decl_sptr();
}

/// Test if a decl is an anonymous data member.
///
/// @param d the decl to consider.
///
/// @return a non-nil pointer to the @ref var_decl denoted by @p d if
/// it's an anonymous data member.  Otherwise returns a nil pointer.
var_decl_sptr
is_anonymous_data_member(const decl_base_sptr& d)
{
  if (var_decl_sptr v = is_data_member(d))
    return is_anonymous_data_member(v);
  return var_decl_sptr();
}

/// Test if a @ref var_decl is an anonymous data member.
///
/// @param d the @ref var_decl to consider.
///
/// @return a non-nil pointer to the @ref var_decl denoted by @p d if
/// it's an anonymous data member.  Otherwise returns a nil pointer.
var_decl_sptr
is_anonymous_data_member(const var_decl_sptr& d)
{
  if (is_anonymous_data_member(d.get()))
    return d;
  return var_decl_sptr();
}

/// Test if a @ref var_decl is an anonymous data member.
///
/// @param d the @ref var_decl to consider.
///
/// @return a non-nil pointer to the @ref var_decl denoted by @p d if
/// it's an anonymous data member.  Otherwise returns a nil pointer.
const var_decl*
is_anonymous_data_member(const var_decl* d)
{
  if (d && is_anonymous_data_member(*d))
    return d;
  return 0;
}

/// Test if a @ref var_decl is an anonymous data member.
///
/// @param d the @ref var_decl to consider.
///
/// @return true iff @p d is an anonymous data member.
bool
is_anonymous_data_member(const var_decl& d)
{
  return (is_data_member(d)
	  && d.get_name().empty()
	  && is_class_or_union_type(d.get_type()));
}

/// Get the @ref class_or_union type of a given anonymous data member.
///
/// @param d the anonymous data member to consider.
///
/// @return the @ref class_or_union type of the anonymous data member
/// @p d.
class_or_union*
anonymous_data_member_to_class_or_union(const var_decl* d)
{
  if ((d = is_anonymous_data_member(d)))
    return is_class_or_union_type(d->get_type().get());
  return 0;
}

/// Test if a data member has annonymous type or not.
///
/// @param d the data member to consider.
///
/// @return the anonymous class or union type iff @p turns out to have
/// an anonymous type.  Otherwise, returns nil.
const class_or_union_sptr
data_member_has_anonymous_type(const var_decl& d)
{
  if (is_data_member(d))
    if (const class_or_union_sptr cou = is_class_or_union_type(d.get_type()))
      if (cou->get_is_anonymous())
	return cou;

  return class_or_union_sptr();
}

/// Test if a data member has annonymous type or not.
///
/// @param d the data member to consider.
///
/// @return the anonymous class or union type iff @p turns out to have
/// an anonymous type.  Otherwise, returns nil.
const class_or_union_sptr
data_member_has_anonymous_type(const var_decl* d)
{
  if (d)
    return data_member_has_anonymous_type(*d);
  return class_or_union_sptr();
}

/// Test if a data member has annonymous type or not.
///
/// @param d the data member to consider.
///
/// @return the anonymous class or union type iff @p turns out to have
/// an anonymous type.  Otherwise, returns nil.
const class_or_union_sptr
data_member_has_anonymous_type(const var_decl_sptr& d)
{return data_member_has_anonymous_type(d.get());}

/// Get the @ref class_or_union type of a given anonymous data member.
///
/// @param d the anonymous data member to consider.
///
/// @return the @ref class_or_union type of the anonymous data member
/// @p d.
class_or_union_sptr
anonymous_data_member_to_class_or_union(const var_decl_sptr &d)
{
  if (var_decl_sptr v = is_anonymous_data_member(d))
    return is_class_or_union_type(v->get_type());
  return class_or_union_sptr();
}

/// Set the offset of a data member into its containing class.
///
/// @param m the data member to consider.
///
/// @param o the offset, in bits.
void
set_data_member_offset(var_decl_sptr m, uint64_t o)
{
  ABG_ASSERT(is_data_member(m));

  dm_context_rel* ctxt_rel =
    dynamic_cast<dm_context_rel*>(m->get_context_rel());
  ABG_ASSERT(ctxt_rel);

  ctxt_rel->set_offset_in_bits(o);
}

/// Get the offset of a data member.
///
/// @param m the data member to consider.
///
/// @return the offset (in bits) of @p m in its containing class.
uint64_t
get_data_member_offset(const var_decl& m)
{
  ABG_ASSERT(is_data_member(m));
  const dm_context_rel* ctxt_rel =
    dynamic_cast<const dm_context_rel*>(m.get_context_rel());
  ABG_ASSERT(ctxt_rel);
  return ctxt_rel->get_offset_in_bits();
}

/// Get the offset of a data member.
///
/// @param m the data member to consider.
///
/// @return the offset (in bits) of @p m in its containing class.
uint64_t
get_data_member_offset(const var_decl_sptr m)
{return get_data_member_offset(*m);}

/// Get the offset of a data member.
///
/// @param m the data member to consider.
///
/// @return the offset (in bits) of @p m in its containing class.
uint64_t
get_data_member_offset(const decl_base_sptr d)
{return get_data_member_offset(dynamic_pointer_cast<var_decl>(d));}

/// Get the absolute offset of a data member.
///
/// If the data member is part of an anonymous data member then this
/// returns the absolute offset -- relative to the beginning of the
/// containing class of the anonymous data member.
///
/// @param m the data member to consider.
///
/// @return the aboslute offset of the data member @p m.
uint64_t
get_absolute_data_member_offset(const var_decl& m)
{
  ABG_ASSERT(is_data_member(m));
  const dm_context_rel* ctxt_rel =
    dynamic_cast<const dm_context_rel*>(m.get_context_rel());
  ABG_ASSERT(ctxt_rel);

  const var_decl *containing_anonymous_data_member =
    ctxt_rel->get_anonymous_data_member();

  uint64_t containing_anonymous_data_member_offset = 0;
  if (containing_anonymous_data_member)
    containing_anonymous_data_member_offset =
      get_absolute_data_member_offset(*containing_anonymous_data_member);

  return (ctxt_rel->get_offset_in_bits()
	  +
	  containing_anonymous_data_member_offset);
}

/// Get the absolute offset of a data member.
///
/// If the data member is part of an anonymous data member then this
/// returns the absolute offset -- relative to the beginning of the
/// containing class of the anonymous data member.
///
/// @param m the data member to consider.
///
/// @return the aboslute offset of the data member @p m.
uint64_t
get_absolute_data_member_offset(const var_decl_sptr& m)
{
  if (!m)
    return 0;
  return get_absolute_data_member_offset(*m);
}

/// Get the size of a given variable.
///
/// @param v the variable to consider.
///
/// @return the size of variable @p v.
uint64_t
get_var_size_in_bits(const var_decl_sptr& v)
{
  type_base_sptr t = v->get_type();
  ABG_ASSERT(t);

  return t->get_size_in_bits();
}

/// Set a flag saying if a data member is laid out.
///
/// @param m the data member to consider.
///
/// @param l true if @p m is to be considered as laid out.
void
set_data_member_is_laid_out(var_decl_sptr m, bool l)
{
  ABG_ASSERT(is_data_member(m));
  dm_context_rel* ctxt_rel =
    dynamic_cast<dm_context_rel*>(m->get_context_rel());
  ctxt_rel->set_is_laid_out(l);
}

/// Test whether a data member is laid out.
///
/// @param m the data member to consider.
///
/// @return true if @p m is laid out, false otherwise.
bool
get_data_member_is_laid_out(const var_decl& m)
{
  ABG_ASSERT(is_data_member(m));
  const dm_context_rel* ctxt_rel =
    dynamic_cast<const dm_context_rel*>(m.get_context_rel());

  return ctxt_rel->get_is_laid_out();
}

/// Test whether a data member is laid out.
///
/// @param m the data member to consider.
///
/// @return true if @p m is laid out, false otherwise.
bool
get_data_member_is_laid_out(const var_decl_sptr m)
{return get_data_member_is_laid_out(*m);}

/// Test whether a function_decl is a member function.
///
/// @param f the function_decl to test.
///
/// @return true if @p f is a member function, false otherwise.
bool
is_member_function(const function_decl& f)
{return is_member_decl(f);}

/// Test whether a function_decl is a member function.
///
/// @param f the function_decl to test.
///
/// @return true if @p f is a member function, false otherwise.
bool
is_member_function(const function_decl* f)
{return is_member_decl(*f);}

/// Test whether a function_decl is a member function.
///
/// @param f the function_decl to test.
///
/// @return true if @p f is a member function, false otherwise.
bool
is_member_function(const function_decl_sptr& f)
{return is_member_decl(*f);}

/// Test whether a member function is a constructor.
///
/// @param f the member function to test.
///
/// @return true if @p f is a constructor, false otherwise.
bool
get_member_function_is_ctor(const function_decl& f)
{
  ABG_ASSERT(is_member_function(f));

  const method_decl* m = is_method_decl(&f);
  ABG_ASSERT(m);

  const mem_fn_context_rel* ctxt =
    dynamic_cast<const mem_fn_context_rel*>(m->get_context_rel());

  return ctxt->is_constructor();
}

/// Test whether a member function is a constructor.
///
/// @param f the member function to test.
///
/// @return true if @p f is a constructor, false otherwise.
bool
get_member_function_is_ctor(const function_decl_sptr& f)
{return get_member_function_is_ctor(*f);}


/// Setter for the is_ctor property of the member function.
///
/// @param f the member function to set.
///
/// @param f the new boolean value of the is_ctor property.  Is true
/// if @p f is a constructor, false otherwise.
void
set_member_function_is_ctor(function_decl& f, bool c)
{
  ABG_ASSERT(is_member_function(f));

  method_decl* m = is_method_decl(&f);
  ABG_ASSERT(m);

  mem_fn_context_rel* ctxt =
    dynamic_cast<mem_fn_context_rel*>(m->get_context_rel());

  ctxt->is_constructor(c);
}

/// Setter for the is_ctor property of the member function.
///
/// @param f the member function to set.
///
/// @param f the new boolean value of the is_ctor property.  Is true
/// if @p f is a constructor, false otherwise.
void
set_member_function_is_ctor(const function_decl_sptr& f, bool c)
{set_member_function_is_ctor(*f, c);}

/// Test whether a member function is a destructor.
///
/// @param f the function to test.
///
/// @return true if @p f is a destructor, false otherwise.
bool
get_member_function_is_dtor(const function_decl& f)
{
  ABG_ASSERT(is_member_function(f));

  const method_decl* m = is_method_decl(&f);
  ABG_ASSERT(m);

  const mem_fn_context_rel* ctxt =
    dynamic_cast<const mem_fn_context_rel*>(m->get_context_rel());

  return ctxt->is_destructor();
}

/// Test whether a member function is a destructor.
///
/// @param f the function to test.
///
/// @return true if @p f is a destructor, false otherwise.
bool
get_member_function_is_dtor(const function_decl_sptr& f)
{return get_member_function_is_dtor(*f);}

/// Set the destructor-ness property of a member function.
///
/// @param f the function to set.
///
/// @param d true if @p f is a destructor, false otherwise.
void
set_member_function_is_dtor(function_decl& f, bool d)
{
    ABG_ASSERT(is_member_function(f));

    method_decl* m = is_method_decl(&f);
  ABG_ASSERT(m);

  mem_fn_context_rel* ctxt =
    dynamic_cast<mem_fn_context_rel*>(m->get_context_rel());

  ctxt->is_destructor(d);
}

/// Set the destructor-ness property of a member function.
///
/// @param f the function to set.
///
/// @param d true if @p f is a destructor, false otherwise.
void
set_member_function_is_dtor(const function_decl_sptr& f, bool d)
{set_member_function_is_dtor(*f, d);}

/// Test whether a member function is const.
///
/// @param f the function to test.
///
/// @return true if @p f is const, false otherwise.
bool
get_member_function_is_const(const function_decl& f)
{
  ABG_ASSERT(is_member_function(f));

  const method_decl* m = is_method_decl(&f);
  ABG_ASSERT(m);

  const mem_fn_context_rel* ctxt =
    dynamic_cast<const mem_fn_context_rel*>(m->get_context_rel());

  return ctxt->is_const();
}

/// Test whether a member function is const.
///
/// @param f the function to test.
///
/// @return true if @p f is const, false otherwise.
bool
get_member_function_is_const(const function_decl_sptr& f)
{return get_member_function_is_const(*f);}

/// set the const-ness property of a member function.
///
/// @param f the function to set.
///
/// @param is_const the new value of the const-ness property of @p f
void
set_member_function_is_const(function_decl& f, bool is_const)
{
  ABG_ASSERT(is_member_function(f));

  method_decl* m = is_method_decl(&f);
  ABG_ASSERT(m);

  mem_fn_context_rel* ctxt =
    dynamic_cast<mem_fn_context_rel*>(m->get_context_rel());

  ctxt->is_const(is_const);
}

/// set the const-ness property of a member function.
///
/// @param f the function to set.
///
/// @param is_const the new value of the const-ness property of @p f
void
set_member_function_is_const(const function_decl_sptr& f, bool is_const)
{set_member_function_is_const(*f, is_const);}

/// Test if a virtual member function has a vtable offset set.
///
/// @param f the virtual member function to consider.
///
/// @return true iff the virtual member function has its vtable offset
/// set, i.e, if the vtable offset of @p is different from -1.
bool
member_function_has_vtable_offset(const function_decl& f)
{return get_member_function_vtable_offset(f) != -1;}

/// Get the vtable offset of a member function.
///
/// @param f the member function to consider.
///
/// @return the vtable offset of @p f.  Note that a vtable offset of
/// value -1 means that the member function does *NOT* yet have a
/// vtable offset associated to it.
ssize_t
get_member_function_vtable_offset(const function_decl& f)
{
  ABG_ASSERT(is_member_function(f));

  const method_decl* m =
    dynamic_cast<const method_decl*>(&f);
  ABG_ASSERT(m);

  const mem_fn_context_rel* ctxt =
    dynamic_cast<const mem_fn_context_rel*>(m->get_context_rel());

  return ctxt->vtable_offset();
}

/// Get the vtable offset of a member function.
///
/// @param f the member function to consider.
///
/// @return the vtable offset of @p f.  Note that a vtable offset of
/// value -1 means that the member function does *NOT* yet have a
/// vtable offset associated to it.
ssize_t
get_member_function_vtable_offset(const function_decl_sptr& f)
{return get_member_function_vtable_offset(*f);}

/// Set the vtable offset of a member function.
///
/// @param f the member function to consider.
///
/// @param s the new vtable offset.  Please note that a vtable offset
/// of value -1 means that the virtual member function does not (yet)
/// have any vtable offset associated to it.
void
set_member_function_vtable_offset(function_decl& f, ssize_t s)
{
  ABG_ASSERT(is_member_function(f));

  method_decl* m = is_method_decl(&f);
  ABG_ASSERT(m);

  mem_fn_context_rel* ctxt =
    dynamic_cast<mem_fn_context_rel*>(m->get_context_rel());

  ctxt->vtable_offset(s);
}

/// Get the vtable offset of a member function.
///
/// @param f the member function to consider.
///
/// @param s the new vtable offset.  Please note that a vtable offset
/// of value -1 means that the virtual member function does not (yet)
/// have any vtable offset associated to it.
void
set_member_function_vtable_offset(const function_decl_sptr& f, ssize_t s)
{return set_member_function_vtable_offset(*f, s);}

/// Test if a given member function is virtual.
///
/// @param mem_fn the member function to consider.
///
/// @return true iff a @p mem_fn is virtual.
bool
get_member_function_is_virtual(const function_decl& f)
{
  ABG_ASSERT(is_member_function(f));

  const method_decl* m =
    dynamic_cast<const method_decl*>(&f);
  ABG_ASSERT(m);

  const mem_fn_context_rel* ctxt =
    dynamic_cast<const mem_fn_context_rel*>(m->get_context_rel());

  return ctxt->is_virtual();
}

/// Test if a given member function is virtual.
///
/// @param mem_fn the member function to consider.
///
/// @return true iff a @p mem_fn is virtual.
bool
get_member_function_is_virtual(const function_decl_sptr& mem_fn)
{return mem_fn ? get_member_function_is_virtual(*mem_fn) : false;}

/// Test if a given member function is virtual.
///
/// @param mem_fn the member function to consider.
///
/// @return true iff a @p mem_fn is virtual.
bool
get_member_function_is_virtual(const function_decl* mem_fn)
{return mem_fn ? get_member_function_is_virtual(*mem_fn) : false;}

/// Set the virtual-ness of a member function.
///
/// @param f the member function to consider.
///
/// @param is_virtual set to true if the function is virtual.
void
set_member_function_is_virtual(function_decl& f, bool is_virtual)
{
  ABG_ASSERT(is_member_function(f));

  method_decl* m = is_method_decl(&f);
  ABG_ASSERT(m);

  mem_fn_context_rel* ctxt =
    dynamic_cast<mem_fn_context_rel*>(m->get_context_rel());

  ctxt->is_virtual(is_virtual);
}

/// Set the virtual-ness of a member function.
///
/// @param f the member function to consider.
///
/// @param is_virtual set to true if the function is virtual.
void
set_member_function_is_virtual(const function_decl_sptr& fn, bool is_virtual)
{
  if (fn)
    {
      set_member_function_is_virtual(*fn, is_virtual);
      fixup_virtual_member_function
	(dynamic_pointer_cast<method_decl>(fn));
    }
}

/// Recursively returns the the underlying type of a typedef.  The
/// return type should not be a typedef of anything anymore.
///
///
/// Also recursively strip typedefs from the sub-types of the type
/// given in arguments.
///
/// Note that this function builds types in which typedefs are
/// stripped off.  Usually, types are held by their scope, so their
/// life time is bound to the life time of their scope.  But as this
/// function cannot really insert the built type into it's scope, it
/// must ensure that the newly built type stays live long enough.
///
/// So, if the newly built type has a canonical type, this function
/// returns the canonical type.  Otherwise, this function ensure that
/// the newly built type has a life time that is the same as the life
/// time of the entire libabigail library.
///
/// @param type the type to strip the typedefs from.
///
/// @return the resulting type stripped from its typedefs, or just
/// return @p type if it has no typedef in any of its sub-types.
type_base_sptr
strip_typedef(const type_base_sptr type)
{
  if (!type)
    return type;

  // If type is a class type then do not try to strip typedefs from it.
  // And if it has no canonical type (which can mean that it's a
  // declaration-only class), then, make sure its live for ever and
  // return it.
  if (class_decl_sptr cl = is_class_type(type))
    {
      if (!cl->get_canonical_type())
	keep_type_alive(type);
      return type;
    }

  environment* env = type->get_environment();
  ABG_ASSERT(env);
  type_base_sptr t = type;

  if (const typedef_decl_sptr ty = is_typedef(t))
    t = strip_typedef(type_or_void(ty->get_underlying_type(), env));
  else if (const reference_type_def_sptr ty = is_reference_type(t))
    {
      type_base_sptr p = strip_typedef(type_or_void(ty->get_pointed_to_type(),
						    env));
      ABG_ASSERT(p);
      t.reset(new reference_type_def(p,
				     ty->is_lvalue(),
				     ty->get_size_in_bits(),
				     ty->get_alignment_in_bits(),
				     ty->get_location()));
    }
  else if (const pointer_type_def_sptr ty = is_pointer_type(t))
    {
      type_base_sptr p = strip_typedef(type_or_void(ty->get_pointed_to_type(),
						    env));
      ABG_ASSERT(p);
      t.reset(new pointer_type_def(p,
				   ty->get_size_in_bits(),
				   ty->get_alignment_in_bits(),
				   ty->get_location()));
    }
  else if (const qualified_type_def_sptr ty = is_qualified_type(t))
    {
      type_base_sptr p = strip_typedef(type_or_void(ty->get_underlying_type(),
						    env));
      ABG_ASSERT(p);
      t.reset(new qualified_type_def(p,
				     ty->get_cv_quals(),
				     ty->get_location()));
    }
  else if (const array_type_def_sptr ty = is_array_type(t))
    {
      type_base_sptr p = strip_typedef(ty->get_element_type());
      ABG_ASSERT(p);
      t.reset(new array_type_def(p, ty->get_subranges(), ty->get_location()));
    }
  else if (const method_type_sptr ty = is_method_type(t))
    {
      function_decl::parameters parm;
      for (function_decl::parameters::const_iterator i =
	     ty->get_parameters().begin();
	   i != ty->get_parameters().end();
	   ++i)
	{
	  function_decl::parameter_sptr p = *i;
	  type_base_sptr typ = strip_typedef(p->get_type());
	  ABG_ASSERT(typ);
	  function_decl::parameter_sptr stripped
	    (new function_decl::parameter(typ,
					  p->get_index(),
					  p->get_name(),
					  p->get_location(),
					  p->get_variadic_marker(),
					  p->get_is_artificial()));
	  parm.push_back(stripped);
	}
      type_base_sptr p = strip_typedef(ty->get_return_type());
      ABG_ASSERT(!!p == !!ty->get_return_type());
      t.reset(new method_type(p, ty->get_class_type(),
			      parm, ty->get_is_const(),
			      ty->get_size_in_bits(),
			      ty->get_alignment_in_bits()));
    }
  else if (const function_type_sptr ty = is_function_type(t))
    {
      function_decl::parameters parm;
      for (function_decl::parameters::const_iterator i =
	     ty->get_parameters().begin();
	   i != ty->get_parameters().end();
	   ++i)
	{
	  function_decl::parameter_sptr p = *i;
	  type_base_sptr typ = strip_typedef(p->get_type());
	  ABG_ASSERT(typ);
	  function_decl::parameter_sptr stripped
	    (new function_decl::parameter(typ,
					  p->get_index(),
					  p->get_name(),
					  p->get_location(),
					  p->get_variadic_marker(),
					  p->get_is_artificial()));
	  parm.push_back(stripped);
	}
      type_base_sptr p = strip_typedef(ty->get_return_type());
      ABG_ASSERT(!!p == !!ty->get_return_type());
      t.reset(new function_type(p, parm,
				ty->get_size_in_bits(),
				ty->get_alignment_in_bits()));
    }

  if (!t->get_environment())
    set_environment_for_artifact(t, env);

  if (!t->get_translation_unit())
    t->set_translation_unit(type->get_translation_unit());

  if (!(type->get_canonical_type() && canonicalize(t)))
    keep_type_alive(t);

  return t->get_canonical_type() ? t->get_canonical_type() : t;
}

/// Return the leaf underlying type node of a @ref typedef_decl node.
///
/// If the underlying type of a @ref typedef_decl node is itself a
/// @ref typedef_decl node, then recursively look at the underlying
/// type nodes to get the first one that is not a a @ref typedef_decl
/// node.  This is what a leaf underlying type node means.
///
/// Otherwise, if the underlying type node of @ref typedef_decl is
/// *NOT* a @ref typedef_decl node, then just return the underlying
/// type node.
///
/// And if the type node considered is not a @ref typedef_decl node,
/// then just return it.
///
/// @return the leaf underlying type node of a @p type.
type_base_sptr
peel_typedef_type(const type_base_sptr& type)
{
  typedef_decl_sptr t = is_typedef(type);
  if (!t)
    return type;

  if (is_typedef(t->get_underlying_type()))
    return peel_typedef_type(t->get_underlying_type());
  return t->get_underlying_type();
}

/// Return the leaf underlying type node of a @ref typedef_decl node.
///
/// If the underlying type of a @ref typedef_decl node is itself a
/// @ref typedef_decl node, then recursively look at the underlying
/// type nodes to get the first one that is not a a @ref typedef_decl
/// node.  This is what a leaf underlying type node means.
///
/// Otherwise, if the underlying type node of @ref typedef_decl is
/// *NOT* a @ref typedef_decl node, then just return the underlying
/// type node.
///
/// And if the type node considered is not a @ref typedef_decl node,
/// then just return it.
///
/// @return the leaf underlying type node of a @p type.
const type_base*
peel_typedef_type(const type_base* type)
{
  const typedef_decl* t = is_typedef(type);
  if (!t)
    return type;

  return peel_typedef_type(t->get_underlying_type()).get();
}

/// Return the leaf pointed-to type node of a @ref pointer_type_def
/// node.
///
/// If the pointed-to type of a @ref pointer_type_def node is itself a
/// @ref pointer_type_def node, then recursively look at the
/// pointed-to type nodes to get the first one that is not a a @ref
/// pointer_type_def node.  This is what a leaf pointed-to type node
/// means.
///
/// Otherwise, if the pointed-to type node of @ref pointer_type_def is
/// *NOT* a @ref pointer_type_def node, then just return the
/// pointed-to type node.
///
/// And if the type node considered is not a @ref pointer_type_def
/// node, then just return it.
///
/// @return the leaf pointed-to type node of a @p type.
type_base_sptr
peel_pointer_type(const type_base_sptr& type)
{
  pointer_type_def_sptr t = is_pointer_type(type);
  if (!t)
    return type;

  if (is_pointer_type(t->get_pointed_to_type()))
    return peel_pointer_type(t->get_pointed_to_type());
  return t->get_pointed_to_type();
}

/// Return the leaf pointed-to type node of a @ref pointer_type_def
/// node.
///
/// If the pointed-to type of a @ref pointer_type_def node is itself a
/// @ref pointer_type_def node, then recursively look at the
/// pointed-to type nodes to get the first one that is not a a @ref
/// pointer_type_def node.  This is what a leaf pointed-to type node
/// means.
///
/// Otherwise, if the pointed-to type node of @ref pointer_type_def is
/// *NOT* a @ref pointer_type_def node, then just return the
/// pointed-to type node.
///
/// And if the type node considered is not a @ref pointer_type_def
/// node, then just return it.
///
/// @return the leaf pointed-to type node of a @p type.
const type_base*
peel_pointer_type(const type_base* type)
{
  const pointer_type_def* t = is_pointer_type(type);
  if (!t)
    return type;

  return peel_pointer_type(t->get_pointed_to_type()).get();
}

/// Return the leaf pointed-to type node of a @ref reference_type_def
/// node.
///
/// If the pointed-to type of a @ref reference_type_def node is itself
/// a @ref reference_type_def node, then recursively look at the
/// pointed-to type nodes to get the first one that is not a a @ref
/// reference_type_def node.  This is what a leaf pointed-to type node
/// means.
///
/// Otherwise, if the pointed-to type node of @ref reference_type_def
/// is *NOT* a @ref reference_type_def node, then just return the
/// pointed-to type node.
///
/// And if the type node considered is not a @ref reference_type_def
/// node, then just return it.
///
/// @return the leaf pointed-to type node of a @p type.
type_base_sptr
peel_reference_type(const type_base_sptr& type)
{
  reference_type_def_sptr t = is_reference_type(type);
  if (!t)
    return type;

  if (is_reference_type(t->get_pointed_to_type()))
    return peel_reference_type(t->get_pointed_to_type());
  return t->get_pointed_to_type();
}

/// Return the leaf pointed-to type node of a @ref reference_type_def
/// node.
///
/// If the pointed-to type of a @ref reference_type_def node is itself
/// a @ref reference_type_def node, then recursively look at the
/// pointed-to type nodes to get the first one that is not a a @ref
/// reference_type_def node.  This is what a leaf pointed-to type node
/// means.
///
/// Otherwise, if the pointed-to type node of @ref reference_type_def
/// is *NOT* a @ref reference_type_def node, then just return the
/// pointed-to type node.
///
/// And if the type node considered is not a @ref reference_type_def
/// node, then just return it.
///
/// @return the leaf pointed-to type node of a @p type.
const type_base*
peel_reference_type(const type_base* type)
{
  const reference_type_def* t = is_reference_type(type);
  if (!t)
    return type;

  return peel_reference_type(t->get_pointed_to_type()).get();
}

/// Return the leaf element type of an array.
///
/// If the element type is itself an array, then recursively return
/// the element type of that array itself.
///
/// @param type the array type to consider.  If this is not an array
/// type, this type is returned by the function.
///
/// @return the leaf element type of the array @p type, or, if it's
/// not an array type, then just return @p.
const type_base_sptr
peel_array_type(const type_base_sptr& type)
{
  const array_type_def_sptr t = is_array_type(type);
  if (!t)
    return type;

  return peel_array_type(t->get_element_type());
}

/// Return the leaf element type of an array.
///
/// If the element type is itself an array, then recursively return
/// the element type of that array itself.
///
/// @param type the array type to consider.  If this is not an array
/// type, this type is returned by the function.
///
/// @return the leaf element type of the array @p type, or, if it's
/// not an array type, then just return @p.
const type_base*
peel_array_type(const type_base* type)
{
  const array_type_def* t = is_array_type(type);
  if (!t)
    return type;

  return peel_array_type(t->get_element_type()).get();
}

/// Return the leaf underlying type of a qualified type.
///
/// If the underlying type is itself a qualified type, then
/// recursively return the first underlying type of that qualified
/// type to return the first underlying type that is not a qualified type.
///
/// If the underlying type is NOT a qualified type, then just return
/// that underlying type.
///
/// @param type the qualified type to consider.
///
/// @return the leaf underlying type.
const type_base*
peel_qualified_type(const type_base* type)
{
  const qualified_type_def* t = is_qualified_type(type);
  if (!t)
    return type;

  return peel_qualified_type(t->get_underlying_type().get());
}

/// Return the leaf underlying type of a qualified type.
///
/// If the underlying type is itself a qualified type, then
/// recursively return the first underlying type of that qualified
/// type to return the first underlying type that is not a qualified type.
///
/// If the underlying type is NOT a qualified type, then just return
/// that underlying type.
///
/// @param type the qualified type to consider.
///
/// @return the leaf underlying type.
const type_base_sptr
peel_qualified_type(const type_base_sptr& type)
{
  const qualified_type_def_sptr t = is_qualified_type(type);
  if (!t)
    return type;

  return peel_qualified_type(t->get_underlying_type());
}

/// Return the leaf underlying type of a qualified or typedef type.
///
/// If the underlying type is itself a qualified or typedef type, then
/// recursively return the first underlying type of that qualified or
/// typedef type to return the first underlying type that is not a
/// qualified or typedef type.
///
/// If the underlying type is NOT a qualified nor a typedef type, then
/// just return that underlying type.
///
/// @param type the qualified or typedef type to consider.
///
/// @return the leaf underlying type.
type_base*
peel_qualified_or_typedef_type(const type_base* type)
{
  while (is_typedef(type) || is_qualified_type(type))
    {
      if (const typedef_decl* t = is_typedef(type))
	type = peel_typedef_type(t);

      if (const qualified_type_def* t = is_qualified_type(type))
	type = peel_qualified_type(t);
    }

  return const_cast<type_base*>(type);
}

/// Return the leaf underlying type of a qualified or typedef type.
///
/// If the underlying type is itself a qualified or typedef type, then
/// recursively return the first underlying type of that qualified or
/// typedef type to return the first underlying type that is not a
/// qualified or typedef type.
///
/// If the underlying type is NOT a qualified nor a typedef type, then
/// just return that underlying type.
///
/// @param type the qualified or typedef type to consider.
///
/// @return the leaf underlying type.
type_base_sptr
peel_qualified_or_typedef_type(const type_base_sptr &t)
{
  type_base_sptr type = t;
  while (is_typedef(type) || is_qualified_type(type))
    {
      if (typedef_decl_sptr t = is_typedef(type))
	type = peel_typedef_type(t);

      if (qualified_type_def_sptr t = is_qualified_type(type))
	type = peel_qualified_type(t);
    }

  return type;
}

/// Return the leaf underlying or pointed-to type node of a @ref
/// typedef_decl, @ref pointer_type_def or @ref reference_type_def
/// node.
///
/// @param type the type to peel.
///
/// @return the leaf underlying or pointed-to type node of @p type.
type_base_sptr
peel_typedef_pointer_or_reference_type(const type_base_sptr type)
{
  type_base_sptr typ = type;
  while (is_typedef(typ)
	 || is_pointer_type(typ)
	 || is_reference_type(typ))
    {
      if (typedef_decl_sptr t = is_typedef(typ))
	typ = peel_typedef_type(t);

      if (pointer_type_def_sptr t = is_pointer_type(typ))
	typ = peel_pointer_type(t);

      if (reference_type_def_sptr t = is_reference_type(typ))
	typ = peel_reference_type(t);
    }

  return typ;
}

/// Return the leaf underlying or pointed-to type node of a @ref
/// typedef_decl, @ref pointer_type_def or @ref reference_type_def
/// node.
///
/// @param type the type to peel.
///
/// @return the leaf underlying or pointed-to type node of @p type.
type_base*
peel_typedef_pointer_or_reference_type(const type_base* type)
{
  while (is_typedef(type)
	 || is_pointer_type(type)
	 || is_reference_type(type))
    {
      if (const typedef_decl* t = is_typedef(type))
	type = peel_typedef_type(t);

      if (const pointer_type_def* t = is_pointer_type(type))
	type = peel_pointer_type(t);

      if (const reference_type_def* t = is_reference_type(type))
	type = peel_reference_type(t);
    }

  return const_cast<type_base*>(type);
}

/// Return the leaf underlying or pointed-to type node of a, @ref
/// pointer_type_def, @ref reference_type_def or @ref
/// qualified_type_def type node.
///
/// @param type the type to peel.
///
/// @param peel_qualified_type if true, also peel qualified types.
///
/// @return the leaf underlying or pointed-to type node of @p type.
type_base*
peel_pointer_or_reference_type(const type_base *type,
			       bool peel_qual_type)
{
  while (is_pointer_type(type)
	 || is_reference_type(type)
	 || (peel_qual_type && is_qualified_type(type)))
    {
      if (const pointer_type_def* t = is_pointer_type(type))
	type = peel_pointer_type(t);

      if (const reference_type_def* t = is_reference_type(type))
	type = peel_reference_type(t);

      if (const array_type_def* t = is_array_type(type))
	type = peel_array_type(t);

      if (peel_qual_type)
	if (const qualified_type_def* t = is_qualified_type(type))
	  type = peel_qualified_type(t);
    }

  return const_cast<type_base*>(type);
}

/// Clone an array type.
///
/// Note that the element type of the new array is shared witht the
/// old one.
///
/// @param array the array type to clone.
///
/// @return a newly built array type.  Note that it needs to be added
/// to a scope (e.g, using add_decl_to_scope) for its lifetime to be
/// bound to the one of that scope.  Otherwise, its lifetime is bound
/// to the lifetime of its containing shared pointer.
array_type_def_sptr
clone_array(const array_type_def_sptr& array)
{
  vector<array_type_def::subrange_sptr> subranges;

  for (vector<array_type_def::subrange_sptr>::const_iterator i =
	 array->get_subranges().begin();
       i != array->get_subranges().end();
       ++i)
    {
      array_type_def::subrange_sptr subrange
	(new array_type_def::subrange_type(array->get_environment(),
					   (*i)->get_name(),
					   (*i)->get_lower_bound(),
					   (*i)->get_upper_bound(),
					   (*i)->get_underlying_type(),
					   (*i)->get_location(),
					   (*i)->get_language()));
      subrange->is_infinite((*i)->is_infinite());
      if (scope_decl *scope = (*i)->get_scope())
	add_decl_to_scope(subrange, scope);
      subranges.push_back(subrange);
    }

  array_type_def_sptr result
    (new array_type_def(array->get_element_type(),
			subranges, array->get_location()));

  return result;
}

/// Clone a typedef type.
///
/// Note that the underlying type of the newly constructed typedef is
/// shared with the old one.
///
/// @param t the typedef to clone.
///
/// @return the newly constructed typedef.  Note that it needs to be
/// added to a scope (e.g, using add_decl_to_scope) for its lifetime
/// to be bound to the one of that scope.  Otherwise, its lifetime is
/// bound to the lifetime of its containing shared pointer.
typedef_decl_sptr
clone_typedef(const typedef_decl_sptr& t)
{
  if (!t)
    return t;

  typedef_decl_sptr result
    (new typedef_decl(t->get_name(), t->get_underlying_type(),
		      t->get_location(), t->get_linkage_name(),
		      t->get_visibility()));
  return result;
}

/// Clone a qualifiend type.
///
/// Note that underlying type of the newly constructed qualified type
/// is shared with the old one.
///
/// @param t the qualified type to clone.
///
/// @return the newly constructed qualified type.  Note that it needs
/// to be added to a scope (e.g, using add_decl_to_scope) for its
/// lifetime to be bound to the one of that scope.  Otherwise, its
/// lifetime is bound to the lifetime of its containing shared
/// pointer.
qualified_type_def_sptr
clone_qualified_type(const qualified_type_def_sptr& t)
{
  if (!t)
    return t;

  qualified_type_def_sptr result
    (new qualified_type_def(t->get_underlying_type(),
			    t->get_cv_quals(), t->get_location()));

  return result;
}

/// Clone a typedef, an array or a qualified tree.
///
/// @param type the typedef, array or qualified tree to clone.  any
/// order.
///
/// @return the cloned type, or NULL if @type was neither a typedef,
/// array nor a qualified type.
static type_base_sptr
clone_typedef_array_qualified_type(type_base_sptr type)
{
  if (!type)
    return type;

  scope_decl* scope = is_decl(type) ? is_decl(type)->get_scope() : 0;
  type_base_sptr result;

  if (typedef_decl_sptr t = is_typedef(type))
    result = clone_typedef(is_typedef(t));
  else if (qualified_type_def_sptr t = is_qualified_type(type))
    result = clone_qualified_type(t);
  else if (array_type_def_sptr t = is_array_type(type))
    result = clone_array(t);
  else
    return type_base_sptr();

  if (scope)
    add_decl_to_scope(is_decl(result), scope);

  return result;
}

/// Clone a type tree made of an array or a typedef of array.
///
/// Note that this can be a tree which root node is a typedef an which
/// sub-tree can be any arbitrary combination of typedef, qualified
/// type and arrays.
///
/// @param t the array or typedef of qualified array to consider.
///
/// @return a clone of @p t.
type_base_sptr
clone_array_tree(const type_base_sptr t)
{
  ABG_ASSERT(is_typedef_of_array(t) || is_array_type(t));

  scope_decl* scope = is_decl(t)->get_scope();
  type_base_sptr result = clone_typedef_array_qualified_type(t);
  ABG_ASSERT(is_typedef_of_array(result) || is_array_type(result));

  type_base_sptr subtree;
  if (typedef_decl_sptr type = is_typedef(result))
    {
      type_base_sptr s =
	clone_typedef_array_qualified_type(type->get_underlying_type());
      if (s)
	{
	  subtree = s;
	  type->set_underlying_type(subtree);
	}
    }
  else if (array_type_def_sptr type = is_array_type(result))
    {
      type_base_sptr s =
	clone_typedef_array_qualified_type(type->get_element_type());
      if (s)
	{
	  subtree = s;
	  type->set_element_type(subtree);
	}
    }
  add_decl_to_scope(is_decl(subtree), scope);

  for (;;)
    {
      if (typedef_decl_sptr t = is_typedef(subtree))
	{
	  type_base_sptr s =
	    clone_typedef_array_qualified_type(t->get_underlying_type());
	  if (s)
	    {
	      scope_decl* scope =
		is_decl(t->get_underlying_type())->get_scope();
	      ABG_ASSERT(scope);
	      add_decl_to_scope(is_decl(s), scope);
	      t->set_underlying_type (s);
	      subtree = s;
	    }
	  else
	    break;
	}
      else if (qualified_type_def_sptr t = is_qualified_type(subtree))
	{
	  type_base_sptr s =
	    clone_typedef_array_qualified_type(t->get_underlying_type());
	  if (s)
	    {
	      scope_decl* scope =
		is_decl(t->get_underlying_type())->get_scope();
	      ABG_ASSERT(scope);
	      add_decl_to_scope(is_decl(s), scope);
	      t->set_underlying_type(s);
	      subtree = s;
	    }
	  else
	    break;
	}
      else if (array_type_def_sptr t = is_array_type(subtree))
	{
	  type_base_sptr e = t->get_element_type();
	  if (is_typedef(e) || is_qualified_type(e))
	    {
	      type_base_sptr s =
		clone_typedef_array_qualified_type(e);
	      if (s)
		{
		  scope_decl* scope = is_decl(e)->get_scope();
		  ABG_ASSERT(scope);
		  add_decl_to_scope(is_decl(s), scope);
		  t->set_element_type(s);
		}
	      else
		break;
	    }
	  break;
	}
      else
	break;
    }
  return result;
}

/// Update the qualified name of a given sub-tree.
///
/// @param d the sub-tree for which to update the qualified name.
static void
update_qualified_name(decl_base * d)
{
  ::qualified_name_setter setter;
  d->traverse(setter);
}

/// Update the qualified name of a given sub-tree.
///
/// @param d the sub-tree for which to update the qualified name.
static void
update_qualified_name(decl_base_sptr d)
{return update_qualified_name(d.get());}

// <scope_decl stuff>

/// Hash a type by returning the pointer value of its canonical type.
///
/// @param l the type to hash.
///
/// @return the the pointer value of the canonical type of @p l.
size_t
canonical_type_hash::operator()(const type_base_sptr& l) const
{return operator()(l.get());}

/// Hash a (canonical) type by returning its pointer value
///
/// @param l the canonical type to hash.
///
/// @return the pointer value of the canonical type of @p l.
size_t
canonical_type_hash::operator()(const type_base *l) const
{return reinterpret_cast<size_t>(l);}

struct scope_decl::priv
{
  declarations members_;
  declarations sorted_members_;
  scopes member_scopes_;
  canonical_type_sptr_set_type canonical_types_;
  type_base_sptrs_type sorted_canonical_types_;
}; // end struct scope_decl::priv

/// Constructor of the @ref scope_decl type.
///
/// @param the environment to use for the new instance.
///
/// @param the name of the scope decl.
///
/// @param locus the source location where the scope_decl is defined.
///
/// @param vis the visibility of the declaration.
scope_decl::scope_decl(const environment* env,
		       const string& name,
		       const location& locus,
		       visibility vis)
  : type_or_decl_base(env, ABSTRACT_SCOPE_DECL|ABSTRACT_DECL_BASE),
    decl_base(env, name, locus, /*mangled_name=*/name, vis),
    priv_(new priv)
{}

/// Constructor of the @ref scope_decl type.
///
/// @param the environment to use for the new instance.
///
/// @param l the source location where the scope_decl is defined.
///
/// @param vis the visibility of the declaration.
scope_decl::scope_decl(const environment* env, location& l)
  : type_or_decl_base(env, ABSTRACT_SCOPE_DECL|ABSTRACT_DECL_BASE),
    decl_base(env, "", l),
    priv_(new priv)
{}

/// @eturn the set of canonical types of the the current scope.
canonical_type_sptr_set_type&
scope_decl::get_canonical_types()
{return priv_->canonical_types_;}

/// @eturn the set of canonical types of the the current scope.
const canonical_type_sptr_set_type&
scope_decl::get_canonical_types() const
{return const_cast<scope_decl*>(this)->get_canonical_types();}

/// Return a vector of sorted canonical types of the current scope.
///
/// The types are sorted "almost topologically". That means, they are
/// sorted using the lexicographic order of the string representing
/// the location their definition point.  If a type doesn't have a
/// location, then its pretty representation is used.
///
/// @return a vector of sorted canonical types of the current scope.
const type_base_sptrs_type&
scope_decl::get_sorted_canonical_types() const
{
  if (priv_->sorted_canonical_types_.empty())
    {
      for (canonical_type_sptr_set_type::const_iterator e =
	     get_canonical_types().begin();
	   e != get_canonical_types().end();
	   ++e)
	priv_->sorted_canonical_types_.push_back(*e);

      type_topo_comp comp;
      std::stable_sort(priv_->sorted_canonical_types_.begin(),
		       priv_->sorted_canonical_types_.end(),
		       comp);
    }
  return priv_->sorted_canonical_types_;
}

/// Getter for the member declarations carried by the current @ref
/// scope_decl.
///
/// @return the member declarations carried by the current @ref
/// scope_decl.
const scope_decl::declarations&
scope_decl::get_member_decls() const
{return priv_->members_;}

/// Getter for the member declarations carried by the current @ref
/// scope_decl.
///
/// @return the member declarations carried by the current @ref
/// scope_decl.
scope_decl::declarations&
scope_decl::get_member_decls()
{return priv_->members_;}

/// Getter for the sorted member declarations carried by the current
/// @ref scope_decl.
///
/// @return the sorted member declarations carried by the current @ref
/// scope_decl.  The declarations are sorted topologically.
const scope_decl::declarations&
scope_decl::get_sorted_member_decls() const
{
  decl_topo_comp comp;
  if (priv_->sorted_members_.empty())
    {
      for (declarations::const_iterator i = get_member_decls().begin();
	   i != get_member_decls().end();
	   ++i)
	priv_->sorted_members_.push_back(*i);

      std::stable_sort(priv_->sorted_members_.begin(),
		       priv_->sorted_members_.end(),
		       comp);
    }
  return priv_->sorted_members_;
}

/// Getter for the number of anonymous classes contained in this
/// scope.
///
/// @return the number of anonymous classes contained in this scope.
size_t
scope_decl::get_num_anonymous_member_classes() const
{
  int result = 0;
  for (declarations::const_iterator it = get_member_decls().begin();
       it != get_member_decls().end();
       ++it)
    if (class_decl_sptr t = is_class_type(*it))
      if (t->get_is_anonymous())
	++result;

  return result;
}

/// Getter for the number of anonymous unions contained in this
/// scope.
///
/// @return the number of anonymous unions contained in this scope.
size_t
scope_decl::get_num_anonymous_member_unions() const
{
  int result = 0;
  for (declarations::const_iterator it = get_member_decls().begin();
       it != get_member_decls().end();
       ++it)
    if (union_decl_sptr t = is_union_type(*it))
      if (t->get_is_anonymous())
	++result;

  return result;
}

/// Getter for the number of anonymous enums contained in this
/// scope.
///
/// @return the number of anonymous enums contained in this scope.
size_t
scope_decl::get_num_anonymous_member_enums() const
{
  int result = 0;
  for (declarations::const_iterator it = get_member_decls().begin();
       it != get_member_decls().end();
       ++it)
    if (enum_type_decl_sptr t = is_enum_type(*it))
      if (t->get_is_anonymous())
	++result;

  return result;
}

/// Getter for the scopes carried by the current scope.
///
/// @return the scopes carried by the current scope.
scope_decl::scopes&
scope_decl::get_member_scopes()
{return priv_->member_scopes_;}

/// Getter for the scopes carried by the current scope.
///
/// @return the scopes carried by the current scope.
const scope_decl::scopes&
scope_decl::get_member_scopes() const
{return priv_->member_scopes_;}

/// Test if the current scope is empty.
///
/// @return true iff the current scope is empty.
bool
scope_decl::is_empty() const
{
  return (get_member_decls().empty()
	  && get_canonical_types().empty());
}

/// Add a member decl to this scope.  Note that user code should not
/// use this, but rather use add_decl_to_scope.
///
/// Note that this function updates the qualified name of the member
/// decl that is added.  It also sets the scope of the member.  Thus,
/// it ABG_ASSERTs that member should not have its scope set, prior to
/// calling this function.
///
/// @param member the new member decl to add to this scope.
decl_base_sptr
scope_decl::add_member_decl(const decl_base_sptr& member)
{
  ABG_ASSERT(!has_scope(member));

  member->set_scope(this);
  priv_->members_.push_back(member);

  if (scope_decl_sptr m = dynamic_pointer_cast<scope_decl>(member))
    priv_->member_scopes_.push_back(m);

  update_qualified_name(member);

  // Propagate scope anonymity
  if (get_has_anonymous_parent()
      || (!is_global_scope(this) && get_is_anonymous()))
    member->set_has_anonymous_parent(true);

  if (const environment* env = get_environment())
    set_environment_for_artifact(member, env);

  if (translation_unit* tu = get_translation_unit())
    {
      if (translation_unit* existing_tu = member->get_translation_unit())
	ABG_ASSERT(tu == existing_tu);
      else
	member->set_translation_unit(tu);
    }

  maybe_update_types_lookup_map(member);

  return member;
}

/// Insert a member decl to this scope, right before an element
/// pointed to by a given iterator.  Note that user code should not
/// use this, but rather use insert_decl_into_scope.
///
/// Note that this function updates the qualified name of the inserted
/// member.
///
/// @param member the new member decl to add to this scope.
///
/// @param before an interator pointing to the element before which
/// the new member should be inserted.
decl_base_sptr
scope_decl::insert_member_decl(decl_base_sptr member,
			       declarations::iterator before)
{
  ABG_ASSERT(!member->get_scope());

  member->set_scope(this);
  priv_->members_.insert(before, member);

  if (scope_decl_sptr m = dynamic_pointer_cast<scope_decl>(member))
   priv_-> member_scopes_.push_back(m);

  update_qualified_name(member);

  if (const environment* env = get_environment())
    set_environment_for_artifact(member, env);

  if (translation_unit* tu = get_translation_unit())
    {
      if (translation_unit* existing_tu = member->get_translation_unit())
	ABG_ASSERT(tu == existing_tu);
      else
	member->set_translation_unit(tu);
    }

  maybe_update_types_lookup_map(member);

  return member;
}

/// Remove a declaration from the current scope.
///
/// @param member the declaration to remove from the scope.
void
scope_decl::remove_member_decl(decl_base_sptr member)
{
  for (declarations::iterator i = priv_->members_.begin();
       i != priv_->members_.end();
       ++i)
    {
      if (**i == *member)
	{
	  priv_->members_.erase(i);
	  // Do not access i after this point as it's invalided by the
	  // erase call.
	  break;
	}
    }

  scope_decl_sptr scope = dynamic_pointer_cast<scope_decl>(member);
  if (scope)
    {
      for (scopes::iterator i = priv_->member_scopes_.begin();
	   i != priv_->member_scopes_.end();
	   ++i)
	{
	  if (**i == *member)
	    {
	      priv_->member_scopes_.erase(i);
	      break;
	    }
	}
    }
}

/// Return the hash value for the current instance of scope_decl.
///
/// This method can trigger the computing of the hash value, if need be.
///
/// @return the hash value.
size_t
scope_decl::get_hash() const
{
  scope_decl::hash hash_scope;
  return hash_scope(this);
}

/// Compares two instances of @ref scope_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const scope_decl& l, const scope_decl& r, change_kind* k)
{
  bool result = true;

  if (!l.decl_base::operator==(r))
    {
      result = false;
      if (k)
	*k |= LOCAL_NON_TYPE_CHANGE_KIND;
      else
	return false;
    }

  scope_decl::declarations::const_iterator i, j;
  for (i = l.get_member_decls().begin(), j = r.get_member_decls().begin();
       i != l.get_member_decls().end() && j != r.get_member_decls().end();
       ++i, ++j)
    {
      if (**i != **j)
	{
	  result = false;
	  if (k)
	    {
	      *k |= SUBTYPE_CHANGE_KIND;
	      break;
	    }
	  else
	    return false;
	}
    }

  if (i != l.get_member_decls().end() || j != r.get_member_decls().end())
    {
      result = false;
      if (k)
	*k |= LOCAL_NON_TYPE_CHANGE_KIND;
      else
	return false;
    }

  return result;
}

/// Return true iff both scopes have the same names and have the same
/// member decls.
///
/// This function doesn't check for equality of the scopes of its
/// arguments.
bool
scope_decl::operator==(const decl_base& o) const
{
  const scope_decl* other = dynamic_cast<const scope_decl*>(&o);
  if (!other)
    return false;

  return equals(*this, *other, 0);
}

/// Equality operator for @ref scope_decl_sptr.
///
/// @param l the left hand side operand of the equality operator.
///
/// @pram r the right hand side operand of the equalify operator.
///
/// @return true iff @p l equals @p r.
bool
operator==(const scope_decl_sptr& l, const scope_decl_sptr& r)
{
  if (!!l != !!r)
    return false;
  if (l.get() == r.get())
    return true;
  return *l == *r;
}

/// Inequality operator for @ref scope_decl_sptr.
///
/// @param l the left hand side operand of the equality operator.
///
/// @pram r the right hand side operand of the equalify operator.
///
/// @return true iff @p l equals @p r.
bool
operator!=(const scope_decl_sptr& l, const scope_decl_sptr& r)
{return !operator==(l, r);}

/// Find a member of the current scope and return an iterator on it.
///
/// @param decl the scope member to find.
///
/// @param i the iterator to set to the member @p decl.  This is set
/// iff the function returns true.
///
/// @return true if the member decl was found, false otherwise.
bool
scope_decl::find_iterator_for_member(const decl_base* decl,
				     declarations::iterator& i)
{
  if (!decl)
    return false;

  if (get_member_decls().empty())
    {
      i = get_member_decls().end();
      return false;
    }

  for (declarations::iterator it = get_member_decls().begin();
       it != get_member_decls().end();
       ++it)
    {
      if ((*it).get() == decl)
	{
	  i = it;
	  return true;
	}
    }

  return false;
}

/// Find a member of the current scope and return an iterator on it.
///
/// @param decl the scope member to find.
///
/// @param i the iterator to set to the member @p decl.  This is set
/// iff the function returns true.
///
/// @return true if the member decl was found, false otherwise.
bool
scope_decl::find_iterator_for_member(const decl_base_sptr decl,
				     declarations::iterator& i)
{return find_iterator_for_member(decl.get(), i);}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance of scope_decl
/// and on its member nodes.
///
/// @return true if the traversal of the tree should continue, false
/// otherwise.
bool
scope_decl::traverse(ir_node_visitor &v)
{
  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      for (scope_decl::declarations::const_iterator i =
	     get_member_decls().begin();
	   i != get_member_decls ().end();
	   ++i)
	if (!(*i)->traverse(v))
	  break;
      visiting(false);
    }
  return v.visit_end(this);
}

scope_decl::~scope_decl()
{}

/// Appends a declaration to a given scope, if the declaration
/// doesn't already belong to one.
///
/// @param decl the declaration to add to the scope
///
/// @param scope the scope to append the declaration to
decl_base_sptr
add_decl_to_scope(decl_base_sptr decl, scope_decl* scope)
{
  ABG_ASSERT(scope);

  if (scope && decl && !decl->get_scope())
    decl = scope->add_member_decl(decl);

  return decl;
}

/// Appends a declaration to a given scope, if the declaration doesn't
/// already belong to a scope.
///
/// @param decl the declaration to add append to the scope
///
/// @param scope the scope to append the decl to
decl_base_sptr
add_decl_to_scope(decl_base_sptr decl, const scope_decl_sptr& scope)
{return add_decl_to_scope(decl, scope.get());}

/// Remove a given decl from its scope
///
/// @param decl the decl to remove from its scope.
void
remove_decl_from_scope(decl_base_sptr decl)
{
  if (!decl)
    return;

  scope_decl* scope = decl->get_scope();
  scope->remove_member_decl(decl);
  decl->set_scope(0);
}

/// Inserts a declaration into a given scope, before a given IR child
/// node of the scope.
///
/// @param decl the declaration to insert into the scope.
///
/// @param before an iterator pointing to the child IR node before
/// which to insert the declaration.
///
/// @param scope the scope into which to insert the declaration.
decl_base_sptr
insert_decl_into_scope(decl_base_sptr decl,
		       scope_decl::declarations::iterator before,
		       scope_decl* scope)
{
  if (scope && decl && !decl->get_scope())
    {
      decl_base_sptr d = scope->insert_member_decl(decl, before);
      decl = d;
    }
  return decl;
}

/// Inserts a declaration into a given scope, before a given IR child
/// node of the scope.
///
/// @param decl the declaration to insert into the scope.
///
/// @param before an iterator pointing to the child IR node before
/// which to insert the declaration.
///
/// @param scope the scope into which to insert the declaration.
decl_base_sptr
insert_decl_into_scope(decl_base_sptr decl,
		       scope_decl::declarations::iterator before,
		       scope_decl_sptr scope)
{return insert_decl_into_scope(decl, before, scope.get());}

/// Constructor of the @ref global_scope type.
///
/// @param tu the translation unit the scope belongs to.
global_scope::global_scope(translation_unit *tu)
  : type_or_decl_base(tu->get_environment(),
		      GLOBAL_SCOPE_DECL
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_DECL),
    decl_base(tu->get_environment(), "", location()),
    scope_decl(tu->get_environment(), "", location()),
    translation_unit_(tu)
{
  runtime_type_instance(this);
}

/// return the global scope as seen by a given declaration.
///
/// @param decl the declaration to consider.
///
/// @return the global scope of the decl, or a null pointer if the
/// decl is not yet added to a translation_unit.
const global_scope*
get_global_scope(const decl_base& decl)
{
  if (const global_scope* s = dynamic_cast<const global_scope*>(&decl))
    return s;

  scope_decl* scope = decl.get_scope();
  while (scope && !dynamic_cast<global_scope*>(scope))
    scope = scope->get_scope();

  return scope ? dynamic_cast<global_scope*> (scope) : 0;
}

/// return the global scope as seen by a given declaration.
///
/// @param decl the declaration to consider.
///
/// @return the global scope of the decl, or a null pointer if the
/// decl is not yet added to a translation_unit.
const global_scope*
get_global_scope(const decl_base* decl)
{return get_global_scope(*decl);}

/// Return the global scope as seen by a given declaration.
///
/// @param decl the declaration to consider.
///
/// @return the global scope of the decl, or a null pointer if the
/// decl is not yet added to a translation_unit.
const global_scope*
get_global_scope(const shared_ptr<decl_base> decl)
{return get_global_scope(decl.get());}

/// Return the a scope S containing a given declaration and that is
/// right under a given scope P.
///
/// Note that @p scope must come before @p decl in topological
/// order.
///
/// @param decl the decl for which to find a scope.
///
/// @param scope the scope under which the resulting scope must be.
///
/// @return the resulting scope.
const scope_decl*
get_top_most_scope_under(const decl_base* decl,
			 const scope_decl* scope)
{
  if (!decl)
    return 0;

  if (scope == 0)
    return get_global_scope(decl);

  // Handle the case where decl is a scope itself.
  const scope_decl* s = dynamic_cast<const scope_decl*>(decl);
  if (!s)
    s = decl->get_scope();

  if (is_global_scope(s))
    return scope;

  // Here, decl is in the scope 'scope', or decl and 'scope' are the
  // same.  The caller needs to be prepared to deal with this case.
  if (s == scope)
    return s;

  while (s && !is_global_scope(s) && s->get_scope() != scope)
    s = s->get_scope();

  if (!s || is_global_scope(s))
    // SCOPE must come before decl in topological order, but I don't
    // know how to ensure that ...
    return scope;
  ABG_ASSERT(s);

  return s;
}

/// Return the a scope S containing a given declaration and that is
/// right under a given scope P.
///
/// @param decl the decl for which to find a scope.
///
/// @param scope the scope under which the resulting scope must be.
///
/// @return the resulting scope.
const scope_decl*
get_top_most_scope_under(const decl_base_sptr decl,
			 const scope_decl* scope)
{return get_top_most_scope_under(decl.get(), scope);}

/// Return the a scope S containing a given declaration and that is
/// right under a given scope P.
///
/// @param decl the decl for which to find a scope.
///
/// @param scope the scope under which the resulting scope must be.
///
/// @return the resulting scope.
const scope_decl*
get_top_most_scope_under(const decl_base_sptr decl,
			 const scope_decl_sptr scope)
{return get_top_most_scope_under(decl, scope.get());}

// </scope_decl stuff>


/// Get the string representation of a CV qualifier bitmap.
///
/// @param cv_quals the bitmap of CV qualifiers to consider.
///
/// @return the string representation.
string
get_string_representation_of_cv_quals(const qualified_type_def::CV cv_quals)
{
  string repr;
  if (cv_quals & qualified_type_def::CV_RESTRICT)
    repr = "restrict";
  if (cv_quals & qualified_type_def::CV_CONST)
    {
      if (!repr.empty())
	repr += ' ';
      repr += "const";
    }
  if (cv_quals & qualified_type_def::CV_VOLATILE)
    {
      if (!repr.empty())
	repr += ' ';
      repr += "volatile";
    }
  return repr;
}

/// Build and return a copy of the name of an ABI artifact that is
/// either a type or a decl.
///
/// @param tod the ABI artifact to get the name for.
///
/// @param qualified if yes, return the qualified name of @p tod;
/// otherwise, return the non-qualified name;
///
/// @return the name of @p tod.
string
get_name(const type_or_decl_base *tod, bool qualified)
{
  string result;

  type_or_decl_base* a = const_cast<type_or_decl_base*>(tod);

  if (type_base* t = dynamic_cast<type_base*>(a))
    result = get_type_name(t, qualified);
  else if (decl_base *d = dynamic_cast<decl_base*>(a))
    {
      if (qualified)
	result = d->get_qualified_name();
      else
	result = d->get_name();
    }
  else
    // We should never reach this point.
    abort();

  return result;
}

/// Build and return a copy of the name of an ABI artifact that is
/// either a type of a decl.
///
/// @param tod the ABI artifact to get the name for.
///
/// @param qualified if yes, return the qualified name of @p tod;
/// otherwise, return the non-qualified name;
///
/// @return the name of @p tod.
string
get_name(const type_or_decl_base_sptr& tod, bool qualified)
{return get_name(tod.get(), qualified);}

/// Build and return a qualified name from a name and its scope.
///
/// The name is supposed to be for an entity that is part of the
/// scope.
///
/// @param the scope to consider.
///
/// @param name of the name to consider.
///
/// @return a copy of the string that represents the qualified name.
string
build_qualified_name(const scope_decl* scope, const string& name)
{
  if (name.empty())
    return "";

  string qualified_name;
  if (scope)
    qualified_name = scope->get_qualified_name();

  if (qualified_name.empty())
    qualified_name = name;
  else
    qualified_name = qualified_name + "::" + name;

  return qualified_name;
}

/// Build and return the qualified name of a type in its scope.
///
/// @param scope the scope of the type to consider.
///
/// @param type the type to consider.
string
build_qualified_name(const scope_decl* scope, const type_base_sptr& type)
{return build_qualified_name(scope, get_name((type)));}

// </scope_decl stuff>

/// Get the location of the declaration of a given type.
///
/// @param type the type to consider.
///
/// @return the location of the declaration of type @p type.
location
get_location(const type_base_sptr& type)
{
  if (decl_base_sptr decl = get_type_declaration(type))
    return get_location(decl);
  return location();
}

/// Get the location of a given declaration.
///
/// @param decl the declaration to consider.
///
/// @return the location of the declaration @p decl.
location
get_location(const decl_base_sptr& decl)
{
  location loc = decl->get_location();
  if (!loc)
    {
      if (class_or_union_sptr c = is_class_or_union_type(decl))
	if (c->get_is_declaration_only() && c->get_definition_of_declaration())
	  {
	    c = is_class_or_union_type(c->get_definition_of_declaration());
	    loc = c->get_location();
	  }
    }
  return loc;
}

/// Get the scope of a given type.
///
/// @param t the type to consider.
///
/// @return the scope of type @p t or 0 if the type has no scope yet.
scope_decl*
get_type_scope(type_base* t)
{
  if (!t)
    return 0;

  decl_base* d = get_type_declaration(t);
  if (d)
    return d->get_scope();
  return 0;
}

/// Get the scope of a given type.
///
/// @param t the type to consider.
///
/// @return the scope of type @p t or 0 if the type has no scope yet.
scope_decl*
get_type_scope(const type_base_sptr& t)
{return get_type_scope(t.get());}

/// Get the name of a given type and return a copy of it.
///
/// @param t the type to consider.
///
/// @param qualified if true then return the qualified name of the
/// type.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the type name if the type has a name, or the
/// empty string if it does not.
interned_string
get_type_name(const type_base_sptr& t, bool qualified, bool internal)
{return get_type_name(t.get(), qualified, internal);}

/// Return the generic internal name of an anonymous type.
///
/// For internal purposes, we want to define a generic name for all
/// anonymous types of a certain kind.  For instance, all anonymous
/// structs will be have a generic name of "__anonymous_struct__", all
/// anonymous unions will have a generic name of
/// "__anonymous_union__", etc.
///
/// That generic name can be used as a hash to put all anonymous types
/// of a certain kind in the same hash table bucket, for instance.
static interned_string
get_generic_anonymous_internal_type_name(const decl_base *d)
{
  ABG_ASSERT(d);

  const environment *env = d->get_environment();

  interned_string result;
  if (is_class_type(d))
    result =
      env->intern(tools_utils::get_anonymous_struct_internal_name_prefix());
  else if (is_union_type(d))
    result =
      env->intern(tools_utils::get_anonymous_union_internal_name_prefix());
  else if (is_enum_type(d))
    result =
      env->intern(tools_utils::get_anonymous_enum_internal_name_prefix());
  else
    ABG_ASSERT_NOT_REACHED;

  return result;
}

/// Get the name of a given type and return a copy of it.
///
/// @param t the type to consider.
///
/// @param qualified if true then return the qualified name of the
/// type.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the type name if the type has a name, or the
/// empty string if it does not.
interned_string
get_type_name(const type_base* t, bool qualified, bool internal)
{
  const decl_base* d = dynamic_cast<const decl_base*>(t);
  if (!d)
    {
      const function_type* fn_type = is_function_type(t);
      ABG_ASSERT(fn_type);
      return fn_type->get_cached_name(internal);
    }

  // All anonymous types of a given kind get to have the same internal
  // name for internal purpose.  This to allow them to be compared
  // among themselves during type canonicalization.
  if (internal && d->get_is_anonymous())
    {
      string r;
      if (qualified)
	{
	  r = d->get_qualified_parent_name();
	  if (!r.empty())
	    r += "::";
	}
      r += get_generic_anonymous_internal_type_name(d);
      return t->get_environment()->intern(r);
    }

  if (qualified)
    return d->get_qualified_name(internal);
  return d->get_name();
}

/// Get the name of a given type and return a copy of it.
///
/// @param t the type to consider.
///
/// @param qualified if true then return the qualified name of the
/// type.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the type name if the type has a name, or the
/// empty string if it does not.
interned_string
get_type_name(const type_base& t, bool qualified, bool internal)
{return get_type_name(&t, qualified, internal);}

/// Get the name of the pointer to a given type.
///
/// @param pointed_to_type the pointed-to-type to consider.
///
/// @param qualified this is true if the resulting name should be of a
/// pointer to a *fully-qualified* pointed-to-type.
///
/// @param internal true if the name is for libabigail-internal
/// purposes.
///
/// @return the name (string representation) of the pointer.
interned_string
get_name_of_pointer_to_type(const type_base& pointed_to_type,
			    bool qualified, bool internal)
{
  const environment* env = pointed_to_type.get_environment();
  ABG_ASSERT(env);

  string tn = get_type_name(pointed_to_type, qualified, internal);
  tn =  tn + "*";

  return env->intern(tn);
}

/// Get the name of the reference to a given type.
///
/// @param pointed_to_type the pointed-to-type to consider.
///
/// @param qualified this is true if the resulting name should be of a
/// reference to a *fully-qualified* pointed-to-type.
///
/// @param internal true if the name is for libabigail-internal
/// purposes.
///
/// @return the name (string representation) of the reference.
interned_string
get_name_of_reference_to_type(const type_base& pointed_to_type,
			      bool lvalue_reference,
			      bool qualified, bool internal)
{
  const environment* env = pointed_to_type.get_environment();
  ABG_ASSERT(env);

  string name = get_type_name(pointed_to_type, qualified, internal);
  if (lvalue_reference)
    name = name + "&";
  else
    name = name + "&&";

  return env->intern(name);
}

/// Get the name of a qualified type, given the underlying type and
/// its qualifiers.
///
/// @param underlying_type the underlying type to consider.
///
/// @param quals the CV qualifiers of the name.
///
/// @param qualified true if we should consider the fully qualified
/// name of @p underlying_type.
///
/// @param internal true if the result is to be used for
/// libabigail-internal purposes.
///
/// @return the name (string representation) of the qualified type.
interned_string
get_name_of_qualified_type(const type_base_sptr& underlying_type,
			   qualified_type_def::CV quals,
			   bool qualified, bool internal)
{
  const environment* env = underlying_type->get_environment();
  ABG_ASSERT(env);

  string quals_repr = get_string_representation_of_cv_quals(quals);
  string name = get_type_name(underlying_type, qualified, internal);

  if (quals_repr.empty() && internal)
    // We are asked to return the internal name, that might be used
    // for type canonicalization.  For that canonicalization, we need
    // to make a difference between a no-op qualified type which
    // underlying type is foo (the qualified type is named "none
    // foo"), and the name of foo, which is just "foo".
    //
    // Please remember that this has to be kept in sync with what is
    // done in die_qualified_name, in abg-dwarf-reader.cc.  So if you
    // change this code here, please change that code there too.
    quals_repr = "";

  if (!quals_repr.empty())
    {
      if (is_pointer_type(underlying_type)
	  || is_reference_type(underlying_type)
	  || is_array_type(underlying_type))
	{
	  name += " ";
	  name += quals_repr;
	}
      else
	name = quals_repr + " " + name;
    }

  return env->intern(name);
}

/// Get the name of a given function type and return a copy of it.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the function type name
interned_string
get_function_type_name(const function_type_sptr& fn_type,
		       bool internal)
{return get_function_type_name(fn_type.get(), internal);}

/// Get the name of a given function type and return a copy of it.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the function type name
interned_string
get_function_type_name(const function_type* fn_type,
		       bool internal)
{
  ABG_ASSERT(fn_type);

  if (const method_type* method = is_method_type(fn_type))
    return get_method_type_name(method, internal);

  return get_function_type_name(*fn_type, internal);
}

/// Get the name of a given function type and return a copy of it.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the function type name
interned_string
get_function_type_name(const function_type& fn_type,
		       bool internal)
{
  std::ostringstream o;
  type_base_sptr return_type = fn_type.get_return_type();
  const environment* env = fn_type.get_environment();
  ABG_ASSERT(env);

  o <<  get_pretty_representation(return_type, internal);

  o << " (";
  for (function_type::parameters::const_iterator i =
	 fn_type.get_parameters().begin();
       i != fn_type.get_parameters().end();
       ++i)
    {
      if (i != fn_type.get_parameters().begin())
	o << ", ";
      o << get_pretty_representation((*i)->get_type(), internal);
    }
  o <<")";

  return env->intern(o.str());
}

/// Get the name of a given method type and return a copy of it.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the function type name
interned_string
get_method_type_name(const method_type_sptr fn_type,
		     bool internal)
{return get_method_type_name(fn_type.get(), internal);}

/// Get the name of a given method type and return a copy of it.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the function type name
interned_string
get_method_type_name(const method_type* fn_type,
		     bool internal)
{
  if (fn_type)
    return get_method_type_name(*fn_type, internal);

  return interned_string();
}

/// Get the name of a given method type and return a copy of it.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the function type name
interned_string
get_method_type_name(const method_type& fn_type,
		     bool internal)
{
  std::ostringstream o;
  type_base_sptr return_type= fn_type.get_return_type();
  const environment* env = fn_type.get_environment();
  ABG_ASSERT(env);

  if (return_type)
    o << return_type->get_cached_pretty_representation(internal);
  else
    // There are still some abixml files out there in which "void"
    // can be expressed as an empty type.
    o << "void";

  class_or_union_sptr class_type = fn_type.get_class_type();
  ABG_ASSERT(class_type);

  o << " (" << class_type->get_qualified_name(internal) << "::*)"
    << " (";

  for (function_type::parameters::const_iterator i =
	 fn_type.get_parameters().begin();
       i != fn_type.get_parameters().end();
       ++i)
    {
      if (i != fn_type.get_parameters().begin())
	o << ", ";
      if (*i)
	o << (*i)->get_type()->get_cached_pretty_representation(internal);
      else
	// There are still some abixml files out there in which "void"
	// can be expressed as an empty type.
	o << "void";
    }
  o <<")";

  return env->intern(o.str());
}

/// Build and return a copy of the pretty representation of an ABI
/// artifact that could be either a type of a decl.
///
/// param tod the ABI artifact to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the pretty representation of an ABI artifact
/// that could be either a type of a decl.
string
get_pretty_representation(const type_or_decl_base* tod, bool internal)
{
  string result;

  if (type_base* t = is_type(const_cast<type_or_decl_base*>(tod)))
    result = get_pretty_representation(t, internal);
  else if (decl_base* d = is_decl(const_cast<type_or_decl_base*>(tod)))
    result =  get_pretty_representation(d, internal);
  else
    // We should never reach this point
    abort();

  return result;
}

/// Build and return a copy of the pretty representation of an ABI
/// artifact that could be either a type of a decl.
///
/// param tod the ABI artifact to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the pretty representation of an ABI artifact
/// that could be either a type of a decl.
string
get_pretty_representation(const type_or_decl_base_sptr& tod, bool internal)
{return get_pretty_representation(tod.get(), internal);}

/// Get a copy of the pretty representation of a decl.
///
/// @param d the decl to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the pretty representation of the decl.
string
get_pretty_representation(const decl_base* d, bool internal)
{
  if (!d)
    return "";
  return d->get_pretty_representation(internal);
}

/// Get a copy of the pretty representation of a type.
///
/// @param d the type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the pretty representation of the type.
string
get_pretty_representation(const type_base* t, bool internal)
{
  if (!t)
    return "void";
  if (const function_type* fn_type = is_function_type(t))
    return get_pretty_representation(fn_type, internal);

  const decl_base* d = get_type_declaration(t);
  ABG_ASSERT(d);
  return get_pretty_representation(d, internal);
}

/// Get a copy of the pretty representation of a decl.
///
/// @param d the decl to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the pretty representation of the decl.
string
get_pretty_representation(const decl_base_sptr& d, bool internal)
{return get_pretty_representation(d.get(), internal);}

/// Get a copy of the pretty representation of a type.
///
/// @param d the type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the pretty representation of the type.
string
get_pretty_representation(const type_base_sptr& t, bool internal)
{return get_pretty_representation(t.get(), internal);}

/// Get the pretty representation of a function type.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the string represenation of the function type.
string
get_pretty_representation(const function_type_sptr& fn_type,
			  bool internal)
{return get_pretty_representation(fn_type.get(), internal);}

/// Get the pretty representation of a function type.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the string represenation of the function type.
string
get_pretty_representation(const function_type* fn_type, bool internal)
{
  if (!fn_type)
    return "void";

  if (const method_type* method = is_method_type(fn_type))
    return get_pretty_representation(method, internal);

  return get_pretty_representation(*fn_type, internal);
}

/// Get the pretty representation of a function type.
///
/// @param fn_type the function type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the string represenation of the function type.
string
get_pretty_representation(const function_type& fn_type, bool internal)
{
  std::ostringstream o;
  o << "function type " << get_function_type_name(fn_type, internal);
  return o.str();
}

/// Get the pretty representation of a method type.
///
/// @param method the method type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the string represenation of the method type.
string
get_pretty_representation(const method_type& method, bool internal)
{
  std::ostringstream o;
  o << "method type " << get_method_type_name(method, internal);
  return o.str();
}

/// Get the pretty representation of a method type.
///
/// @param method the method type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the string represenation of the method type.
string
get_pretty_representation(const method_type* method, bool internal)
{
  if (!method)
    return "void";
  return get_pretty_representation(*method, internal);
}

/// Get the pretty representation of a method type.
///
/// @param method the method type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the string represenation of the method type.
string
get_pretty_representation(const method_type_sptr method, bool internal)
{return get_pretty_representation(method.get(), internal);}

/// Get the flat representation of an instance of @ref class_or_union
/// type.
///
/// The flat representation of a given @ref class_or_union type is the
/// actual definition of the type, for instance:
///
///   struct foo {int a; char b;}
///
///@param cou the instance of @ref class_or_union to consider.
///
///@param indent the identation spaces to use in the representation.
///
///@param one_line if true, then the flat representation stands on one
///line.  Otherwise, it stands on multiple lines.
///
///@return the resulting flat representation.
string
get_class_or_union_flat_representation(const class_or_union& cou,
				       const string& indent,
				       bool one_line,
				       bool internal,
				       bool qualified_names)
{
  string repr;
  string local_indent = "  ";

  if (class_decl* clazz = is_class_type(&cou))
    {
      repr = indent;
      if (!internal && clazz->is_struct())
	repr += "struct";
      else
	repr += "class";
    }
  else if (is_union_type(cou))
    repr = indent + "union";
  else
    return "";

  repr += " ";

  string name = cou.get_qualified_name();

  if (!cou.get_is_anonymous())
    repr += name;

  repr += "{";

  if (!one_line)
    repr += "\n";

  string real_indent;
  const class_or_union::data_members &dmems = cou.get_data_members();
  for (class_or_union::data_members::const_iterator dm = dmems.begin();
       dm != dmems.end();
       ++dm)
    {
      if (dm != dmems.begin())
	{
	  if (one_line)
	    real_indent = " ";
	  else
	    real_indent = "\n" + indent + local_indent;
	}

      if (var_decl_sptr v = is_anonymous_data_member(*dm))
	repr +=
	  get_class_or_union_flat_representation
	  (anonymous_data_member_to_class_or_union(*dm),
	   real_indent, one_line, internal, qualified_names);
      else
	{
	  if (one_line)
	    {
	      if (dm != dmems.begin())
		repr += real_indent;
	      repr += (*dm)->get_pretty_representation(internal,
						       qualified_names);
	    }
	  else
	    repr +=
	      real_indent+ (*dm)->get_pretty_representation(internal,
							    qualified_names);
	}
      repr += ";";
    }

  if (one_line)
    repr += "}";
  else
    repr += indent + "}";

  return repr;
}

/// Get the flat representation of an instance of @ref class_or_union
/// type.
///
/// The flat representation of a given @ref class_or_union type is the
/// actual definition of the type, for instance:
///
///   struct foo {int a; char b;}
///
///@param cou the instance of @ref class_or_union to consider.
///
///@param indent the identation spaces to use in the representation.
///
///@param one_line if true, then the flat representation stands on one
///line.  Otherwise, it stands on multiple lines.
///
///@return the resulting flat representation.
string
get_class_or_union_flat_representation(const class_or_union* cou,
				       const string& indent,
				       bool one_line,
				       bool internal,
				       bool qualified_names)
{
  if (cou)
    return get_class_or_union_flat_representation(*cou, indent, one_line,
						  internal, qualified_names);
  return "";
}

/// Get the flat representation of an instance of @ref class_or_union
/// type.
///
/// The flat representation of a given @ref class_or_union type is the
/// actual definition of the type, for instance:
///
///   struct foo {int a; char b;}
///
///@param cou the instance of @ref class_or_union to consider.
///
///@param indent the identation spaces to use in the representation.
///
///@param one_line if true, then the flat representation stands on one
///line.  Otherwise, it stands on multiple lines.
///
///@return the resulting flat representation.
string
get_class_or_union_flat_representation(const class_or_union_sptr& cou,
				       const string& indent,
				       bool one_line,
				       bool internal,
				       bool qualified_names)
{return get_class_or_union_flat_representation(cou.get(),
					       indent,
					       one_line,
					       internal,
					       qualified_names);}

/// By looking at the language of the TU a given ABI artifact belongs
/// to, test if the ONE Definition Rule should apply.
///
/// To date, it applies to c++, java and ada.
///
/// @param artifact the ABI artifact to consider.
///
/// @return true iff the One Definition Rule should apply.
bool
odr_is_relevant(const type_or_decl_base& artifact)
{
  translation_unit::language l =
    artifact.get_translation_unit()->get_language();

  if (is_cplus_plus_language(l)
      || is_java_language(l)
      || is_ada_language(l))
    return true;

  return false;
}

/// Get the declaration for a given type.
///
/// @param t the type to consider.
///
/// @return the declaration for the type to return.
const decl_base*
get_type_declaration(const type_base* t)
{return dynamic_cast<const decl_base*>(t);}

/// Get the declaration for a given type.
///
/// @param t the type to consider.
///
/// @return the declaration for the type to return.
decl_base*
get_type_declaration(type_base* t)
{return dynamic_cast<decl_base*>(t);}

/// Get the declaration for a given type.
///
/// @param t the type to consider.
///
/// @return the declaration for the type to return.
decl_base_sptr
get_type_declaration(const type_base_sptr t)
{return dynamic_pointer_cast<decl_base>(t);}

/// Test if two types are equal modulo a typedef.
///
/// Type A and B are compatible if
///
///	- A and B are equal
///	- or if one type is a typedef of the other one.
///
/// @param type1 the first type to consider.
///
/// @param type2 the second type to consider.
///
/// @return true iff @p type1 and @p type2 are compatible.
bool
types_are_compatible(const type_base_sptr type1,
		     const type_base_sptr type2)
{
  if (!type1 || !type2)
    return false;

  if (type1 == type2)
    return true;

  // Normally we should strip typedefs entirely, but this is
  // potentially costly, especially on binaries with huge changesets
  // like the Linux Kernel.  So we just get the leaf types for now.
  //
  // Maybe there should be an option by which users accepts to pay the
  // CPU usage toll in exchange for finer filtering?

  // type_base_sptr t1 = strip_typedef(type1);
  // type_base_sptr t2 = strip_typedef(type2);

  type_base_sptr t1 = peel_typedef_type(type1);
  type_base_sptr t2 = peel_typedef_type(type2);

  return t1 == t2;
}

/// Test if two types are equal modulo a typedef.
///
/// Type A and B are compatible if
///
///	- A and B are equal
///	- or if one type is a typedef of the other one.
///
/// @param type1 the declaration of the first type to consider.
///
/// @param type2 the declaration of the second type to consider.
///
/// @return true iff @p type1 and @p type2 are compatible.
bool
types_are_compatible(const decl_base_sptr d1,
		     const decl_base_sptr d2)
{return types_are_compatible(is_type(d1), is_type(d2));}

/// Return the translation unit a declaration belongs to.
///
/// @param decl the declaration to consider.
///
/// @return the resulting translation unit, or null if the decl is not
/// yet added to a translation unit.
translation_unit*
get_translation_unit(const decl_base& decl)
{return const_cast<translation_unit*>(decl.get_translation_unit());}

/// Return the translation unit a declaration belongs to.
///
/// @param decl the declaration to consider.
///
/// @return the resulting translation unit, or null if the decl is not
/// yet added to a translation unit.
translation_unit*
get_translation_unit(const decl_base* decl)
{return decl ? get_translation_unit(*decl) : 0;}

/// Return the translation unit a declaration belongs to.
///
/// @param decl the declaration to consider.
///
/// @return the resulting translation unit, or null if the decl is not
/// yet added to a translation unit.
translation_unit*
get_translation_unit(const shared_ptr<decl_base> decl)
{return get_translation_unit(decl.get());}

/// Tests whether if a given scope is the global scope.
///
/// @param scope the scope to consider.
///
/// @return true iff the current scope is the global one.
bool
is_global_scope(const scope_decl& scope)
{return !!dynamic_cast<const global_scope*>(&scope);}

/// Tests whether if a given scope is the global scope.
///
/// @param scope the scope to consider.
///
/// @return the @ref global_scope* representing the scope @p scope or
/// 0 if @p scope is not a global scope.
const global_scope*
is_global_scope(const scope_decl* scope)
{return dynamic_cast<const global_scope*>(scope);}

/// Tests whether if a given scope is the global scope.
///
/// @param scope the scope to consider.
///
/// @return true iff the current scope is the global one.
bool
is_global_scope(const shared_ptr<scope_decl>scope)
{return is_global_scope(scope.get());}

/// Tests whether a given declaration is at global scope.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is at global scope.
bool
is_at_global_scope(const decl_base& decl)
{return (is_global_scope(decl.get_scope()));}

/// Tests whether a given declaration is at global scope.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is at global scope.
bool
is_at_global_scope(const decl_base_sptr decl)
{return (decl && is_global_scope(decl->get_scope()));}

/// Tests whether a given decl is at class scope.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is at class scope.
class_or_union*
is_at_class_scope(const decl_base_sptr decl)
{return is_at_class_scope(decl.get());}

/// Tests whether a given decl is at class scope.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is at class scope.
class_or_union*
is_at_class_scope(const decl_base* decl)
{
  if (!decl)
    return 0;

  return is_at_class_scope(*decl);
}

/// Tests whether a given decl is at class scope.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is at class scope.
class_or_union*
is_at_class_scope(const decl_base& decl)
{
  scope_decl* scope = decl.get_scope();
  if (class_or_union* cl = is_class_type(scope))
    return cl;
  if (class_or_union* cl = is_union_type(scope))
    return cl;
  return 0;
}

/// Find a data member inside an anonymous data member.
///
/// An anonymous data member has a type which is a class or union.
/// This function looks for a data member inside the type of that
/// anonymous data member.
///
/// @param anon_dm the anonymous data member to consider.
///
/// @param name the name of the data member to look for.
var_decl_sptr
find_data_member_from_anonymous_data_member(const var_decl_sptr& anon_dm,
					    const string& name)
{
  const class_or_union* containing_class_or_union =
    anonymous_data_member_to_class_or_union(anon_dm.get());

  if (!containing_class_or_union)
    return var_decl_sptr();

  var_decl_sptr result = containing_class_or_union->find_data_member(name);
  return result;
}

/// Tests whether a given decl is at template scope.
///
/// Note that only template parameters , types that are compositions,
/// and template patterns (function or class) can be at template scope.
///
/// @param decl the decl to consider.
///
/// @return true iff the decl is at template scope.
bool
is_at_template_scope(const shared_ptr<decl_base> decl)
{return (decl && dynamic_cast<template_decl*>(decl->get_scope()));}

/// Tests whether a decl is a template parameter.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is a template parameter.
bool
is_template_parameter(const shared_ptr<decl_base> decl)
{
  return (decl && (dynamic_pointer_cast<type_tparameter>(decl)
		   || dynamic_pointer_cast<non_type_tparameter>(decl)
		   || dynamic_pointer_cast<template_tparameter>(decl)));
}

/// Test whether a declaration is a @ref function_decl.
///
/// @param d the declaration to test for.
///
/// @return a shared pointer to @ref function_decl if @p d is a @ref
/// function_decl.  Otherwise, a nil shared pointer.
function_decl*
is_function_decl(const type_or_decl_base* d)
{return dynamic_cast<function_decl*>(const_cast<type_or_decl_base*>(d));}

/// Test whether a declaration is a @ref function_decl.
///
/// @param d the declaration to test for.
///
/// @return true if @p d is a function_decl.
bool
is_function_decl(const type_or_decl_base& d)
{return is_function_decl(&d);}

/// Test whether a declaration is a @ref function_decl.
///
/// @param d the declaration to test for.
///
/// @return a shared pointer to @ref function_decl if @p d is a @ref
/// function_decl.  Otherwise, a nil shared pointer.
function_decl_sptr
is_function_decl(const type_or_decl_base_sptr& d)
{return dynamic_pointer_cast<function_decl>(d);}

/// Test whether a declaration is a @ref function_decl.
///
/// @param d the declaration to test for.
///
/// @return a pointer to @ref function_decl if @p d is a @ref
/// function_decl.  Otherwise, a nil shared pointer.
function_decl::parameter*
is_function_parameter(const type_or_decl_base* tod)
{
  return dynamic_cast<function_decl::parameter*>
    (const_cast<type_or_decl_base*>(tod));
}

/// Test whether an ABI artifact is a @ref function_decl.
///
/// @param tod the declaration to test for.
///
/// @return a pointer to @ref function_decl if @p d is a @ref
/// function_decl.  Otherwise, a nil shared pointer.
function_decl::parameter_sptr
is_function_parameter(const type_or_decl_base_sptr tod)
{return dynamic_pointer_cast<function_decl::parameter>(tod);}

/// Test if an ABI artifact is a declaration.
///
/// @param d the artifact to consider.
///
/// @param return the declaration sub-object of @p d if it's a
/// declaration, or NULL if it is not.
decl_base*
is_decl(const type_or_decl_base* d)
{
  if (d && (d->kind() & type_or_decl_base::ABSTRACT_DECL_BASE))
    {
      if (!(d->kind() & type_or_decl_base::ABSTRACT_TYPE_BASE))
	// The artifact is a decl-only (like a function or a
	// variable).  That is, it's not a type that also has a
	// declaration.  In this case, we are in the fast path and we
	// have a pointer to the decl sub-object handy.  Just return
	// it ...
	return reinterpret_cast<decl_base*>
	  (const_cast<type_or_decl_base*>(d)->type_or_decl_base_pointer());

      // ... Otherwise, we are in the slow path, which is that the
      // artifact is a type which has a declaration.  In that case,
      // let's use the slow dynamic_cast because we don't have the
      // pointer to the decl sub-object handily present.
      return dynamic_cast<decl_base*>(const_cast<type_or_decl_base*>(d));
    }
  return 0;
}

/// Test if an ABI artifact is a declaration.
///
/// @param d the artifact to consider.
///
/// @param return the declaration sub-object of @p d if it's a
/// declaration, or NULL if it is not.
decl_base_sptr
is_decl(const type_or_decl_base_sptr& d)
{return dynamic_pointer_cast<decl_base>(d);}

/// Test if an ABI artifact is a declaration.
///
/// This is done using a slow path that uses dynamic_cast.
///
/// @param d the artifact to consider.
///
/// @param return the declaration sub-object of @p d if it's a
decl_base*
is_decl_slow(const type_or_decl_base* t)
{return dynamic_cast<decl_base*>(const_cast<type_or_decl_base*>(t));}

/// Test if an ABI artifact is a declaration.
///
/// This is done using a slow path that uses dynamic_cast.
///
/// @param d the artifact to consider.
///
/// @param return the declaration sub-object of @p d if it's a
decl_base_sptr
is_decl_slow(const type_or_decl_base_sptr& t)
{return dynamic_pointer_cast<decl_base>(t);}

/// Test whether a declaration is a type.
///
/// @param d the IR artefact to test for.
///
/// @return true if the artifact is a type, false otherwise.
bool
is_type(const type_or_decl_base& tod)
{
  if (dynamic_cast<const type_base*>(&tod))
    return true;
  return false;
}

/// Test whether a declaration is a type.
///
/// @param d the IR artefact to test for.
///
/// @return true if the artifact is a type, false otherwise.
type_base*
is_type(const type_or_decl_base* t)
{
  if (t && (t->kind() & type_or_decl_base::ABSTRACT_TYPE_BASE))
    return reinterpret_cast<type_base*>
      (const_cast<type_or_decl_base*>(t)->type_or_decl_base_pointer());

  return 0;
}

/// Test whether a declaration is a type.
///
/// @param d the IR artefact to test for.
///
/// @return true if the artifact is a type, false otherwise.
type_base_sptr
is_type(const type_or_decl_base_sptr& tod)
{return dynamic_pointer_cast<type_base>(tod);}

/// Test whether a declaration is a type.
///
/// @param d the declaration to test for.
///
/// @return true if the declaration is a type, false otherwise.

/// Test if a given type is anonymous.
///
/// Note that this function considers that an anonymous class that is
/// named by a typedef is not anonymous anymore.  This is the C idiom:
///
///       typedef struct {int member;} s_type;
///
/// The typedef s_type becomes the name of the originally anonymous
/// struct.
///
/// @param t the type to consider.
///
/// @return true iff @p t is anonymous.
bool
is_anonymous_type(type_base* t)
{
  decl_base* d = get_type_declaration(t);
  if (d)
    if (d->get_is_anonymous())
      {
	if (class_or_union *cou = is_class_or_union_type(t))
	  {
	    // An anonymous class that is named by a typedef is not
	    // considered anonymous anymore.
	    if (!cou->get_naming_typedef())
	      return true;
	  }
	else
	  return true;
      }
  return false;
}

/// Test if a given type is anonymous.
///
/// @param t the type to consider.
///
/// @return true iff @p t is anonymous.
bool
is_anonymous_type(const type_base_sptr& t)
{return is_anonymous_type(t.get());}

/// Test whether a type is a type_decl (a builtin type).
///
/// @return the type_decl* for @t if it's type_decl, otherwise, return
/// nil.
const type_decl*
is_type_decl(const type_or_decl_base* t)
{return dynamic_cast<const type_decl*>(t);}

/// Test whether a type is a type_decl (a builtin type).
///
/// @return the type_decl_sptr for @t if it's type_decl, otherwise,
/// return nil.
type_decl_sptr
is_type_decl(const type_or_decl_base_sptr& t)
{return dynamic_pointer_cast<type_decl>(t);}

/// Test whether a type is a typedef.
///
/// @param t the type to test for.
///
/// @return the typedef declaration of the @p t, or NULL if it's not a
/// typedef.
typedef_decl_sptr
is_typedef(const type_or_decl_base_sptr t)
{return dynamic_pointer_cast<typedef_decl>(t);}

/// Test whether a type is a typedef.
///
/// @param t the declaration of the type to test for.
///
/// @return the typedef declaration of the @p t, or NULL if it's not a
/// typedef.
const typedef_decl*
is_typedef(const type_base* t)
{return dynamic_cast<const typedef_decl*>(t);}

/// Test whether a type is a typedef.
///
/// @param t the declaration of the type to test for.
///
/// @return the typedef declaration of the @p t, or NULL if it's not a
/// typedef.
typedef_decl*
is_typedef(type_base* t)
{return dynamic_cast<typedef_decl*>(t);}

/// Test if a type is an enum. This function looks through typedefs.
///
/// @parm t the type to consider.
///
/// @return the enum_decl if @p t is an @ref enum_decl or null
/// otherwise.
enum_type_decl_sptr
is_compatible_with_enum_type(const type_base_sptr& t)
{
  if (!t)
    return enum_type_decl_sptr();

  // Normally we should strip typedefs entirely, but this is
  // potentially costly, especially on binaries with huge changesets
  // like the Linux Kernel.  So we just get the leaf types for now.
  //
  // Maybe there should be an option by which users accepts to pay the
  // CPU usage toll in exchange for finer filtering?

  // type_base_sptr ty = strip_typedef(t);
  type_base_sptr ty = peel_typedef_type(t);;
  return is_enum_type(ty);
}

/// Test if a type is an enum. This function looks through typedefs.
///
/// @parm t the type to consider.
///
/// @return the enum_decl if @p t is an @ref enum_decl or null
/// otherwise.
enum_type_decl_sptr
is_compatible_with_enum_type(const decl_base_sptr& t)
{return is_compatible_with_enum_type(is_type(t));}

/// Test if a decl is an enum_type_decl
///
/// @param d the decl to test for.
///
/// @return the enum_type_decl* if @p d is an enum, nil otherwise.
const enum_type_decl*
is_enum_type(const type_or_decl_base* d)
{return dynamic_cast<const enum_type_decl*>(d);}

/// Test if a decl is an enum_type_decl
///
/// @param d the decl to test for.
///
/// @return the enum_type_decl_sptr if @p d is an enum, nil otherwise.
enum_type_decl_sptr
is_enum_type(const type_or_decl_base_sptr& d)
{return dynamic_pointer_cast<enum_type_decl>(d);}

/// Test if a type is a class. This function looks through typedefs.
///
/// @parm t the type to consider.
///
/// @return the class_decl if @p t is a class_decl or null otherwise.
class_decl_sptr
is_compatible_with_class_type(const type_base_sptr& t)
{
  if (!t)
    return class_decl_sptr();

  // Normally we should strip typedefs entirely, but this is
  // potentially costly, especially on binaries with huge changesets
  // like the Linux Kernel.  So we just get the leaf types for now.
  //
  // Maybe there should be an option by which users accepts to pay the
  // CPU usage toll in exchange for finer filtering?

  // type_base_sptr ty = strip_typedef(t);
  type_base_sptr ty = peel_typedef_type(t);
  return is_class_type(ty);
}

/// Test if a type is a class. This function looks through typedefs.
///
/// @parm t the type to consider.
///
/// @return the class_decl if @p t is a class_decl or null otherwise.
class_decl_sptr
is_compatible_with_class_type(const decl_base_sptr& t)
{return is_compatible_with_class_type(is_type(t));}

/// Test whether a type is a class.
///
/// @parm t the type to consider.
///
/// @return true iff @p t is a class_decl.
bool
is_class_type(const type_or_decl_base& t)
{return is_class_type(&t);}

/// Test whether a type is a class.
///
/// @parm t the type to consider.
///
/// @return the class_decl if @p t is a class_decl or null otherwise.
class_decl*
is_class_type(const type_or_decl_base* t)
{
  if (!t)
    return 0;

  if (t->kind() & type_or_decl_base::CLASS_TYPE)
    return reinterpret_cast<class_decl*>
      (const_cast<type_or_decl_base*>(t)->runtime_type_instance());

  return 0;
}

/// Test whether a type is a class.
///
/// @parm t the type to consider.
///
/// @return the class_decl if @p t is a class_decl or null otherwise.
class_decl_sptr
is_class_type(const type_or_decl_base_sptr& d)
{return dynamic_pointer_cast<class_decl>(d);}


/// Test wheter a type is a declaration-only class.
///
/// @param t the type to considier.
///
/// @return true iff @p t is a declaration-only class.
bool
is_declaration_only_class_or_union_type(const type_base *t)
{
  if (const class_or_union *klass = is_class_or_union_type(t))
    return klass->get_is_declaration_only();
  return false;
}

/// Test wheter a type is a declaration-only class.
///
/// @param t the type to considier.
///
/// @return true iff @p t is a declaration-only class.
bool
is_declaration_only_class_type(const type_base_sptr& t)
{return is_declaration_only_class_or_union_type(t.get());}

/// Test if a type is a @ref class_or_union.
///
/// @param t the type to consider.
///
/// @return the @ref class_or_union is @p is a @ref class_or_union, or
/// nil otherwise.
class_or_union*
is_class_or_union_type(const type_or_decl_base* t)
{return dynamic_cast<class_or_union*>(const_cast<type_or_decl_base*>(t));}

/// Test if a type is a @ref class_or_union.
///
/// @param t the type to consider.
///
/// @return the @ref class_or_union is @p is a @ref class_or_union, or
/// nil otherwise.
shared_ptr<class_or_union>
is_class_or_union_type(const shared_ptr<type_or_decl_base>& t)
{return dynamic_pointer_cast<class_or_union>(t);}

/// Test if a type is a @ref union_decl.
///
/// @param t the type to consider.
///
/// @return true iff @p t is a union_decl.
bool
is_union_type(const type_or_decl_base& t)
{return is_union_type(&t);}

/// Test if a type is a @ref union_decl.
///
/// @param t the type to consider.
///
/// @return the @ref union_decl is @p is a @ref union_decl, or nil
/// otherwise.
union_decl*
is_union_type(const type_or_decl_base* t)
{return dynamic_cast<union_decl*>(const_cast<type_or_decl_base*>(t));}

/// Test if a type is a @ref union_decl.
///
/// @param t the type to consider.
///
/// @return the @ref union_decl is @p is a @ref union_decl, or nil
/// otherwise.
union_decl_sptr
is_union_type(const shared_ptr<type_or_decl_base>& t)
{return dynamic_pointer_cast<union_decl>(t);}

/// Test whether a type is a pointer_type_def.
///
/// @param t the type to test.
///
/// @return the @ref pointer_type_def_sptr if @p t is a
/// pointer_type_def, null otherwise.
pointer_type_def*
is_pointer_type(type_or_decl_base* t)
{
  if (!t)
    return 0;

  if (t->kind() & type_or_decl_base::POINTER_TYPE)
    return reinterpret_cast<pointer_type_def*>
      (const_cast<type_or_decl_base*>(t)->runtime_type_instance());

  return 0;
}

/// Test whether a type is a pointer_type_def.
///
/// @param t the type to test.
///
/// @return the @ref pointer_type_def_sptr if @p t is a
/// pointer_type_def, null otherwise.
const pointer_type_def*
is_pointer_type(const type_or_decl_base* t)
{
  return is_pointer_type(const_cast<type_or_decl_base*>(t));
}

/// Test whether a type is a pointer_type_def.
///
/// @param t the type to test.
///
/// @return the @ref pointer_type_def_sptr if @p t is a
/// pointer_type_def, null otherwise.
pointer_type_def_sptr
is_pointer_type(const type_or_decl_base_sptr &t)
{return dynamic_pointer_cast<pointer_type_def>(t);}

/// Test whether a type is a reference_type_def.
///
/// @param t the type to test.
///
/// @return the @ref reference_type_def_sptr if @p t is a
/// reference_type_def, null otherwise.
reference_type_def*
is_reference_type(type_or_decl_base* t)
{return dynamic_cast<reference_type_def*>(t);}

/// Test whether a type is a reference_type_def.
///
/// @param t the type to test.
///
/// @return the @ref reference_type_def_sptr if @p t is a
/// reference_type_def, null otherwise.
const reference_type_def*
is_reference_type(const type_or_decl_base* t)
{return dynamic_cast<const reference_type_def*>(t);}

/// Test whether a type is a reference_type_def.
///
/// @param t the type to test.
///
/// @return the @ref reference_type_def_sptr if @p t is a
/// reference_type_def, null otherwise.
reference_type_def_sptr
is_reference_type(const type_or_decl_base_sptr& t)
{return dynamic_pointer_cast<reference_type_def>(t);}

/// Test if a type is a pointer to void type.
///
/// Note that this looks trough typedefs or CV qualifiers to look for
/// the void pointer.
///
/// @param type the type to consider.
///
/// @return the actual void pointer if @p is a void pointer or NULL if
/// it's not.
const type_base*
is_void_pointer_type(const type_base* type)
{
  type = peel_qualified_or_typedef_type(type);

  const pointer_type_def * t = is_pointer_type(type);
  if (!t)
    return 0;

  // Look through typedefs in the pointed-to type as well.
  type_base * ty = t->get_pointed_to_type().get();
  ty = peel_qualified_or_typedef_type(ty);
  if (ty->get_environment()->is_void_type(ty))
    return ty;

  return 0;
}

/// Test whether a type is a reference_type_def.
///
/// @param t the type to test.
///
/// @return the @ref reference_type_def_sptr if @p t is a
/// reference_type_def, null otherwise.
qualified_type_def*
is_qualified_type(const type_or_decl_base* t)
{return dynamic_cast<qualified_type_def*>(const_cast<type_or_decl_base*>(t));}

/// Test whether a type is a qualified_type_def.
///
/// @param t the type to test.
///
/// @return the @ref qualified_type_def_sptr if @p t is a
/// qualified_type_def, null otherwise.
qualified_type_def_sptr
is_qualified_type(const type_or_decl_base_sptr& t)
{return dynamic_pointer_cast<qualified_type_def>(t);}

/// Strip a type from its top level no-op qualifier.
///
/// Note that a no-op qualifier is how we represents, for instance, a
/// "const reference".  As a reference is always const, that const
/// qualifier just adds noise in terms of change analysis.  Se we
/// represent it as a no-op qualifier so that we can strip it.
///
/// @param t to type to strip from its potential top-level no-op
/// qualifier.
///
/// @return If @t is a no-op qualified type, then return the first
/// underlying type that is not a no-op qualified type.
type_base_sptr
look_through_no_op_qualified_type(const type_base_sptr& t)
{
  type_base_sptr ty;
  if (qualified_type_def_sptr qt = is_qualified_type(t))
    if (qt->get_cv_quals() == qualified_type_def::CV_NONE)
      ty = qt->get_underlying_type();

  if (is_qualified_type(ty))
    return look_through_no_op_qualified_type(ty);

  return ty ? ty : t;
}

/// Test whether a type is a function_type.
///
/// @param t the type to test.
///
/// @return the @ref function_type_sptr if @p t is a
/// function_type, null otherwise.
function_type_sptr
is_function_type(const type_or_decl_base_sptr& t)
{return dynamic_pointer_cast<function_type>(t);}

/// Test whether a type is a function_type.
///
/// @param t the type to test.
///
/// @return the @ref function_type_sptr if @p t is a
/// function_type, null otherwise.
function_type*
is_function_type(type_or_decl_base* t)
{return dynamic_cast<function_type*>(t);}

/// Test whether a type is a function_type.
///
/// @param t the type to test.
///
/// @return the @ref function_type_sptr if @p t is a
/// function_type, null otherwise.
const function_type*
is_function_type(const type_or_decl_base* t)
{return dynamic_cast<const function_type*>(t);}

/// Test whether a type is a method_type.
///
/// @param t the type to test.
///
/// @return the @ref method_type_sptr if @p t is a
/// method_type, null otherwise.
method_type_sptr
is_method_type(const type_or_decl_base_sptr& t)
{return dynamic_pointer_cast<method_type>(t);}

/// Test whether a type is a method_type.
///
/// @param t the type to test.
///
/// @return the @ref method_type_sptr if @p t is a
/// method_type, null otherwise.
const method_type*
is_method_type(const type_or_decl_base* t)
{return dynamic_cast<const method_type*>(t);}

/// Test whether a type is a method_type.
///
/// @param t the type to test.
///
/// @return the @ref method_type_sptr if @p t is a
/// method_type, null otherwise.
method_type*
is_method_type(type_or_decl_base* t)
{return dynamic_cast<method_type*>(t);}

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

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

/// If a class (or union) is a decl-only class, get its definition.
/// Otherwise, just return the initial class.
///
/// @param klass the class (or union) to consider.
///
/// @return either the definition of the class, or the class itself.
class_or_union_sptr
look_through_decl_only_class(class_or_union_sptr klass)
{return is_class_or_union_type(look_through_decl_only(klass));}

/// If an enum is a decl-only enum, get its definition.
/// Otherwise, just return the initial enum.
///
/// @param the_enum the enum to consider.
///
/// @return either the definition of the enum, or the enum itself.
enum_type_decl_sptr
look_through_decl_only_enum(const enum_type_decl& the_enum)
{return is_enum_type(look_through_decl_only(the_enum));}

/// If an enum is a decl-only enum, get its definition.
/// Otherwise, just return the initial enum.
///
/// @param enom the enum to consider.
///
/// @return either the definition of the enum, or the enum itself.
enum_type_decl_sptr
look_through_decl_only_enum(enum_type_decl_sptr enom)
{return is_enum_type(look_through_decl_only(enom));}

/// If a decl is decl-only get its definition.  Otherwise, just return nil.
///
/// @param d the decl to consider.
///
/// @return either the definition of the decl, or nil.
decl_base_sptr
look_through_decl_only(const decl_base& d)
{
  decl_base_sptr decl;
  if (d.get_is_declaration_only())
    decl = d.get_definition_of_declaration();

  if (!decl)
    return decl;

  while (decl->get_is_declaration_only()
	 && decl->get_definition_of_declaration())
    decl = decl->get_definition_of_declaration();

  return decl;
}

/// If a decl is decl-only enum, get its definition.  Otherwise, just
/// return the initial decl.
///
/// @param d the decl to consider.
///
/// @return either the definition of the enum, or the decl itself.
decl_base*
look_through_decl_only(decl_base* d)
{
  if (!d)
    return d;

  decl_base* result = look_through_decl_only(*d).get();
  if (!result)
    result = d;

  return result;
}

/// If a decl is decl-only get its definition.  Otherwise, just return nil.
///
/// @param d the decl to consider.
///
/// @return either the definition of the decl, or nil.
decl_base_sptr
look_through_decl_only(const decl_base_sptr& d)
{
  if (!d)
    return d;

  decl_base_sptr result = look_through_decl_only(*d);
  if (!result)
    result = d;

  return result;
}

/// Tests if a declaration is a variable declaration.
///
/// @param decl the decl to test.
///
/// @return the var_decl_sptr iff decl is a variable declaration; nil
/// otherwise.
var_decl*
is_var_decl(const type_or_decl_base* tod)
{return dynamic_cast<var_decl*>(const_cast<type_or_decl_base*>(tod));}

/// Tests if a declaration is a variable declaration.
///
/// @param decl the decl to test.
///
/// @return the var_decl_sptr iff decl is a variable declaration; nil
/// otherwise.
var_decl_sptr
is_var_decl(const type_or_decl_base_sptr& decl)
{return dynamic_pointer_cast<var_decl>(decl);}

/// Tests if a declaration is a namespace declaration.
///
/// @param d the decalration to consider.
///
/// @return the namespace declaration if @p d is a namespace.
namespace_decl_sptr
is_namespace(const decl_base_sptr& d)
{return dynamic_pointer_cast<namespace_decl>(d);}

/// Tests if a declaration is a namespace declaration.
///
/// @param d the decalration to consider.
///
/// @return the namespace declaration if @p d is a namespace.
namespace_decl*
is_namespace(const decl_base* d)
{return dynamic_cast<namespace_decl*>(const_cast<decl_base*>(d));}

/// Tests whether a decl is a template parameter composition type.
///
/// @param decl the declaration to consider.
///
/// @return true iff decl is a template parameter composition type.
bool
is_template_parm_composition_type(const shared_ptr<decl_base> decl)
{
  return (decl
	  && is_at_template_scope(decl)
	  && is_type(decl)
	  && !is_template_parameter(decl));
}

/// Test whether a decl is the pattern of a function template.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is the pattern of a function template.
bool
is_function_template_pattern(const shared_ptr<decl_base> decl)
{
  return (decl
	  && dynamic_pointer_cast<function_decl>(decl)
	  && dynamic_cast<template_decl*>(decl->get_scope()));
}

/// Test if a type is an array_type_def.
///
/// @param type the type to consider.
///
/// @return true iff @p type is an array_type_def.
array_type_def*
is_array_type(const type_or_decl_base* type)
{return dynamic_cast<array_type_def*>(const_cast<type_or_decl_base*>(type));}

/// Test if a type is an array_type_def.
///
/// @param type the type to consider.
///
/// @return true iff @p type is an array_type_def.
array_type_def_sptr
is_array_type(const type_or_decl_base_sptr& type)
{return dynamic_pointer_cast<array_type_def>(type);}

/// Tests if the element of a given array is a qualified type.
///
/// @param array the array type to consider.
///
/// @return the qualified element of the array iff it's a qualified
/// type.  Otherwise, return a nil object.
qualified_type_def_sptr
is_array_of_qualified_element(const array_type_def_sptr& array)
{
  if (!array)
    return qualified_type_def_sptr();

  return is_qualified_type(array->get_element_type());
}

/// Test if an array type is an array to a qualified element type.
///
/// @param type the array type to consider.
///
/// @return true the array @p type iff it's an array to a qualified
/// element type.
array_type_def_sptr
is_array_of_qualified_element(const type_base_sptr& type)
{
  if (array_type_def_sptr array = is_array_type(type))
    if (is_array_of_qualified_element(array))
      return array;

  return array_type_def_sptr();
}

/// Test if a type is a typedef of an array.
///
/// Note that the function looks through qualified and typedefs types
/// of the underlying type of the current typedef.  In other words, if
/// we are looking at a typedef of a CV-qualified array, or at a
/// typedef of a CV-qualified typedef of an array, this function will
/// still return TRUE.
///
/// @param t the type to consider.
///
/// @return true if t is a typedef which underlying type is an array.
/// That array might be either cv-qualified array or a typedef'ed
/// array, or a combination of both.
array_type_def_sptr
is_typedef_of_array(const type_base_sptr& t)
{
  array_type_def_sptr result;

  if (typedef_decl_sptr typdef = is_typedef(t))
    {
      type_base_sptr u =
	peel_qualified_or_typedef_type(typdef->get_underlying_type());
      result = is_array_type(u);
    }

  return result;
}

/// Test if a type is an array_type_def::subrange_type.
///
/// @param type the type to consider.
///
/// @return the array_type_def::subrange_type which @p type is a type
/// of, or nil if it's not of that type.
array_type_def::subrange_type*
is_subrange_type(const type_or_decl_base *type)
{
  return dynamic_cast<array_type_def::subrange_type*>
    (const_cast<type_or_decl_base*>(type));
}

/// Test if a type is an array_type_def::subrange_type.
///
/// @param type the type to consider.
///
/// @return the array_type_def::subrange_type which @p type is a type
/// of, or nil if it's not of that type.
array_type_def::subrange_sptr
is_subrange_type(const type_or_decl_base_sptr &type)
{return dynamic_pointer_cast<array_type_def::subrange_type>(type);}

/// Tests whether a decl is a template.
///
/// @param decl the decl to consider.
///
/// @return true iff decl is a function template, class template, or
/// template template parameter.
bool
is_template_decl(const shared_ptr<decl_base> decl)
{return decl && dynamic_pointer_cast<template_decl>(decl);}

/// This enum describe the kind of entity to lookup, while using the
/// lookup API.
enum lookup_entity_kind
{
  LOOKUP_ENTITY_TYPE,
  LOOKUP_ENTITY_VAR,
};

/// Find the first relevant delimiter (the "::" string) in a fully
/// qualified C++ type name, starting from a given position.  The
/// delimiter returned separates a type name from the name of its
/// context.
///
/// This is supposed to work correctly on names in cases like this:
///
///    foo<ns1::name1, ns2::name2>
///
/// In that case when called with with parameter @p begin set to 0, no
/// delimiter is returned, because the type name in this case is:
/// 'foo<ns1::name1, ns2::name2>'.
///
/// But in this case:
///
///   foo<p1, bar::name>::some_type
///
/// The "::" returned is the one right before 'some_type'.
///
/// @param fqn the fully qualified name of the type to consider.
///
/// @param begin the position from which to look for the delimiter.
///
/// @param delim_pos out parameter. Is set to the position of the
/// delimiter iff the function returned true.
///
/// @return true iff the function found and returned the delimiter.
static bool
find_next_delim_in_cplus_type(const string&	fqn,
			      size_t		begin,
			      size_t&		delim_pos)
{
  int angle_count = 0;
  bool found = false;
  size_t i = begin;
  for (; i < fqn.size(); ++i)
    {
      if (fqn[i] == '<')
	++angle_count;
      else if (fqn[i] == '>')
	--angle_count;
      else if (i + 1 < fqn.size()
	       && !angle_count
	       && fqn[i] == ':'
	       && fqn[i+1] == ':')
	{
	  delim_pos = i;
	  found = true;
	  break;
	}
    }
  return found;
}

/// Decompose a fully qualified name into the list of its components.
///
/// @param fqn the fully qualified name to decompose.
///
/// @param comps the resulting list of component to fill.
void
fqn_to_components(const string& fqn,
		  list<string>& comps)
{
  string::size_type fqn_size = fqn.size(), comp_begin = 0, comp_end = fqn_size;
  do
    {
      if (!find_next_delim_in_cplus_type(fqn, comp_begin, comp_end))
	comp_end = fqn_size;

      string comp = fqn.substr(comp_begin, comp_end - comp_begin);
      comps.push_back(comp);

      comp_begin = comp_end + 2;
      if (comp_begin >= fqn_size)
	break;
    } while (true);
}

/// Turn a set of qualified name components (that name a type) into a
/// qualified name string.
///
/// @param comps the name components
///
/// @return the resulting string, which would be the qualified name of
/// a type.
string
components_to_type_name(const list<string>& comps)
{
  string result;
  for (list<string>::const_iterator c = comps.begin();
       c != comps.end();
       ++c)
    if (c == comps.begin())
      result = *c;
    else
      result += "::" + *c;
  return result;
}

/// This predicate returns true if a given container iterator points
/// to the last element of the container, false otherwise.
///
/// @tparam T the type of the container of the iterator.
///
/// @param container the container the iterator points into.
///
/// @param i the iterator to consider.
///
/// @return true iff the iterator points to the last element of @p
/// container.
template<typename T>
static bool
iterator_is_last(T& container,
		 typename T::const_iterator i)
{
  typename T::const_iterator next = i;
  ++next;
  return (next == container.end());
}

//--------------------------------
// <type and decls lookup stuff>
// ------------------------------

/// Lookup all the type*s* that have a given fully qualified name.
///
/// @param type_name the fully qualified name of the type to
/// lookup.
///
/// @param type_map the map to look into.
///
/// @return the vector containing the types named @p type_name.  If
/// the lookup didn't yield any type, then this function returns nil.
static const type_base_wptrs_type*
lookup_types_in_map(const interned_string& type_name,
		    const istring_type_base_wptrs_map_type& type_map)
{
  istring_type_base_wptrs_map_type::const_iterator i = type_map.find(type_name);
  if (i != type_map.end())
    return &i->second;
  return 0;
}

/// Lookup a type (with a given name) in a map that associates a type
/// name to a type.  If there are several types with a given name,
/// then return the last of such types, that is, the last one that got
/// registered.
///
/// @tparam TypeKind the type of the type this function is supposed to
/// return.
///
/// @param type_name the name of the type to lookup.
///
/// @param type_map the map in which to look.
///
/// @return a shared_ptr to the type found.  If no type was found or
/// if the type found was not of type @p TypeKind then the function
/// returns nil.
template <class TypeKind>
static shared_ptr<TypeKind>
lookup_type_in_map(const interned_string& type_name,
		   const istring_type_base_wptrs_map_type& type_map)
{
  istring_type_base_wptrs_map_type::const_iterator i = type_map.find(type_name);
  if (i != type_map.end())
    return dynamic_pointer_cast<TypeKind>(type_base_sptr(i->second.back()));
  return shared_ptr<TypeKind>();
}

/// Lookup a basic type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the basic type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the basic type found or nil if no basic type was found.
type_decl_sptr
lookup_basic_type(const interned_string& type_name, const translation_unit& tu)
{
  return lookup_type_in_map<type_decl>(type_name,
				       tu.get_types().basic_types());
}

/// Lookup a basic type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the basic type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the basic type found or nil if no basic type was found.
type_decl_sptr
lookup_basic_type(const string& type_name, const translation_unit& tu)
{
  const environment* env = tu.get_environment();
  ABG_ASSERT(env);

  interned_string s = env->intern(type_name);
  return lookup_basic_type(s, tu);
}

/// Lookup a class type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param fqn the fully qualified name of the class type node to look
/// up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the class type IR node found, NULL
/// otherwise.
class_decl_sptr
lookup_class_type(const string& fqn, const translation_unit& tu)
{
  const environment* env = tu.get_environment();
  ABG_ASSERT(env);

  interned_string s = env->intern(fqn);
  return lookup_class_type(s, tu);
}

/// Lookup a class type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the class type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the class type found or nil if no class type was found.
class_decl_sptr
lookup_class_type(const interned_string& type_name, const translation_unit& tu)
{
  return lookup_type_in_map<class_decl>(type_name,
					tu.get_types().class_types());
}

/// Lookup a union type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the union type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the union type found or nil if no union type was found.
union_decl_sptr
lookup_union_type(const interned_string& type_name, const translation_unit& tu)
{
  return lookup_type_in_map<union_decl>(type_name,
					tu.get_types().union_types());
}

/// Lookup a union type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param fqn the fully qualified name of the type to lookup.
///
/// @param tu the translation unit to look into.
///
/// @return the union type found or nil if no union type was found.
union_decl_sptr
lookup_union_type(const string& fqn, const translation_unit& tu)
{
  const environment* env = tu.get_environment();
  ABG_ASSERT(env);

  interned_string s = env->intern(fqn);
  return lookup_union_type(s, tu);
}

/// Lookup a union type in a given corpus, from its location.
///
/// @param loc the location of the union type to look for.
///
/// @param corp the corpus to look it from.
///
/// @return the resulting union_decl.
union_decl_sptr
lookup_union_type_per_location(const interned_string &loc, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m =
    corp.get_type_per_loc_map().union_types();
  union_decl_sptr result = lookup_type_in_map<union_decl>(loc, m);

  return result;
}

/// Lookup a union type in a given corpus, from its location.
///
/// @param loc the location of the union type to look for.
///
/// @param corp the corpus to look it from.
///
/// @return the resulting union_decl.
union_decl_sptr
lookup_union_type_per_location(const string& loc, const corpus& corp)
{
  const environment* env = corp.get_environment();
  ABG_ASSERT(env);

  return lookup_union_type_per_location(env->intern(loc), corp);
}

/// Lookup an enum type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the enum type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the enum type found or nil if no enum type was found.
enum_type_decl_sptr
lookup_enum_type(const interned_string& type_name, const translation_unit& tu)
{
  return lookup_type_in_map<enum_type_decl>(type_name,
					    tu.get_types().enum_types());
}

/// Lookup an enum type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the enum type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the enum type found or nil if no enum type was found.
enum_type_decl_sptr
lookup_enum_type(const string& type_name, const translation_unit& tu)
{
  const environment* env = tu.get_environment();
  ABG_ASSERT(env);

  interned_string s = env->intern(type_name);
  return lookup_enum_type(s, tu);
}

/// Lookup a typedef type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the typedef type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the typedef type found or nil if no typedef type was
/// found.
typedef_decl_sptr
lookup_typedef_type(const interned_string& type_name,
		    const translation_unit& tu)
{
  return lookup_type_in_map<typedef_decl>(type_name,
					  tu.get_types().typedef_types());
}

/// Lookup a typedef type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the typedef type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the typedef type found or nil if no typedef type was
/// found.
typedef_decl_sptr
lookup_typedef_type(const string& type_name, const translation_unit& tu)
{
  const environment* env = tu.get_environment();
  ABG_ASSERT(env);

  interned_string s = env->intern(type_name);
  return lookup_typedef_type(s, tu);
}

/// Lookup a qualified type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the qualified type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the qualified type found or nil if no qualified type was
/// found.
qualified_type_def_sptr
lookup_qualified_type(const interned_string& type_name,
		      const translation_unit& tu)
{
  const type_maps& m = tu.get_types();
  return lookup_type_in_map<qualified_type_def>(type_name,
						m.qualified_types());
}

/// Lookup a qualified type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param underlying_type the underying type of the qualified type to
/// look up.
///
/// @param quals the CV-qualifiers of the qualified type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the qualified type found or nil if no qualified type was
/// found.
qualified_type_def_sptr
lookup_qualified_type(const type_base_sptr& underlying_type,
		      qualified_type_def::CV quals,
		      const translation_unit& tu)
{
  interned_string type_name = get_name_of_qualified_type(underlying_type,
							 quals);
  return lookup_qualified_type(type_name, tu);
}

/// Lookup a pointer type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the pointer type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the pointer type found or nil if no pointer type was
/// found.
pointer_type_def_sptr
lookup_pointer_type(const interned_string& type_name,
		    const translation_unit& tu)
{
  const type_maps& m = tu.get_types();
  return lookup_type_in_map<pointer_type_def>(type_name,
					      m.pointer_types());
}

/// Lookup a pointer type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the pointer type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the pointer type found or nil if no pointer type was
/// found.
pointer_type_def_sptr
lookup_pointer_type(const string& type_name, const translation_unit& tu)
{
  const environment* env = tu.get_environment();
  ABG_ASSERT(env);

  interned_string s = env->intern(type_name);
  return lookup_pointer_type(s, tu);
}

/// Lookup a pointer type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param pointed_to_type the pointed-to-type of the pointer to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the pointer type found or nil if no pointer type was
/// found.
pointer_type_def_sptr
lookup_pointer_type(const type_base_sptr& pointed_to_type,
		    const translation_unit& tu)
{
  interned_string type_name = get_name_of_pointer_to_type(*pointed_to_type);
  return lookup_pointer_type(type_name, tu);
}

/// Lookup a reference type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the reference type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the reference type found or nil if no reference type was
/// found.
reference_type_def_sptr
lookup_reference_type(const interned_string& type_name,
		      const translation_unit& tu)
{
  const type_maps& m = tu.get_types();
  return lookup_type_in_map<reference_type_def>(type_name,
						m.reference_types());
}

/// Lookup a reference type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param pointed_to_type the pointed-to-type of the reference to
/// look up.
///
/// @param tu the translation unit to look into.
///
/// @return the reference type found or nil if no reference type was
/// found.
const reference_type_def_sptr
lookup_reference_type(const type_base_sptr& pointed_to_type,
		      bool lvalue_reference,
		      const translation_unit& tu)
{
  interned_string type_name =
    get_name_of_reference_to_type(*pointed_to_type, lvalue_reference);
  return lookup_reference_type(type_name, tu);
}

/// Lookup an array type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the array type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the array type found or nil if no array type was found.
array_type_def_sptr
lookup_array_type(const interned_string& type_name,
		  const translation_unit& tu)
{
  const type_maps& m = tu.get_types();
  return lookup_type_in_map<array_type_def>(type_name,
					    m.array_types());
}

/// Lookup a function type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param type_name the name of the type to lookup.
///
/// @param tu the translation unit to look into.
///
/// @return the function type found, or NULL of none was found.
function_type_sptr
lookup_function_type(const interned_string& type_name,
		     const translation_unit& tu)
{
  const type_maps& m = tu.get_types();
  return lookup_type_in_map<function_type>(type_name,
					   m.function_types());
}

/// Lookup a function type from a translation unit.
///
/// This walks all the function types held by the translation unit and
/// compare their sub-type *names*.  If the names match then return
/// the function type found in the translation unit.
///
/// @param t the function type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the function type found, or NULL of none was found.
function_type_sptr
lookup_function_type(const function_type& t,
		     const translation_unit& tu)
{
  interned_string type_name = get_type_name(t);
  return lookup_function_type(type_name, tu);
}

/// Lookup a function type from a translation unit.
///
/// This is done by looking the type up in the type map that is
/// maintained in the translation unit.  So this is as fast as
/// possible.
///
/// @param t the function type to look for.
///
/// @param tu the translation unit to look into.
///
/// @return the function type found, or NULL of none was found.
function_type_sptr
lookup_function_type(const function_type_sptr& t,
		     const translation_unit& tu)
{return lookup_function_type(*t, tu);}

/// Lookup a type in a translation unit.
///
/// @param fqn the fully qualified name of the type to lookup.
///
/// @param tu the translation unit to consider.
///
/// @return the declaration of the type if found, NULL otherwise.
const type_base_sptr
lookup_type(const interned_string& fqn,
	    const translation_unit& tu)
{
  type_base_sptr result;
  ((result = lookup_typedef_type(fqn, tu))
   || (result = lookup_class_type(fqn, tu))
   || (result = lookup_union_type(fqn, tu))
   || (result = lookup_enum_type(fqn, tu))
   || (result = lookup_qualified_type(fqn, tu))
   || (result = lookup_pointer_type(fqn, tu))
   || (result = lookup_reference_type(fqn, tu))
   || (result = lookup_array_type(fqn, tu))
   || (result = lookup_function_type(fqn, tu))
   || (result = lookup_basic_type(fqn, tu)));

  return result;
}

/// Lookup a type in a translation unit, starting from the global
/// namespace.
///
/// @param fqn the fully qualified name of the type to lookup.
///
/// @param tu the translation unit to consider.
///
/// @return the declaration of the type if found, NULL otherwise.
type_base_sptr
lookup_type(const string& fqn, const translation_unit& tu)
{
  const environment *env = tu.get_environment();
  ABG_ASSERT(env);
  interned_string ifqn = env->intern(fqn);
  return lookup_type(ifqn, tu);
}

/// Lookup a type from a translation unit.
///
/// @param fqn the components of the fully qualified name of the node
/// to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the IR node found, NULL otherwise.
const type_base_sptr
lookup_type(const type_base_sptr type,
	    const translation_unit& tu)
{
  interned_string type_name = get_type_name(type);
  return lookup_type(type_name, tu);
}

/// Lookup a type in a scope.
///
/// This is really slow as it walks the member types of the scope in
/// sequence to find the type with a given name.
///
/// If possible, users should prefer looking up types from the
/// enclosing translation unit or even ABI corpus because both the
/// translation unit and the corpus have a map of type, indexed by
/// their name.  Looking up a type from those maps is thus much
/// faster.
///
/// @param fqn the fully qualified name of the type to lookup.
///
/// @param skope the scope to look into.
///
/// @return the declaration of the type if found, NULL otherwise.
const type_base_sptr
lookup_type_in_scope(const string& fqn,
		     const scope_decl_sptr& skope)
{
  list<string> comps;
  fqn_to_components(fqn, comps);
  return lookup_type_in_scope(comps, skope);
}

/// Lookup a @ref var_decl in a scope.
///
/// @param fqn the fuly qualified name of the @var_decl to lookup.
///
/// @param skope the scope to look into.
///
/// @return the declaration of the @ref var_decl if found, NULL
/// otherwise.
const decl_base_sptr
lookup_var_decl_in_scope(const string& fqn,
			 const scope_decl_sptr& skope)
{
  list<string> comps;
  fqn_to_components(fqn, comps);
  return lookup_var_decl_in_scope(comps, skope);
}

/// A generic function (template) to get the name of a node, whatever
/// node it is.  This has to be specialized for the kind of node we
/// want.
///
/// Note that a node is a member of a scope.
///
/// @tparam NodeKind the kind of node to consider.
///
/// @param node the node to get the name from.
///
/// @return the name of the node.
template<typename NodeKind>
static const interned_string&
get_node_name(shared_ptr<NodeKind> node);

/// Gets the name of a class_decl node.
///
/// @param node the decl_base node to get the name from.
///
/// @return the name of the node.
template<>
const interned_string&
get_node_name(class_decl_sptr node)
{return node->get_name();}

/// Gets the name of a type_base node.
///
/// @param node the type_base node to get the name from.
///
/// @return the name of the node.
template<>
const interned_string&
get_node_name(type_base_sptr node)
{return get_type_declaration(node)->get_name();}

/// Gets the name of a var_decl node.
///
/// @param node the var_decl node to get the name from.
///
/// @return the name of the node.
template<>
const interned_string&
get_node_name(var_decl_sptr node)
{return node->get_name();}

/// Generic function to get the declaration of a given node, whatever
/// it is.  There has to be specializations for the kind of the nodes
/// we want to support.
///
/// @tparam NodeKind the type of the node we are looking at.
///
/// @return the declaration.
template<typename NodeKind>
static decl_base_sptr
convert_node_to_decl(shared_ptr<NodeKind> node);

/// Lookup a node in a given scope.
///
/// @tparam the type of the node to lookup.
///
/// @param fqn the components of the fully qualified name of the node
/// to lookup.
///
/// @param skope the scope to look into.
///
/// @return the declaration of the looked up node, or NULL if it
/// wasn't found.
template<typename NodeKind>
static const type_or_decl_base_sptr
lookup_node_in_scope(const list<string>& fqn,
		     const scope_decl_sptr& skope)
{
  type_or_decl_base_sptr resulting_decl;
  shared_ptr<NodeKind> node;
  bool it_is_last = false;
  scope_decl_sptr cur_scope = skope, new_scope, scope;

  for (list<string>::const_iterator c = fqn.begin(); c != fqn.end(); ++c)
    {
      new_scope.reset();
      it_is_last = iterator_is_last(fqn, c);
      for (scope_decl::declarations::const_iterator m =
	     cur_scope->get_member_decls().begin();
	   m != cur_scope->get_member_decls().end();
	   ++m)
	{
	  if (!it_is_last)
	    {
	      // looking for a scope
	      scope = dynamic_pointer_cast<scope_decl>(*m);
	      if (scope && scope->get_name() == *c)
		{
		  new_scope = scope;
		  break;
		}
	    }
	  else
	    {
	      //looking for a final type.
	      node = dynamic_pointer_cast<NodeKind>(*m);
	      if (node && get_node_name(node) == *c)
		{
		  if (class_decl_sptr cl =
		      dynamic_pointer_cast<class_decl>(node))
		    if (cl->get_is_declaration_only()
			&& !cl->get_definition_of_declaration())
		      continue;
		  resulting_decl = node;
		  break;
		}
	    }
	}
      if (!new_scope && !resulting_decl)
	return decl_base_sptr();
      cur_scope = new_scope;
    }
  ABG_ASSERT(resulting_decl);
  return resulting_decl;
}

/// lookup a type in a scope.
///
///
/// This is really slow as it walks the member types of the scope in
/// sequence to find the type with a given name.
///
/// If possible, users should prefer looking up types from the
/// enclosing translation unit or even ABI corpus because both the
/// translation unit and the corpus have a map of type, indexed by
/// their name.  Looking up a type from those maps is thus much
/// faster.
///
/// @param comps the components of the fully qualified name of the
/// type to lookup.
///
/// @param skope the scope to look into.
///
/// @return the declaration of the type found.
const type_base_sptr
lookup_type_in_scope(const list<string>& comps,
		     const scope_decl_sptr& scope)
{return is_type(lookup_node_in_scope<type_base>(comps, scope));}

/// lookup a type in a scope.
///
/// This is really slow as it walks the member types of the scope in
/// sequence to find the type with a given name.
///
/// If possible, users should prefer looking up types from the
/// enclosing translation unit or even ABI corpus because both the
/// translation unit and the corpus have a map of type, indexed by
/// their name.  Looking up a type from those maps is thus much
/// faster.
///
/// @param type the type to look for.
///
/// @param access_path a vector of scopes the path of scopes to follow
/// before reaching the scope into which to look for @p type.  Note
/// that the deepest scope (the one immediately containing @p type) is
/// at index 0 of this vector, and the top-most scope is the last
/// element of the vector.
///
/// @param scope the top-most scope into which to look for @p type.
///
/// @return the scope found in @p scope, or NULL if it wasn't found.
static const type_base_sptr
lookup_type_in_scope(const type_base& type,
		     const vector<scope_decl*>& access_path,
		     const scope_decl* scope)
{
  vector<scope_decl*> a = access_path;
  type_base_sptr result;

  scope_decl* first_scope = 0;
  if (!a.empty())
    {
      first_scope = a.back();
      ABG_ASSERT(first_scope->get_name() == scope->get_name());
      a.pop_back();
    }

  if (a.empty())
    {
      interned_string n = get_type_name(type, false);
      for (scope_decl::declarations::const_iterator i =
	     scope->get_member_decls().begin();
	   i != scope->get_member_decls().end();
	   ++i)
	if (is_type(*i) && (*i)->get_name() == n)
	  {
	    result = is_type(*i);
	    break;
	  }
    }
  else
    {
      first_scope = a.back();
      interned_string scope_name, cur_scope_name = first_scope->get_name();
      for (scope_decl::scopes::const_iterator i =
	     scope->get_member_scopes().begin();
	   i != scope->get_member_scopes().end();
	   ++i)
	{
	  scope_name = (*i)->get_name();
	  if (scope_name == cur_scope_name)
	    {
	      result = lookup_type_in_scope(type, a, (*i).get());
	      break;
	    }
	}
    }
  return result;
}

/// lookup a type in a scope.
///
/// This is really slow as it walks the member types of the scope in
/// sequence to find the type with a given name.
///
/// If possible, users should prefer looking up types from the
/// enclosing translation unit or even ABI corpus because both the
/// translation unit and the corpus have a map of type, indexed by
/// their name.  Looking up a type from those maps is thus much
/// faster.
///
/// @param type the type to look for.
///
/// @param scope the top-most scope into which to look for @p type.
///
/// @return the scope found in @p scope, or NULL if it wasn't found.
static const type_base_sptr
lookup_type_in_scope(const type_base_sptr type,
		     const scope_decl* scope)
{
  if (!type || is_function_type(type))
    return type_base_sptr();

  decl_base_sptr type_decl = get_type_declaration(type);
  ABG_ASSERT(type_decl);
  vector<scope_decl*> access_path;
  for (scope_decl* s = type_decl->get_scope(); s != 0; s = s->get_scope())
    {
      access_path.push_back(s);
      if (is_global_scope(s))
	break;
    }
  return lookup_type_in_scope(*type, access_path, scope);
}

/// Lookup a type from a translation unit by walking the scopes of the
/// translation unit in sequence and looking into them.
///
/// This is really slow as it walks the member types of the scopes in
/// sequence to find the type with a given name.
///
/// If possible, users should prefer looking up types from the
/// translation unit or even ABI corpus in a more direct way, by using
/// the lookup_type() functins.
///
///
/// This is because both the translation unit and the corpus have a
/// map of types, indexed by their name.  Looking up a type from those
/// maps is thus much faster.  @param fqn the components of the fully
/// qualified name of the node to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the IR node found, NULL otherwise.
const type_base_sptr
lookup_type_through_scopes(const type_base_sptr type,
			   const translation_unit& tu)
{
  if (function_type_sptr fn_type = is_function_type(type))
    return lookup_function_type(fn_type, tu);
  return lookup_type_in_scope(type, tu.get_global_scope().get());
}

/// lookup a var_decl in a scope.
///
/// @param comps the components of the fully qualified name of the
/// var_decl to lookup.
///
/// @param skope the scope to look into.
const decl_base_sptr
lookup_var_decl_in_scope(const std::list<string>& comps,
			 const scope_decl_sptr& skope)
{return is_var_decl(lookup_node_in_scope<var_decl>(comps, skope));}

/// Lookup an IR node from a translation unit.
///
/// @tparam NodeKind the type of the IR node to lookup from the
/// translation unit.
///
/// @param fqn the components of the fully qualified name of the node
/// to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the IR node found, NULL otherwise.
template<typename NodeKind>
static const type_or_decl_base_sptr
lookup_node_in_translation_unit(const list<string>& fqn,
				const translation_unit& tu)
{return lookup_node_in_scope<NodeKind>(fqn, tu.get_global_scope());}

/// Lookup a type from a translation unit by walking its scopes in
/// sequence and by looking into them.
///
/// This is much slower than using the lookup_type() function.
///
/// @param fqn the components of the fully qualified name of the node
/// to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the IR node found, NULL otherwise.
type_base_sptr
lookup_type_through_scopes(const list<string>& fqn,
			   const translation_unit& tu)
{return is_type(lookup_node_in_translation_unit<type_base>(fqn, tu));}


/// Lookup a class type from a translation unit by walking its scopes
/// in sequence and by looking into them.
///
/// This is much slower than using the lookup_class_type() function
/// because it walks all the scopes of the translation unit in
/// sequence and lookup the types to find one that has a given name.
///
/// @param fqn the components of the fully qualified name of the class
/// type node to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the class type IR node found, NULL
/// otherwise.
class_decl_sptr
lookup_class_type_through_scopes(const list<string>& fqn,
				 const translation_unit& tu)
{return is_class_type(lookup_node_in_translation_unit<class_decl>(fqn, tu));}

/// Lookup a basic type from all the translation units of a given
/// corpus.
///
/// @param fqn the components of the fully qualified name of the basic
/// type node to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the basic type IR node found, NULL
/// otherwise.
static type_decl_sptr
lookup_basic_type_through_translation_units(const interned_string& type_name,
					    const corpus& abi_corpus)
{
  type_decl_sptr result;

  for (translation_units::const_iterator tu =
	 abi_corpus.get_translation_units().begin();
       tu != abi_corpus.get_translation_units().end();
       ++tu)
    if ((result = lookup_basic_type(type_name, **tu)))
      break;

  return result;
}

/// Lookup a union type from all the translation units of a given
/// corpus.
///
/// @param fqn the components of the fully qualified name of the union
/// type node to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the union type IR node found, NULL
/// otherwise.
static union_decl_sptr
lookup_union_type_through_translation_units(const interned_string& type_name,
					    const corpus & abi_corpus)
{
 union_decl_sptr result;

  for (translation_units::const_iterator tu =
	 abi_corpus.get_translation_units().begin();
       tu != abi_corpus.get_translation_units().end();
       ++tu)
    if ((result = lookup_union_type(type_name, **tu)))
      break;

  return result;
}

/// Lookup an enum type from all the translation units of a given
/// corpus.
///
/// @param fqn the components of the fully qualified name of the enum
/// type node to look up.
///
/// @param tu the translation unit to perform lookup from.
///
/// @return the declaration of the enum type IR node found, NULL
/// otherwise.
static enum_type_decl_sptr
lookup_enum_type_through_translation_units(const interned_string& type_name,
					   const corpus & abi_corpus)
{
  enum_type_decl_sptr result;

  for (translation_units::const_iterator tu =
	 abi_corpus.get_translation_units().begin();
       tu != abi_corpus.get_translation_units().end();
       ++tu)
    if ((result = lookup_enum_type(type_name, **tu)))
      break;

  return result;
}

/// Lookup a typedef type definition in all the translation units of a
/// given ABI corpus.
///
/// @param @param qn the fully qualified name of the typedef type to lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
static typedef_decl_sptr
lookup_typedef_type_through_translation_units(const interned_string& type_name,
					      const corpus & abi_corpus)
{
  typedef_decl_sptr result;

  for (translation_units::const_iterator tu =
	 abi_corpus.get_translation_units().begin();
       tu != abi_corpus.get_translation_units().end();
       ++tu)
    if ((result = lookup_typedef_type(type_name, **tu)))
      break;

  return result;
}

/// Lookup a qualified type definition in all the translation units of a
/// given ABI corpus.
///
/// @param @param qn the fully qualified name of the qualified type to
/// lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
static qualified_type_def_sptr
lookup_qualified_type_through_translation_units(const interned_string& t_name,
						const corpus & abi_corpus)
{
  qualified_type_def_sptr result;

  for (translation_units::const_iterator tu =
	 abi_corpus.get_translation_units().begin();
       tu != abi_corpus.get_translation_units().end();
       ++tu)
    if ((result = lookup_qualified_type(t_name, **tu)))
      break;

  return result;
}

/// Lookup a pointer type definition in all the translation units of a
/// given ABI corpus.
///
/// @param @param qn the fully qualified name of the pointer type to
/// lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
static pointer_type_def_sptr
lookup_pointer_type_through_translation_units(const interned_string& type_name,
					      const corpus & abi_corpus)
{
  pointer_type_def_sptr result;

  for (translation_units::const_iterator tu =
	 abi_corpus.get_translation_units().begin();
       tu != abi_corpus.get_translation_units().end();
       ++tu)
    if ((result = lookup_pointer_type(type_name, **tu)))
      break;

  return result;
}

/// Lookup a reference type definition in all the translation units of a
/// given ABI corpus.
///
/// @param @param qn the fully qualified name of the reference type to
/// lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
static reference_type_def_sptr
lookup_reference_type_through_translation_units(const interned_string& t_name,
						const corpus & abi_corpus)
{
  reference_type_def_sptr result;

  for (translation_units::const_iterator tu =
	 abi_corpus.get_translation_units().begin();
       tu != abi_corpus.get_translation_units().end();
       ++tu)
    if ((result = lookup_reference_type(t_name, **tu)))
      break;

  return result;
}

/// Lookup a array type definition in all the translation units of a
/// given ABI corpus.
///
/// @param @param qn the fully qualified name of the array type to
/// lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
static array_type_def_sptr
lookup_array_type_through_translation_units(const interned_string& type_name,
					    const corpus & abi_corpus)
{
  array_type_def_sptr result;

  for (translation_units::const_iterator tu =
	 abi_corpus.get_translation_units().begin();
       tu != abi_corpus.get_translation_units().end();
       ++tu)
    if ((result = lookup_array_type(type_name, **tu)))
      break;

  return result;
}

/// Lookup a function type definition in all the translation units of
/// a given ABI corpus.
///
/// @param @param qn the fully qualified name of the function type to
/// lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
static function_type_sptr
lookup_function_type_through_translation_units(const interned_string& type_name,
					       const corpus & abi_corpus)
{
  function_type_sptr result;

  for (translation_units::const_iterator tu =
	 abi_corpus.get_translation_units().begin();
       tu != abi_corpus.get_translation_units().end();
       ++tu)
    if ((result = lookup_function_type(type_name, **tu)))
      break;

  return result;
}

/// Lookup a type definition in all the translation units of a given
/// ABI corpus.
///
/// @param @param qn the fully qualified name of the type to lookup.
///
/// @param abi_corpus the ABI corpus which to look the type up in.
///
/// @return the type definition if any was found, or a NULL pointer.
type_base_sptr
lookup_type_through_translation_units(const string& qn,
				      const corpus& abi_corpus)
{
  type_base_sptr result;

  for (translation_units::const_iterator tu =
	 abi_corpus.get_translation_units().begin();
       tu != abi_corpus.get_translation_units().end();
       ++tu)
    if ((result = lookup_type(qn, **tu)))
      break;

  return result;
}

/// Lookup a type from a given translation unit present in a give corpus.
///
/// @param type_name the name of the type to look for.
///
/// @parm tu_path the path of the translation unit to consider.
///
/// @param corp the corpus to consider.
///
/// @return the resulting type, if any.
type_base_sptr
lookup_type_from_translation_unit(const string& type_name,
				  const string& tu_path,
				  const corpus& corp)
{
  string_tu_map_type::const_iterator i =  corp.priv_->path_tu_map.find(tu_path);
  if (i == corp.priv_->path_tu_map.end())
    return type_base_sptr();

  translation_unit_sptr tu = i->second;
  ABG_ASSERT(tu);

  type_base_sptr t = lookup_type(type_name, *tu);
  return t;
}

/// Look into an ABI corpus for a function type.
///
/// @param fn_type the function type to be looked for in the ABI
/// corpus.
///
/// @param corpus the ABI corpus into which to look for the function
/// type.
///
/// @return the function type found in the corpus.
function_type_sptr
lookup_or_synthesize_fn_type(const function_type_sptr& fn_t,
			     const corpus& corpus)
{
  ABG_ASSERT(fn_t);

  function_type_sptr result;

  if ((result = lookup_function_type(fn_t, corpus)))
    return result;

  for (translation_units::const_iterator i =
	 corpus.get_translation_units().begin();
       i != corpus.get_translation_units().end();
       ++i)
    if ((result = synthesize_function_type_from_translation_unit(*fn_t,
								 **i)))
      return result;

  return result;
}

/// Look into a given corpus to find a type which has the same
/// qualified name as a giventype.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the type which has the same qualified name as the type we
/// are looking for.
///
/// @param corp the ABI corpus to look into for the type.
type_decl_sptr
lookup_basic_type(const type_decl& t, const corpus& corp)
{return lookup_basic_type(t.get_name(), corp);}

/// Look into a given corpus to find a basic type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the basic type to look
/// for.
///
/// @param corp the corpus to look into.
type_decl_sptr
lookup_basic_type(const interned_string &qualified_name, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m = corp.get_types().basic_types();
  type_decl_sptr result;

  if (!m.empty())
    result = lookup_type_in_map<type_decl>(qualified_name, m);
  else
    result = lookup_basic_type_through_translation_units(qualified_name, corp);

  return result;
}

/// Lookup a @ref type_decl type from a given corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to consider.
///
/// @return the resulting basic type, if any.
type_decl_sptr
lookup_basic_type_per_location(const interned_string &loc,
			       const corpus &corp)
{
  const istring_type_base_wptrs_map_type& m =
    corp.get_type_per_loc_map().basic_types();
  type_decl_sptr result;

  result = lookup_type_in_map<type_decl>(loc, m);

  return result;
}

/// Lookup a @ref type_decl type from a given corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to consider.
///
/// @return the resulting basic type, if any.
type_decl_sptr
lookup_basic_type_per_location(const string &loc, const corpus &corp)
{
  const environment* env = corp.get_environment();
  ABG_ASSERT(env);

  return lookup_basic_type_per_location(env->intern(loc), corp);
}

/// Look into a given corpus to find a basic type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the basic type to look
/// for.
///
/// @param corp the corpus to look into.
type_decl_sptr
lookup_basic_type(const string& qualified_name, const corpus& corp)
{
  return lookup_basic_type(corp.get_environment()->intern(qualified_name),
			   corp);
}

/// Look into a given corpus to find a class type which has the same
/// qualified name as a given type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the class decl type which has the same qualified name as
/// the type we are looking for.
///
/// @param corp the corpus to look into.
class_decl_sptr
lookup_class_type(const class_decl& t, const corpus& corp)
{
  interned_string s = get_type_name(t);
  return lookup_class_type(s, corp);
}

/// Look into a given corpus to find a class type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
class_decl_sptr
lookup_class_type(const string& qualified_name, const corpus& corp)
{
  interned_string s = corp.get_environment()->intern(qualified_name);
  return lookup_class_type(s, corp);
}

/// Look into a given corpus to find a class type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
class_decl_sptr
lookup_class_type(const interned_string& qualified_name, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m = corp.get_types().class_types();

  class_decl_sptr result = lookup_type_in_map<class_decl>(qualified_name, m);

  return result;
}

/// Look into a given corpus to find the class type*s* that have a
/// given qualified name.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
///
/// @return the vector of class types named @p qualified_name.
const type_base_wptrs_type *
lookup_class_types(const interned_string& qualified_name, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m = corp.get_types().class_types();

  return lookup_types_in_map(qualified_name, m);
}

/// Look into a given corpus to find the class type*s* that have a
/// given qualified name.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
///
/// @return the vector of class types that which name is @p qualified_name.
const type_base_wptrs_type*
lookup_class_types(const string& qualified_name, const corpus& corp)
{
  interned_string s = corp.get_environment()->intern(qualified_name);
  return lookup_class_types(s, corp);
}

/// Look up a @ref class_decl from a given corpus by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to consider.
///
/// @return the resulting class decl, if any.
class_decl_sptr
lookup_class_type_per_location(const interned_string& loc,
			       const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m =
    corp.get_type_per_loc_map().class_types();
  class_decl_sptr result = lookup_type_in_map<class_decl>(loc, m);

  return result;
}

/// Look up a @ref class_decl from a given corpus by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to consider.
///
/// @return the resulting class decl, if any.
class_decl_sptr
lookup_class_type_per_location(const string &loc, const corpus &corp)
{
  const environment* env = corp.get_environment();
  ABG_ASSERT(env);

  return lookup_class_type_per_location(env->intern(loc), corp);
}

/// Look into a given corpus to find a union type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
union_decl_sptr
lookup_union_type(const interned_string& type_name, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m = corp.get_types().union_types();

  union_decl_sptr result = lookup_type_in_map<union_decl>(type_name, m);
  if (!result)
    result = lookup_union_type_through_translation_units(type_name, corp);

  return result;
}

/// Look into a given corpus to find a union type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
union_decl_sptr
lookup_union_type(const string& type_name, const corpus& corp)
{
  interned_string s = corp.get_environment()->intern(type_name);
  return lookup_union_type(s, corp);
}

/// Look into a given corpus to find an enum type which has the same
/// qualified name as a given enum type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the enum type which has the same qualified name as the
/// type we are looking for.
///
/// @param corp the corpus to look into.
enum_type_decl_sptr
lookup_enum_type(const enum_type_decl& t, const corpus& corp)
{
  interned_string s = get_type_name(t);
  return lookup_enum_type(s, corp);
}

/// Look into a given corpus to find an enum type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the enum type to look
/// for.
///
/// @param corp the corpus to look into.
enum_type_decl_sptr
lookup_enum_type(const string& qualified_name, const corpus& corp)
{
  interned_string s = corp.get_environment()->intern(qualified_name);
  return lookup_enum_type(s, corp);
}

/// Look into a given corpus to find an enum type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the enum type to look
/// for.
///
/// @param corp the corpus to look into.
enum_type_decl_sptr
lookup_enum_type(const interned_string& qualified_name, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m = corp.get_types().enum_types();

  enum_type_decl_sptr result =
    lookup_type_in_map<enum_type_decl>(qualified_name, m);
  if (!result)
    result = lookup_enum_type_through_translation_units(qualified_name, corp);

  return result;
}

/// Look into a given corpus to find the enum type*s* that have a
/// given qualified name.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
///
/// @return the vector of enum types that which name is @p qualified_name.
const type_base_wptrs_type *
lookup_enum_types(const interned_string& qualified_name, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m = corp.get_types().enum_types();

  return lookup_types_in_map(qualified_name, m);
}

/// Look into a given corpus to find the enum type*s* that have a
/// given qualified name.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
///
/// @return the vector of enum types that which name is @p qualified_name.
const type_base_wptrs_type*
lookup_enum_types(const string& qualified_name, const corpus& corp)
{
  interned_string s = corp.get_environment()->intern(qualified_name);
  return lookup_enum_types(s, corp);
}

/// Look up an @ref enum_type_decl from a given corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to look the type from.
///
/// @return the resulting enum type, if any.
enum_type_decl_sptr
lookup_enum_type_per_location(const interned_string &loc, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m =
    corp.get_type_per_loc_map().enum_types();
  enum_type_decl_sptr result = lookup_type_in_map<enum_type_decl>(loc, m);

  return result;
}

/// Look up an @ref enum_type_decl from a given corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to look the type from.
///
/// @return the resulting enum type, if any.
enum_type_decl_sptr
lookup_enum_type_per_location(const string &loc, const corpus &corp)
{
  const environment* env = corp.get_environment();
  ABG_ASSERT(env);

  return lookup_enum_type_per_location(env->intern(loc), corp);
}

/// Look into a given corpus to find a typedef type which has the
/// same qualified name as a given typedef type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the typedef type which has the same qualified name as the
/// typedef type we are looking for.
///
/// @param corp the corpus to look into.
typedef_decl_sptr
lookup_typedef_type(const typedef_decl& t, const corpus& corp)
{
  interned_string s = get_type_name(t);
  return lookup_typedef_type(s, corp);
}

/// Look into a given corpus to find a typedef type which has the
/// same qualified name as a given typedef type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the typedef type which has the same qualified name as the
/// typedef type we are looking for.
///
/// @param corp the corpus to look into.
typedef_decl_sptr
lookup_typedef_type(const string& qualified_name, const corpus& corp)
{
  interned_string s = corp.get_environment()->intern(qualified_name);
  return lookup_typedef_type(s, corp);
}

/// Look into a given corpus to find a typedef type which has a
/// given qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the typedef type to
/// look for.
///
/// @param corp the corpus to look into.
typedef_decl_sptr
lookup_typedef_type(const interned_string& qualified_name, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m = corp.get_types().typedef_types();

  typedef_decl_sptr result =
    lookup_type_in_map<typedef_decl>(qualified_name, m);
  if (!result)
    result = lookup_typedef_type_through_translation_units(qualified_name,
							   corp);

  return result;
}

/// Lookup a @ref typedef_decl from a corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to consider.
///
/// @return the typedef_decl found, if any.
typedef_decl_sptr
lookup_typedef_type_per_location(const interned_string &loc, const corpus &corp)
{
  const istring_type_base_wptrs_map_type& m =
    corp.get_type_per_loc_map().typedef_types();
  typedef_decl_sptr result = lookup_type_in_map<typedef_decl>(loc, m);

  return result;
}

/// Lookup a @ref typedef_decl from a corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to consider.
///
/// @return the typedef_decl found, if any.
typedef_decl_sptr
lookup_typedef_type_per_location(const string &loc, const corpus &corp)
{
  const environment* env = corp.get_environment();
  ABG_ASSERT(env);

  return lookup_typedef_type_per_location(env->intern(loc), corp);
}

/// Look into a corpus to find a class, union or typedef type which
/// has a given qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the name of the type to find.
///
/// @param corp the corpus to look into.
///
/// @return the typedef or class type found.
type_base_sptr
lookup_class_or_typedef_type(const string& qualified_name, const corpus& corp)
{
  type_base_sptr result = lookup_class_type(qualified_name, corp);
  if (!result)
    result = lookup_union_type(qualified_name, corp);

  if (!result)
    result = lookup_typedef_type(qualified_name, corp);
  return result;
}

/// Look into a corpus to find a class, typedef or enum type which has
/// a given qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
///
/// @return the typedef, class or enum type found.
type_base_sptr
lookup_class_typedef_or_enum_type(const string& qualified_name,
				  const corpus& corp)
{
  type_base_sptr result = lookup_class_or_typedef_type(qualified_name, corp);
  if (!result)
    result = lookup_enum_type(qualified_name, corp);

  return result;
}

/// Look into a given corpus to find a qualified type which has the
/// same qualified name as a given type.
///
/// @param t the type which has the same qualified name as the
/// qualified type we are looking for.
///
/// @param corp the corpus to look into.
///
/// @return the qualified type found.
qualified_type_def_sptr
lookup_qualified_type(const qualified_type_def& t, const corpus& corp)
{
  interned_string s = get_type_name(t);
  return lookup_qualified_type(s, corp);
}

/// Look into a given corpus to find a qualified type which has a
/// given qualified name.
///
/// @param qualified_name the qualified name of the type to look for.
///
/// @param corp the corpus to look into.
///
/// @return the type found.
qualified_type_def_sptr
lookup_qualified_type(const interned_string& qualified_name, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m =
    corp.get_types().qualified_types();

  qualified_type_def_sptr result =
    lookup_type_in_map<qualified_type_def>(qualified_name, m);

  if (!result)
    result = lookup_qualified_type_through_translation_units(qualified_name,
							     corp);

  return result;
}

/// Look into a given corpus to find a pointer type which has the same
/// qualified name as a given pointer type.
///
/// @param t the pointer type which has the same qualified name as the
/// type we are looking for.
///
/// @param corp the corpus to look into.
///
/// @return the pointer type found.
pointer_type_def_sptr
lookup_pointer_type(const pointer_type_def& t, const corpus& corp)
{
  interned_string s = get_type_name(t);
  return lookup_pointer_type(s, corp);
}

/// Look into a given corpus to find a pointer type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the pointer type to
/// look for.
///
/// @param corp the corpus to look into.
///
/// @return the pointer type found.
pointer_type_def_sptr
lookup_pointer_type(const interned_string& qualified_name, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m = corp.get_types().pointer_types();

  pointer_type_def_sptr result =
    lookup_type_in_map<pointer_type_def>(qualified_name, m);
  if (!result)
    result = lookup_pointer_type_through_translation_units(qualified_name,
							   corp);

  return result;
}

/// Look into a given corpus to find a reference type which has the
/// same qualified name as a given reference type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the reference type which has the same qualified name as
/// the reference type we are looking for.
///
/// @param corp the corpus to look into.
///
/// @return the reference type found.
reference_type_def_sptr
lookup_reference_type(const reference_type_def& t, const corpus& corp)
{
  interned_string s = get_type_name(t);
  return lookup_reference_type(s, corp);
}

/// Look into a given corpus to find a reference type which has a
/// given qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the reference type to
/// look for.
///
/// @param corp the corpus to look into.
///
/// @return the reference type found.
reference_type_def_sptr
lookup_reference_type(const interned_string& qualified_name, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m =
    corp.get_types().reference_types();

  reference_type_def_sptr result =
    lookup_type_in_map<reference_type_def>(qualified_name, m);
  if (!result)
    result = lookup_reference_type_through_translation_units(qualified_name,
							     corp);

  return result;
}

/// Look into a given corpus to find an array type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the array type to look
/// for.
///
/// @param corp the corpus to look into.
///
/// @return the array type found.
array_type_def_sptr
lookup_array_type(const array_type_def& t, const corpus& corp)
{
  interned_string s = get_type_name(t);
  return lookup_array_type(s, corp);
}

/// Look into a given corpus to find an array type which has the same
/// qualified name as a given array type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the type which has the same qualified name as the type we
/// are looking for.
///
/// @param corp the corpus to look into.
///
/// @return the type found.
array_type_def_sptr
lookup_array_type(const interned_string& qualified_name, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m = corp.get_types().array_types();

  array_type_def_sptr result =
    lookup_type_in_map<array_type_def>(qualified_name, m);
  if (!result)
    result = lookup_array_type_through_translation_units(qualified_name, corp);

  return result;
}

/// Look into a given corpus to find a function type which has the same
/// qualified name as a given function type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the function type which has the same qualified name as
/// the function type we are looking for.
///
/// @param corp the corpus to look into.
///
/// @return the function type found.
function_type_sptr
lookup_function_type(const function_type&t, const corpus& corp)
{
  interned_string type_name = get_type_name(t);
  return lookup_function_type(type_name, corp);
}

/// Look into a given corpus to find a function type which has the same
/// qualified name as a given function type.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param t the function type which has the same qualified name as
/// the function type we are looking for.
///
/// @param corp the corpus to look into.
///
/// @return the function type found.
function_type_sptr
lookup_function_type(const function_type_sptr& fn_t,
		     const corpus& corpus)
{
  if (fn_t)
    return lookup_function_type(*fn_t, corpus);
  return function_type_sptr();
}

/// Look into a given corpus to find a function type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the function type to
/// look for.
///
/// @param corp the corpus to look into.
///
/// @return the function type found.
function_type_sptr
lookup_function_type(const interned_string& qualified_name, const corpus& corp)
{
  const istring_type_base_wptrs_map_type& m = corp.get_types().function_types();

  function_type_sptr result =
    lookup_type_in_map<function_type>(qualified_name, m);
  if (!result)
    result = lookup_function_type_through_translation_units(qualified_name,
							    corp);

  return result;
}

/// Look into a given corpus to find a type which has a given
/// qualified name.
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the function type to
/// look for.
///
/// @param corp the corpus to look into.
///
/// @return the function type found.
type_base_sptr
lookup_type(const interned_string& n, const corpus& corp)
{
  type_base_sptr result;

  ((result = lookup_basic_type(n, corp))
   || (result = lookup_class_type(n, corp))
   || (result = lookup_union_type(n, corp))
   || (result = lookup_enum_type(n, corp))
   || (result = lookup_typedef_type(n, corp))
   || (result = lookup_qualified_type(n, corp))
   || (result = lookup_pointer_type(n, corp))
   || (result = lookup_reference_type(n, corp))
   || (result = lookup_array_type(n, corp))
   || (result= lookup_function_type(n, corp)));

  return result;
}

/// Lookup a type from a corpus, by its location.
///
/// @param loc the location to consider.
///
/// @param corp the corpus to look the type from.
///
/// @return the resulting type, if any found.
type_base_sptr
lookup_type_per_location(const interned_string& loc, const corpus& corp)
{
  // TODO: finish this.

  //TODO: when we fully support types indexed by their location, this
  //function should return a vector of types because at each location,
  //there can be several types that are defined (yay, C and C++,
  //*sigh*).

  type_base_sptr result;
  ((result = lookup_basic_type_per_location(loc, corp))
   || (result = lookup_class_type_per_location(loc, corp))
   || (result = lookup_union_type_per_location(loc, corp))
   || (result = lookup_enum_type_per_location(loc, corp))
   || (result = lookup_typedef_type_per_location(loc, corp)));

  return result;
}

/// Look into a given corpus to find a type
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the function type to
/// look for.
///
/// @param corp the corpus to look into.
///
/// @return the function type found.
type_base_sptr
lookup_type(const type_base&t, const corpus& corp)
{
  interned_string n = get_type_name(t);
  return lookup_type(n, corp);
}

/// Look into a given corpus to find a type
///
/// If the per-corpus type map is non-empty (because the corpus allows
/// the One Definition Rule) then the type islooked up in that
/// per-corpus type map.  Otherwise, the type is looked-up in each
/// translation unit.
///
/// @param qualified_name the qualified name of the function type to
/// look for.
///
/// @param corp the corpus to look into.
///
/// @return the function type found.
type_base_sptr
lookup_type(const type_base_sptr&t, const corpus& corp)
{
  if (t)
    return lookup_type(*t, corp);
  return type_base_sptr();
}

/// Update the map that associates a fully qualified name of a given
/// type to that type.
///
///
/// @param type the type we are considering.
///
/// @param types_map the map to update.  It's a map that assciates a
/// fully qualified name of a type to the type itself.
///
/// @param use_type_name_as_key if true, use the name of the type as
/// the key to look it up later.  If false, then use the location of
/// the type as a key to look it up later.
///
/// @return true iff the type was added to the map.
template<typename TypeKind>
bool
maybe_update_types_lookup_map(const shared_ptr<TypeKind>& type,
			      istring_type_base_wptrs_map_type& types_map,
			      bool use_type_name_as_key = true)
{
  interned_string s;

  if (use_type_name_as_key)
    s = get_type_name(type);
  else if (location l = type->get_location())
    {
      string str = l.expand();
      s = type->get_environment()->intern(str);
    }

  istring_type_base_wptrs_map_type::iterator i = types_map.find(s);
  bool result = false;

  if (i == types_map.end())
    {
      types_map[s].push_back(type);
      result = true;
    }
  else
    i->second.push_back(type);

  return result;
}

/// This is the specialization for type @ref class_decl of the
/// function template:
///
///    maybe_update_types_lookup_map<T>(scope_decl*,
///					const shared_ptr<T>&,
///					istring_type_base_wptrs_map_type&)
///
/// @param class_type the type to consider.
///
/// @param types_map the type map to update.
///
/// @return true iff the type was added to the map.
template<>
bool
maybe_update_types_lookup_map<class_decl>(const class_decl_sptr& class_type,
					  istring_type_base_wptrs_map_type& map,
					  bool use_type_name_as_key)
{
  class_decl_sptr type = class_type;

  bool update_qname_map = true;
  if (type->get_is_declaration_only())
    {
      if (class_decl_sptr def =
	  is_class_type(class_type->get_definition_of_declaration()))
	type = def;
      else
	update_qname_map = false;
    }

  if (!update_qname_map)
    return false;

  interned_string s;
  if (use_type_name_as_key)
    {
      string qname = type->get_qualified_name();
      s = type->get_environment()->intern(qname);
    }
  else if (location l = type->get_location())
    {
      string str = l.expand();
      s = type->get_environment()->intern(str);
    }

  bool result = false;
  istring_type_base_wptrs_map_type::iterator i = map.find(s);
  if (i == map.end())
    {
      map[s].push_back(type);
      result = true;
    }
  else
    i->second.push_back(type);

  return result;
}

/// This is the specialization for type @ref function_type of the
/// function template:
///
///    maybe_update_types_lookup_map<T>(scope_decl*,
///					const shared_ptr<T>&,
///					istring_type_base_wptrs_map_type&)
///
/// @param scope the scope of the type to consider.
///
/// @param class_type the type to consider.
///
/// @param types_map the type map to update.
///
/// @return true iff the type was added to the map.
template<>
bool
maybe_update_types_lookup_map<function_type>
(const function_type_sptr& type,
 istring_type_base_wptrs_map_type& types_map,
 bool /*use_type_name_as_key*/)
{
  bool result = false;
  interned_string s = get_type_name(type);
  istring_type_base_wptrs_map_type::iterator i = types_map.find(s);
  if (i == types_map.end())
    {
      types_map[s].push_back(type);
      result = true;
    }
  else
    i->second.push_back(type);

  return result;
}

/// Update the map that associates the fully qualified name of a basic
/// type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param basic_type the basic type to consider.
void
maybe_update_types_lookup_map(const type_decl_sptr& basic_type)
{
  if (translation_unit *tu = basic_type->get_translation_unit())
    maybe_update_types_lookup_map<type_decl>
      (basic_type, tu->get_types().basic_types());

  if (corpus *type_corpus = basic_type->get_corpus())
    {
      maybe_update_types_lookup_map<type_decl>
	(basic_type,
	 type_corpus->priv_->get_types().basic_types());

      maybe_update_types_lookup_map<type_decl>
	(basic_type,
	 type_corpus->get_type_per_loc_map().basic_types(),
	 /*use_type_name_as_key*/false);

      if (corpus *group = type_corpus->get_group())
	{
	  maybe_update_types_lookup_map<type_decl>
	    (basic_type,
	     group->priv_->get_types().basic_types());

	  maybe_update_types_lookup_map<type_decl>
	    (basic_type,
	     group->get_type_per_loc_map().basic_types(),
	     /*use_type_name_as_key*/false);
	}
    }

}

/// Update the map that associates the fully qualified name of a class
/// type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param class_type the class type to consider.
void
maybe_update_types_lookup_map(const class_decl_sptr& class_type)
{
  if (translation_unit *tu = class_type->get_translation_unit())
    maybe_update_types_lookup_map<class_decl>
      (class_type, tu->get_types().class_types());

  if (corpus *type_corpus = class_type->get_corpus())
    {
      maybe_update_types_lookup_map<class_decl>
	(class_type,
	 type_corpus->priv_->get_types().class_types());

      maybe_update_types_lookup_map<class_decl>
	(class_type,
	 type_corpus->get_type_per_loc_map().class_types(),
	 /*use_type_name_as_key*/false);

      if (corpus *group = type_corpus->get_group())
	{
	  maybe_update_types_lookup_map<class_decl>
	    (class_type,
	     group->priv_->get_types().class_types());

	  maybe_update_types_lookup_map<class_decl>
	    (class_type,
	     group->get_type_per_loc_map().class_types(),
	     /*use_type_name_as_key*/false);
	}
    }
}

/// Update the map that associates the fully qualified name of a union
/// type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param union_type the union type to consider.
void
maybe_update_types_lookup_map(const union_decl_sptr& union_type)
{
  if (translation_unit *tu = union_type->get_translation_unit())
    maybe_update_types_lookup_map<union_decl>
      (union_type, tu->get_types().union_types());

  if (corpus *type_corpus = union_type->get_corpus())
    {
      maybe_update_types_lookup_map<union_decl>
	(union_type,
	 type_corpus->priv_->get_types().union_types());

      maybe_update_types_lookup_map<union_decl>
	(union_type,
	 type_corpus->get_type_per_loc_map().union_types(),
	 /*use_type_name_as_key*/false);

      if (corpus *group = type_corpus->get_group())
	{
	  maybe_update_types_lookup_map<union_decl>
	    (union_type,
	     group->priv_->get_types().union_types());

	  maybe_update_types_lookup_map<union_decl>
	    (union_type,
	     group->get_type_per_loc_map().union_types(),
	     /*use_type_name_as_key*/false);
	}
    }
}

/// Update the map that associates the fully qualified name of an enum
/// type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param enum_type the type to consider.
void
maybe_update_types_lookup_map(const enum_type_decl_sptr& enum_type)
{
  if (translation_unit *tu = enum_type->get_translation_unit())
    maybe_update_types_lookup_map<enum_type_decl>
      (enum_type, tu->get_types().enum_types());

  if (corpus *type_corpus = enum_type->get_corpus())
    {
      maybe_update_types_lookup_map<enum_type_decl>
	(enum_type,
	 type_corpus->priv_->get_types().enum_types());

      maybe_update_types_lookup_map<enum_type_decl>
	(enum_type,
	 type_corpus->get_type_per_loc_map().enum_types(),
	 /*use_type_name_as_key*/false);

      if (corpus *group = type_corpus->get_group())
	{
	  maybe_update_types_lookup_map<enum_type_decl>
	    (enum_type,
	     group->priv_->get_types().enum_types());

	  maybe_update_types_lookup_map<enum_type_decl>
	    (enum_type,
	     group->get_type_per_loc_map().enum_types(),
	     /*use_type_name_as_key*/false);
	}
    }

}

/// Update the map that associates the fully qualified name of a
/// typedef type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param typedef_type the type to consider.
void
maybe_update_types_lookup_map(const typedef_decl_sptr& typedef_type)
{
  if (translation_unit *tu = typedef_type->get_translation_unit())
    maybe_update_types_lookup_map<typedef_decl>
      (typedef_type, tu->get_types().typedef_types());

  if (corpus *type_corpus = typedef_type->get_corpus())
    {
      maybe_update_types_lookup_map<typedef_decl>
	(typedef_type,
	 type_corpus->priv_->get_types().typedef_types());

      maybe_update_types_lookup_map<typedef_decl>
	(typedef_type,
	 type_corpus->get_type_per_loc_map().typedef_types(),
	 /*use_type_name_as_key*/false);

      if (corpus *group = type_corpus->get_group())
	{
	  maybe_update_types_lookup_map<typedef_decl>
	    (typedef_type,
	     group->priv_->get_types().typedef_types());

	  maybe_update_types_lookup_map<typedef_decl>
	    (typedef_type,
	     group->get_type_per_loc_map().typedef_types(),
	     /*use_type_name_as_key*/false);
	}
    }
}

/// Update the map that associates the fully qualified name of a
/// qualified type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param qualified_type the type to consider.
void
maybe_update_types_lookup_map(const qualified_type_def_sptr& qualified_type)
{
  if (translation_unit *tu = qualified_type->get_translation_unit())
    maybe_update_types_lookup_map<qualified_type_def>
      (qualified_type, tu->get_types().qualified_types());

  if (corpus *type_corpus = qualified_type->get_corpus())
    {
      maybe_update_types_lookup_map<qualified_type_def>
	(qualified_type,
	 type_corpus->priv_->get_types().qualified_types());

      if (corpus *group = type_corpus->get_group())
	{
	  maybe_update_types_lookup_map<qualified_type_def>
	    (qualified_type,
	     group->priv_->get_types().qualified_types());
	}
    }
}

/// Update the map that associates the fully qualified name of a
/// pointer type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param pointer_type the type to consider.
void
maybe_update_types_lookup_map(const pointer_type_def_sptr& pointer_type)
{
  if (translation_unit *tu = pointer_type->get_translation_unit())
    maybe_update_types_lookup_map<pointer_type_def>
      (pointer_type, tu->get_types().pointer_types());

  if (corpus *type_corpus = pointer_type->get_corpus())
    {
      maybe_update_types_lookup_map<pointer_type_def>
	(pointer_type,
	 type_corpus->priv_->get_types().pointer_types());

      if (corpus *group = type_corpus->get_group())
	{
	  maybe_update_types_lookup_map<pointer_type_def>
	    (pointer_type,
	     group->priv_->get_types().pointer_types());
	}
    }
}

/// Update the map that associates the fully qualified name of a
/// reference type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param reference_type the type to consider.
void
maybe_update_types_lookup_map(const reference_type_def_sptr& reference_type)
{
  if (translation_unit *tu = reference_type->get_translation_unit())
    maybe_update_types_lookup_map<reference_type_def>
      (reference_type, tu->get_types().reference_types());

  if (corpus *type_corpus = reference_type->get_corpus())
    {
      maybe_update_types_lookup_map<reference_type_def>
	(reference_type,
	 type_corpus->priv_->get_types().reference_types());

      if (corpus *group = type_corpus->get_group())
	{
	  maybe_update_types_lookup_map<reference_type_def>
	    (reference_type,
	     group->priv_->get_types().reference_types());
	}
    }
}

/// Update the map that associates the fully qualified name of a type
/// with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param array_type the type to consider.
void
maybe_update_types_lookup_map(const array_type_def_sptr& array_type)
{
  if (translation_unit *tu = array_type->get_translation_unit())
    maybe_update_types_lookup_map<array_type_def>
      (array_type, tu->get_types().array_types());

  if (corpus *type_corpus = array_type->get_corpus())
    {
      maybe_update_types_lookup_map<array_type_def>
	(array_type,
	 type_corpus->priv_->get_types().array_types());

      maybe_update_types_lookup_map<array_type_def>
	(array_type,
	 type_corpus->get_type_per_loc_map().array_types(),
	 /*use_type_name_as_key*/false);

      if (corpus *group = type_corpus->get_group())
	{
	  maybe_update_types_lookup_map<array_type_def>
	    (array_type,
	     group->priv_->get_types().array_types());

	  maybe_update_types_lookup_map<array_type_def>
	    (array_type,
	     group->get_type_per_loc_map().array_types(),
	     /*use_type_name_as_key*/false);
	}
    }
}

/// Update the map that associates the fully qualified name of a type
/// with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param subrange_type the type to consider.
void
maybe_update_types_lookup_map
(const array_type_def::subrange_sptr& subrange_type)
{
  if (translation_unit *tu = subrange_type->get_translation_unit())
    maybe_update_types_lookup_map<array_type_def::subrange_type>
      (subrange_type, tu->get_types().subrange_types());

  if (corpus *type_corpus = subrange_type->get_corpus())
    {
      maybe_update_types_lookup_map<array_type_def::subrange_type>
	(subrange_type,
	 type_corpus->priv_->get_types().subrange_types());

      maybe_update_types_lookup_map<array_type_def::subrange_type>
	(subrange_type,
	 type_corpus->get_type_per_loc_map().subrange_types(),
	 /*use_type_name_as_key*/false);

      if (corpus *group = subrange_type->get_corpus())
	{
	  maybe_update_types_lookup_map<array_type_def::subrange_type>
	    (subrange_type,
	     group->priv_->get_types().subrange_types());

	  maybe_update_types_lookup_map<array_type_def::subrange_type>
	    (subrange_type,
	     group->get_type_per_loc_map().subrange_types(),
	     /*use_type_name_as_key*/false);
	}
    }
}

/// Update the map that associates the fully qualified name of a
/// function type with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param scope the scope of the function type.
/// @param fn_type the type to consider.
void
maybe_update_types_lookup_map(const function_type_sptr& fn_type)
{
  if (translation_unit *tu = fn_type->get_translation_unit())
    maybe_update_types_lookup_map<function_type>
      (fn_type, tu->get_types().function_types());

  if (corpus *type_corpus = fn_type->get_corpus())
    {
      maybe_update_types_lookup_map<function_type>
	(fn_type,
	 type_corpus->priv_->get_types().function_types());

      if (corpus *group = fn_type->get_corpus())
	{
	  maybe_update_types_lookup_map<function_type>
	    (fn_type,
	     group->priv_->get_types().function_types());
	}
    }
}

/// Update the map that associates the fully qualified name of a type
/// declaration with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param decl the declaration of the type to consider.
void
maybe_update_types_lookup_map(const decl_base_sptr& decl)
{
  if (!is_type(decl))
    return;

  if (type_decl_sptr basic_type = is_type_decl(decl))
    maybe_update_types_lookup_map(basic_type);
  else if (class_decl_sptr class_type = is_class_type(decl))
    maybe_update_types_lookup_map(class_type);
  else if (union_decl_sptr union_type = is_union_type(decl))
    maybe_update_types_lookup_map(union_type);
  else if (enum_type_decl_sptr enum_type = is_enum_type(decl))
    maybe_update_types_lookup_map(enum_type);
  else if (typedef_decl_sptr typedef_type = is_typedef(decl))
    maybe_update_types_lookup_map(typedef_type);
  else if (qualified_type_def_sptr qualified_type = is_qualified_type(decl))
    maybe_update_types_lookup_map(qualified_type);
  else if (pointer_type_def_sptr pointer_type = is_pointer_type(decl))
    maybe_update_types_lookup_map(pointer_type);
  else if (reference_type_def_sptr reference_type = is_reference_type(decl))
    maybe_update_types_lookup_map(reference_type);
  else if (array_type_def_sptr array_type = is_array_type(decl))
    maybe_update_types_lookup_map(array_type);
  else if (array_type_def::subrange_sptr subrange_type = is_subrange_type(decl))
    maybe_update_types_lookup_map(subrange_type);
  else
    ABG_ASSERT_NOT_REACHED;
}

/// Update the map that associates the fully qualified name of a type
/// with the type itself.
///
/// The per-translation unit type map is updated if no type with this
/// name was already existing in that map.
///
/// If no type with this name did already exist in the per-corpus type
/// map, then that per-corpus type map is updated. Otherwise, that
/// type is erased from that per-corpus map.
///
/// @param type the type to consider.
void
maybe_update_types_lookup_map(const type_base_sptr& type)
{
  if (decl_base_sptr decl = get_type_declaration(type))
    maybe_update_types_lookup_map(decl);
  else
    ABG_ASSERT_NOT_REACHED;
}

//--------------------------------
// </type and decls lookup stuff>
// ------------------------------

/// In a translation unit, lookup a given type or synthesize it if
/// it's a qualified type.
///
/// So this function first looks the type up in the translation unit.
/// If it's found, then OK, it's returned.  Otherwise, if it's a
/// qualified, reference or pointer or function type (a composite
/// type), lookup the underlying type, synthesize the type we want
/// from it and return it.
///
/// If the underlying types is not not found, then give up and return
/// nil.
///
/// @return the type that was found or the synthesized type.
type_base_sptr
synthesize_type_from_translation_unit(const type_base_sptr& type,
				      translation_unit& tu)
{
  type_base_sptr result;

   result = lookup_type(type, tu);

  if (!result)
    {
      if (qualified_type_def_sptr qual = is_qualified_type(type))
	{
	  type_base_sptr underlying_type =
	    synthesize_type_from_translation_unit(qual->get_underlying_type(),
						  tu);
	  if (underlying_type)
	    {
	      result.reset(new qualified_type_def(underlying_type,
						  qual->get_cv_quals(),
						  qual->get_location()));
	    }
	}
      else if (pointer_type_def_sptr p = is_pointer_type(type))
	{
	  type_base_sptr pointed_to_type =
	    synthesize_type_from_translation_unit(p->get_pointed_to_type(),
						  tu);
	  if (pointed_to_type)
	    {
	      result.reset(new pointer_type_def(pointed_to_type,
						p->get_size_in_bits(),
						p->get_alignment_in_bits(),
						p->get_location()));
	      result->set_environment(pointed_to_type->get_environment());
	    }
	}
      else if (reference_type_def_sptr r = is_reference_type(type))
	{
	  type_base_sptr pointed_to_type =
	    synthesize_type_from_translation_unit(r->get_pointed_to_type(), tu);
	  if (pointed_to_type)
	    {
	      result.reset(new reference_type_def(pointed_to_type,
						  r->is_lvalue(),
						  r->get_size_in_bits(),
						  r->get_alignment_in_bits(),
						  r->get_location()));
	      result->set_environment(pointed_to_type->get_environment());
	    }
	}
      else if (function_type_sptr f = is_function_type(type))
	result = synthesize_function_type_from_translation_unit(*f, tu);

      if (result)
	{
	  add_decl_to_scope(is_decl(result), tu.get_global_scope());
	  canonicalize(result);
	}
    }

  if (result)
    tu.priv_->synthesized_types_.push_back(result);

  return result;
}

/// In a translation unit, lookup the sub-types that make up a given
/// function type and if the sub-types are all found, synthesize and
/// return a function_type with them.
///
/// This function is like lookup_function_type_in_translation_unit()
/// execept that it constructs the function type from the sub-types
/// found in the translation, rather than just looking for the
/// function types held by the translation unit.  This can be useful
/// if the translation unit doesnt hold the function type we are
/// looking for (i.e, lookup_function_type_in_translation_unit()
/// returned NULL) but we still want to see if the sub-types of the
/// function types are present in the translation unit.
///
/// @param fn_type the function type to consider.
///
/// @param tu the translation unit to look into.
///
/// @return the resulting synthesized function type if all its
/// sub-types have been found, NULL otherwise.
function_type_sptr
synthesize_function_type_from_translation_unit(const function_type& fn_type,
					       translation_unit& tu)
{
  function_type_sptr nil = function_type_sptr();

  environment* env = tu.get_environment();
  ABG_ASSERT(env);

  type_base_sptr return_type = fn_type.get_return_type();
  type_base_sptr result_return_type;
  if (!return_type || env->is_void_type(return_type))
    result_return_type = env->get_void_type();
  else
    result_return_type = synthesize_type_from_translation_unit(return_type, tu);
  if (!result_return_type)
    return nil;

  function_type::parameters parms;
  type_base_sptr parm_type;
  function_decl::parameter_sptr parm;
  for (function_type::parameters::const_iterator i =
	 fn_type.get_parameters().begin();
       i != fn_type.get_parameters().end();
       ++i)
    {
      type_base_sptr t = (*i)->get_type();
      parm_type = synthesize_type_from_translation_unit(t, tu);
      if (!parm_type)
	return nil;
      parm.reset(new function_decl::parameter(parm_type,
					      (*i)->get_index(),
					      (*i)->get_name(),
					      (*i)->get_location(),
					      (*i)->get_variadic_marker(),
					      (*i)->get_is_artificial()));
      parms.push_back(parm);
    }

  class_or_union_sptr class_type;
  const method_type* method = is_method_type(&fn_type);
  if (method)
    {
      class_type = is_class_or_union_type
	(synthesize_type_from_translation_unit(method->get_class_type(), tu));
      ABG_ASSERT(class_type);
    }

  function_type_sptr result_fn_type;

  if (class_type)
    result_fn_type.reset(new method_type(result_return_type,
					 class_type,
					 parms,
					 method->get_is_const(),
					 fn_type.get_size_in_bits(),
					 fn_type.get_alignment_in_bits()));
  else
    result_fn_type.reset(new function_type(result_return_type,
					   parms,
					   fn_type.get_size_in_bits(),
					   fn_type.get_alignment_in_bits()));

  tu.priv_->synthesized_types_.push_back(result_fn_type);
  // The new synthesized type must be in the same environment as its
  // translation unit.
  result_fn_type->set_environment(tu.get_environment());
  tu.bind_function_type_life_time(result_fn_type);

  canonicalize(result_fn_type);
  return result_fn_type;
}

/// Demangle a C++ mangled name and return the resulting string
///
/// @param mangled_name the C++ mangled name to demangle.
///
/// @return the resulting mangled name.
string
demangle_cplus_mangled_name(const string& mangled_name)
{
  if (mangled_name.empty())
    return "";

  size_t l = 0;
  int status = 0;
  char * str = abi::__cxa_demangle(mangled_name.c_str(),
				   NULL, &l, &status);
  string demangled_name = mangled_name;
  if (str)
    {
      ABG_ASSERT(status == 0);
      demangled_name = str;
      free(str);
      str = 0;
    }
  return demangled_name;
}

/// Return either the type given in parameter if it's non-null, or the
/// void type.
///
/// @param t the type to consider.
///
/// @param env the environment to use.  If NULL, just abort the
/// process.
///
/// @return either @p t if it is non-null, or the void type.
type_base_sptr
type_or_void(const type_base_sptr t, const environment* env)
{
  type_base_sptr r;

  if (t)
    r = t;
  else
    {
      ABG_ASSERT(env);
      r = type_base_sptr(env->get_void_type());
    }

  return r;
}

global_scope::~global_scope()
{
}

// <type_base definitions>

/// Definition of the private data of @ref type_base.
struct type_base::priv
{
  size_t		size_in_bits;
  size_t		alignment_in_bits;
  type_base_wptr	canonical_type;
  // The data member below holds the canonical type that is managed by
  // the smart pointer referenced by the canonical_type data member
  // above.  We are storing this underlying (naked) pointer here, so
  // that users can access it *fast*.  Otherwise, accessing
  // canonical_type above implies creating a shared_ptr, and that has
  // been measured to be slow for some performance hot spots.
  type_base*		naked_canonical_type;
  // Computing the representation of a type again and again can be
  // costly.  So we cache the internal and non-internal type
  // representation strings here.
  interned_string	internal_cached_repr_;
  interned_string	cached_repr_;

  priv()
    : size_in_bits(),
      alignment_in_bits(),
      naked_canonical_type()
  {}

  priv(size_t s,
       size_t a,
       type_base_sptr c = type_base_sptr())
    : size_in_bits(s),
      alignment_in_bits(a),
      canonical_type(c),
      naked_canonical_type(c.get())
  {}
}; // end struct type_base::priv

static void
maybe_propagate_canonical_type(const type_base& lhs_type,
			       const type_base& rhs_type);

/// Test if two types are eligible to the "Linux Kernel Fast Type
/// Comparison Optimization", a.k.a LKFTCO.
///
/// Two types T1 and T2 (who are presumably of the same name and kind)
/// are eligible to the LKFTCO if they fulfill the following criteria/
///
/// 1/ T1 and T2 come from the same Linux Kernel Corpus and they are
/// either class, union or enums.
///
/// 2/ They are defined in the same translation unit.
///
/// @param t1 the first type to consider.
///
/// @param t2 the second type to consider.
///
/// @return true iff t1 and t2 are eligible to the LKFTCO.
static bool
types_defined_same_linux_kernel_corpus_public(const type_base& t1,
					      const type_base& t2)
{
  const corpus *t1_corpus = t1.get_corpus(), *t2_corpus = t2.get_corpus();
  string t1_file_path, t2_file_path;

  /// If the t1 (and t2) are classes/unions/enums from the same linux
  /// kernel corpus, let's move on.  Otherwise bail out.
  if (!(t1_corpus && t2_corpus
	&& t1_corpus == t2_corpus
	&& (t1_corpus->get_origin() == corpus::LINUX_KERNEL_BINARY_ORIGIN)
	&& (is_class_or_union_type(&t1)
	    || is_enum_type(&t1))))
    return false;

  class_or_union *c1 = 0, *c2 = 0;
  c1 = is_class_or_union_type(&t1);
  c2 = is_class_or_union_type(&t2);

  // Two anonymous class types with no naming typedefs cannot be
  // eligible to this optimization.
  if ((c1 && c1->get_is_anonymous() && !c1->get_naming_typedef())
      || (c2 && c2->get_is_anonymous() && !c2->get_naming_typedef()))
    return false;

  // Two anonymous enum types cannot be eligible to this optimization.
  if (const enum_type_decl *e1 = is_enum_type(&t1))
    if (const enum_type_decl *e2 = is_enum_type(&t2))
      if (e1->get_is_anonymous() || e2->get_is_anonymous())
	return false;

  // Look through declaration-only types.  That is, get the associated
  // definition type.
  c1 = look_through_decl_only_class(c1);
  c2 = look_through_decl_only_class(c2);

  if (c1 && c2)
    {
      if (c1->get_is_declaration_only() != c2->get_is_declaration_only())
	{
	  if (c1->get_environment()->decl_only_class_equals_definition())
	    // At least one of classes/union is declaration-only.
	    // Because we are in a context in which a declaration-only
	    // class/union is equal to all definitions of that
	    // class/union, we can assume that the two types are
	    // equal.
	    return true;
	}
    }

  if (t1.get_size_in_bits() != t2.get_size_in_bits())
    return false;

  // Look at the file names of the locations of t1 and t2.  If they
  // are equal, then t1 and t2 are defined in the same file.
  {
    location l;

    if (c1)
      l = c1->get_location();
    else
      l = dynamic_cast<const decl_base&>(t1).get_location();

    unsigned line = 0, col = 0;
    if (l)
      l.expand(t1_file_path, line, col);
    if (c2)
      l = c2->get_location();
    else
      l = dynamic_cast<const decl_base&>(t2).get_location();
    if (l)
      l.expand(t2_file_path, line, col);
  }

  if (t1_file_path.empty() || t2_file_path.empty())
    return false;

  if (t1_file_path == t2_file_path)
    return true;

  return false;
}

/// Compute the canonical type for a given instance of @ref type_base.
///
/// Consider two types T and T'.  The canonical type of T, denoted
/// C(T) is a type such as T == T' if and only if C(T) == C(T').  Said
/// otherwise, to compare two types, one just needs to compare their
/// canonical types using pointer equality.  That makes type
/// comparison faster than the structural comparison performed by the
/// abigail::ir::equals() overloads.
///
/// If there is not yet any canonical type for @p t, then @p t is its
/// own canonical type.  Otherwise, this function returns the
/// canonical type of @p t which is the canonical type that has the
/// same hash value as @p t and that structurally equals @p t.  Note
/// that after invoking this function, the life time of the returned
/// canonical time is then equals to the life time of the current
/// process.
///
/// @param t a smart pointer to instance of @ref type_base we want to
/// compute a canonical type for.
///
/// @return the canonical type for the current instance of @ref
/// type_base.
type_base_sptr
type_base::get_canonical_type_for(type_base_sptr t)
{
  if (!t)
    return t;

  environment* env = t->get_environment();
  ABG_ASSERT(env);

  bool decl_only_class_equals_definition =
    (odr_is_relevant(*t) || env->decl_only_class_equals_definition());

  class_or_union_sptr class_or_union = is_class_or_union_type(t);

  // Look through declaration-only classes
  if (decl_only_class_equals_definition)
    if (class_or_union)
      {
	class_or_union = look_through_decl_only_class(class_or_union);
	if (class_or_union->get_is_declaration_only())
	  return type_base_sptr();
	else
	  t = class_or_union;
      }

  class_decl_sptr is_class = is_class_type(t);
  if (t->get_canonical_type())
    return t->get_canonical_type();

  // For classes and union, ensure that an anonymous class doesn't
  // have a linkage name.  If it does in the future, then me must be
  // mindful that the linkage name respects the type identity
  // constraints which states that "if two linkage names are different
  // then the two types are different".
  ABG_ASSERT(!class_or_union
	     || !class_or_union->get_is_anonymous()
	     || class_or_union->get_linkage_name().empty());

  // We want the pretty representation of the type, but for an
  // internal use, not for a user-facing purpose.
  //
  // If two classe types Foo are declared, one as a class and the
  // other as a struct, but are otherwise equivalent, we want their
  // pretty representation to be the same.  Hence the 'internal'
  // argument of ir::get_pretty_representation() is set to true here.
  // So in this case, the pretty representation of Foo is going to be
  // "class Foo", regardless of its struct-ness. This also applies to
  // composite types which would have "class Foo" as a sub-type.
  string repr = t->get_cached_pretty_representation(/*internal=*/true);

  // If 't' already has a canonical type 'inside' its corpus
  // (t_corpus), then this variable is going to contain that canonical
  // type.
  type_base_sptr canonical_type_present_in_corpus;
  environment::canonical_types_map_type& types =
    env->get_canonical_types_map();

  type_base_sptr result;
  environment::canonical_types_map_type::iterator i = types.find(repr);
  if (i == types.end())
    {
      vector<type_base_sptr> v;
      v.push_back(t);
      types[repr] = v;
      result = t;
    }
  else
    {
      vector<type_base_sptr> &v = i->second;
      // Let's compare 't' structurally (i.e, compare its sub-types
      // recursively) against the canonical types of the system. If it
      // equals a given canonical type C, then it means C is the
      // canonical type of 't'.  Otherwise, if 't' is different from
      // all the canonical types of the system, then it means 't' is a
      // canonical type itself.
      for (vector<type_base_sptr>::const_reverse_iterator it = v.rbegin();
	   it != v.rend();
	   ++it)
	{
	  // Before the "*it == it" comparison below is done, let's
	  // perform on-the-fly-canonicalization.  For C types, let's
	  // consider that an unresolved struct declaration 'struct S'
	  // is different from a definition 'struct S'.  This is
	  // because normally, at this point all the declarations of
	  // struct S that are compatible with the definition of
	  // struct S have already been resolved to that definition,
	  // during the DWARF parsing.  The remaining unresolved
	  // declaration are thus considered different.  With this
	  // setup we can properly handle cases of two *different*
	  // struct S being defined in the same binary (in different
	  // translation units), and a third struct S being only
	  // declared as an opaque type in a third translation unit of
	  // its own, with no definition in there.  In that case, the
	  // declaration-only struct S should be left alone and not
	  // resolved to any of the two definitions of struct S.
	  bool saved_decl_only_class_equals_definition =
	    env->decl_only_class_equals_definition();
	  env->do_on_the_fly_canonicalization(true);
	  // Compare types by considering that decl-only classes don't
	  // equal their definition.
	  env->decl_only_class_equals_definition(false);
	  bool equal = types_defined_same_linux_kernel_corpus_public(**it, *t)
		       || *it == t;
	  // Restore the state of the on-the-fly-canonicalization and
	  // the decl-only-class-being-equal-to-a-matching-definition
	  // flags.
	  env->do_on_the_fly_canonicalization(false);
	  env->decl_only_class_equals_definition
	    (saved_decl_only_class_equals_definition);
	  if (equal)
	    {
	      result = *it;
	      break;
	    }
	}
      if (!result)
	{
	  v.push_back(t);
	  result = t;
	}
    }

  return result;
}

/// This method is invoked automatically right after the current
/// instance of @ref class_decl has been canonicalized.
void
type_base::on_canonical_type_set()
{}

/// This is a subroutine of the canonicalize() function.
///
/// When the canonical type C of type T has just been computed, there
/// can be cases where T has member functions that C doesn't have.
///
/// This is possible because non virtual member functions are not
/// taken in account when comparing two types.
///
/// In that case, this function updates C so that it contains the
/// member functions.
///
/// There can also be cases where C has a method M which is not linked
/// to any underlying symbol, whereas in T, M is to link to an
/// underlying symbol.  In that case, this function updates M in C so
/// that it's linked to the same underlying symbol as for M in T.
static void
maybe_adjust_canonical_type(const type_base_sptr& canonical,
			    const type_base_sptr& type)
{
  if (// If 'type' is *NOT* a newly canonicalized type ...
      type->get_naked_canonical_type()
      // ... or if 'type' is it's own canonical type, then get out.
      || type.get() == canonical.get())
    return;

  if (class_decl_sptr cl = is_class_type(type))
    {
      class_decl_sptr canonical_class = is_class_type(canonical);

      if (canonical_class)
	{
	  // Set symbols of member functions that might be missing
	  // theirs.
	  for (class_decl::member_functions::const_iterator i =
		 cl->get_member_functions().begin();
	       i != cl->get_member_functions().end();
	       ++i)
	    if ((*i)->get_symbol())
	      {
		if (method_decl *m = canonical_class->
		    find_member_function((*i)->get_linkage_name()))
		  {
		    elf_symbol_sptr s1 = (*i)->get_symbol();
		    if (s1 && !m->get_symbol())
		      // Method 'm' in the canonical type is not
		      // linked to the underlying symbol of '*i'.
		      // Let's link it now.  have th
		      m->set_symbol(s1);
		  }
		else
		  // There is a member function defined and publicly
		  // exported in the other class, and the canonical
		  // class doesn't have that member function.  Let's
		  // copy that member function to the canonical class
		  // then.
		  copy_member_function (canonical_class, *i);
	      }
	}
    }
}

/// Compute the canonical type of a given type.
///
/// It means that after invoking this function, comparing the intance
/// instance @ref type_base and another one (on which
/// type_base::enable_canonical_equality() would have been invoked as
/// well) is performed by just comparing the pointer values of the
/// canonical types of both types.  That equality comparison is
/// supposedly faster than structural comparison of the types.
///
/// @param t a smart pointer to the instance of @ref type_base for
/// which to compute the canonical type.  After this call,
/// t->get_canonical_type() will return the newly computed canonical
/// type.
///
/// @return the canonical type computed for @p t.
type_base_sptr
canonicalize(type_base_sptr t)
{
  if (!t)
    return t;

  if (t->get_canonical_type())
    return t->get_canonical_type();

  type_base_sptr canonical = type_base::get_canonical_type_for(t);
  maybe_adjust_canonical_type(canonical, t);

  t->priv_->canonical_type = canonical;
  t->priv_->naked_canonical_type = canonical.get();

  if (class_decl_sptr cl = is_class_type(t))
    if (type_base_sptr d = is_type(cl->get_earlier_declaration()))
      if ((canonical = d->get_canonical_type()))
	{
	  d->priv_->canonical_type = canonical;
	  d->priv_->naked_canonical_type = canonical.get();
	}

  if (canonical)
    if (decl_base_sptr d = is_decl_slow(canonical))
      {
	scope_decl *scope = d->get_scope();
	// Add the canonical type to the set of canonical types
	// belonging to its scope.
	if (scope)
	  scope->get_canonical_types().insert(canonical);
	//else, if the type doesn't have a scope, it's doesn't meant
	// to be emitted.  This can be the case for the result of the
	// function strip_typedef, for instance.
      }

  t->on_canonical_type_set();
  return canonical;
}


/// Set the definition of this declaration-only @ref decl_base.
///
/// @param d the new definition to set.
void
decl_base::set_definition_of_declaration(const decl_base_sptr& d)
{
  ABG_ASSERT(get_is_declaration_only());
  priv_->definition_of_declaration_ = d;
  if (type_base *t = is_type(this))
    if (type_base_sptr canonical_type = is_type(d)->get_canonical_type())
      t->priv_->canonical_type = canonical_type;

  priv_->naked_definition_of_declaration_ = const_cast<decl_base*>(d.get());
}

/// The constructor of @ref type_base.
///
/// @param s the size of the type, in bits.
///
/// @param a the alignment of the type, in bits.
type_base::type_base(const environment* e, size_t s, size_t a)
  : type_or_decl_base(e, ABSTRACT_TYPE_BASE|ABSTRACT_TYPE_BASE),
    priv_(new priv(s, a))
{}

/// Getter of the canonical type of the current instance of @ref
/// type_base.
///
/// @return a smart pointer to the canonical type of the current
/// intance of @ref type_base, or an empty smart pointer if the
/// current instance of @ref type_base doesn't have any canonical
/// type.
type_base_sptr
type_base::get_canonical_type() const
{return priv_->canonical_type.lock();}

/// Getter of the canonical type pointer.
///
/// Note that this function doesn't return a smart pointer, but rather
/// the underlying pointer managed by the smart pointer.  So it's as
/// fast as possible.  This getter is to be used in code paths that
/// are proven to be performance hot spots; especially, when comparing
/// sensitive types like class, function, pointers and reference
/// types.  Those are compared extremely frequently and thus, their
/// accessing the canonical type must be fast.
///
/// @return the canonical type pointer, not managed by a smart
/// pointer.
type_base*
type_base::get_naked_canonical_type() const
{return priv_->naked_canonical_type;}

/// Get the pretty representation of the current type.
///
/// The pretty representation is retrieved from a cache.  If the cache
/// is empty, this function computes the pretty representation, put it
/// in the cache and returns it.
///
/// Note that if the type is *NOT* canonicalized, the pretty
/// representation is never cached.
///
/// @param internal if true, then the pretty representation is to be
/// used for purpuses that are internal to the libabigail library
/// itself.  If you don't know what this means, then you probably
/// should set this parameter to "false".
const interned_string&
type_base::get_cached_pretty_representation(bool internal) const
{
  if (internal)
    {
      if (!get_naked_canonical_type() || priv_->internal_cached_repr_.empty())
	{
	  string r = ir::get_pretty_representation(this, internal);
	  priv_->internal_cached_repr_ = get_environment()->intern(r);
	}
      return priv_->internal_cached_repr_;
    }

  if (!get_naked_canonical_type() || priv_->cached_repr_.empty())
    {
      string r = ir::get_pretty_representation(this, internal);
      priv_->cached_repr_ = get_environment()->intern(r);
    }

  return priv_->cached_repr_;
}

/// Compares two instances of @ref type_base.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p is non-null and if the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const type_base& l, const type_base& r, change_kind* k)
{
  bool result = (l.get_size_in_bits() == r.get_size_in_bits()
		 && l.get_alignment_in_bits() == r.get_alignment_in_bits());
  if (!result)
    if (k)
      *k |= LOCAL_TYPE_CHANGE_KIND;
  return result;
}

/// Return true iff both type declarations are equal.
///
/// Note that this doesn't test if the scopes of both types are equal.
bool
type_base::operator==(const type_base& other) const
{return equals(*this, other, 0);}

/// Inequality operator.
///
///@param other the instance of @ref type_base to compare the current
/// instance against.
///
/// @return true iff the current instance is different from @p other.
bool
type_base::operator!=(const type_base& other) const
{return !operator==(other);}

/// Setter for the size of the type.
///
/// @param s the new size -- in bits.
void
type_base::set_size_in_bits(size_t s)
{priv_->size_in_bits = s;}

/// Getter for the size of the type.
///
/// @return the size in bits of the type.
size_t
type_base::get_size_in_bits() const
{return priv_->size_in_bits;}

/// Setter for the alignment of the type.
///
/// @param a the new alignment -- in bits.
void
type_base::set_alignment_in_bits(size_t a)
{priv_->alignment_in_bits = a;}

/// Getter for the alignment of the type.
///
/// @return the alignment of the type in bits.
size_t
type_base::get_alignment_in_bits() const
{return priv_->alignment_in_bits;}

/// Default implementation of traversal for types.  This function does
/// nothing.  It must be implemented by every single new type that is
/// written.
///
/// Please look at e.g, class_decl::traverse() for an example of how
/// to implement this.
///
/// @param v the visitor used to visit the type.
bool
type_base::traverse(ir_node_visitor& v)
{
  if (v.type_node_has_been_visited(this))
    return true;

  v.visit_begin(this);
  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);

  return result;
}

type_base::~type_base()
{delete priv_;}

// </type_base definitions>

// <integral_type definitions>

/// Bitwise OR operator for integral_type::modifiers_type.
///
/// @param l the left-hand side operand.
///
/// @param r the right-hand side operand.
///
/// @return the result of the bitwise OR.
integral_type::modifiers_type
operator|(integral_type::modifiers_type l, integral_type::modifiers_type r)
{
  return static_cast<integral_type::modifiers_type>(static_cast<unsigned>(l)
						    |static_cast<unsigned>(r));
}

/// Bitwise AND operator for integral_type::modifiers_type.
///
/// @param l the left-hand side operand.
///
/// @param r the right-hand side operand.
///
/// @return the result of the bitwise AND.
integral_type::modifiers_type
operator&(integral_type::modifiers_type l, integral_type::modifiers_type r)
{
  return static_cast<integral_type::modifiers_type>(static_cast<unsigned>(l)
						    &static_cast<unsigned>(r));
}

/// Bitwise |= operator for integral_type::modifiers_type.
///
/// @param l the left-hand side operand.
///
/// @param r the right-hand side operand.
///
/// @return the result of the bitwise |=.
integral_type::modifiers_type&
operator|=(integral_type::modifiers_type& l, integral_type::modifiers_type r)
{
  l = l | r;
  return l;
}

/// Parse a word containing one integral type modifier.
///
/// A word is considered to be a string of characters that doesn't
/// contain any white space.
///
/// @param word the word to parse.  It is considered to be a string of
/// characters that doesn't contain any white space.
///
/// @param modifiers out parameter.  It's set by this function to the
/// parsed modifier iff the function returned true.
///
/// @return true iff @word was successfully parsed.
static bool
parse_integral_type_modifier(const string& word,
			     integral_type::modifiers_type &modifiers)
{
    if (word == "signed")
      modifiers |= integral_type::SIGNED_MODIFIER;
    else if (word == "unsigned")
      modifiers |= integral_type::UNSIGNED_MODIFIER;
    else if (word == "short")
      modifiers |= integral_type::SHORT_MODIFIER;
    else if (word == "long")
      modifiers |= integral_type::LONG_MODIFIER;
    else if (word == "long long")
      modifiers |= integral_type::LONG_LONG_MODIFIER;
    else
      return false;

    return true;
}

/// Parse a base type of an integral type from a string.
///
/// @param type_name the type name to parse.
///
/// @param base out parameter.  This is set to the resulting base type
/// parsed, iff the function returned true.
///
/// @return true iff the function could successfully parse the base
/// type.
static bool
parse_base_integral_type(const string& type_name,
			 integral_type::base_type& base)
{
  if (type_name == "int")
    base = integral_type::INT_BASE_TYPE;
  else if (type_name == "char")
    base = integral_type::CHAR_BASE_TYPE;
  else if (type_name == "bool" || type_name == "_Bool")
    base = integral_type::BOOL_BASE_TYPE;
  else if (type_name == "double")
    base = integral_type::DOUBLE_BASE_TYPE;
  else if (type_name =="float")
    base = integral_type::FLOAT_BASE_TYPE;
  else if (type_name == "char16_t")
    base = integral_type::CHAR16_T_BASE_TYPE;
  else if (type_name == "char32_t")
    base = integral_type::CHAR32_T_BASE_TYPE;
  else if (type_name == "wchar_t")
    base = integral_type::WCHAR_T_BASE_TYPE;
  else
    return false;

  return true;
}

/// Parse an integral type from a string.
///
/// @param type_name the string containing the integral type to parse.
///
/// @param base out parameter.  Is set by this function to the base
/// type of the integral type, iff the function returned true.
///
/// @param modifiers out parameter  If set by this function to the
/// modifier of the integral type, iff the function returned true.
///
/// @return true iff the function could parse an integral type from @p
/// type_name.
static bool
parse_integral_type(const string&			type_name,
		    integral_type::base_type&		base,
		    integral_type::modifiers_type&	modifiers)
{
  string input = type_name;
  string::size_type len = input.length();
  string::size_type cur_pos = 0, prev_pos = 0;
  string cur_word, prev_word;
  bool ok = false;

  while (cur_pos < len)
    {
      prev_pos = cur_pos;
      cur_pos = input.find(" ", prev_pos);
      prev_word = cur_word;
      cur_word = input.substr(prev_pos, cur_pos - prev_pos);

      if (cur_pos < len && isspace(input[cur_pos]))
	do
	  ++cur_pos;
	while (cur_pos < len && isspace(input[cur_pos]));

      if (cur_pos < len
	  && cur_word == "long"
	  && prev_word != "long")
	{
	  prev_pos = cur_pos;
	  cur_pos = input.find(" ", prev_pos);
	  string saved_prev_word = prev_word;
	  prev_word = cur_word;
	  cur_word = input.substr(prev_pos, cur_pos - prev_pos);
	  if (cur_word == "long")
	    cur_word = "long long";
	  else
	    {
	      cur_pos = prev_pos;
	      cur_word = prev_word;
	      prev_word = saved_prev_word;
	    }
	}

      if (!parse_integral_type_modifier(cur_word, modifiers))
	{
	  if (!parse_base_integral_type(cur_word, base))
	    return false;
	  else
	    ok = true;
	}
      else
	ok = true;
    }

  return ok;
}

/// Parse an integral type from a string.
///
/// @param str the string containing the integral type to parse.
///
///@param type the resulting @ref integral_type.  Is set to the result
///of the parse, iff the function returns true.
///
/// @return true iff the function could parse an integral type from @p
/// str.
bool
parse_integral_type(const string& str, integral_type& type)
{
  integral_type::base_type base_type = integral_type::INT_BASE_TYPE;
  integral_type::modifiers_type modifiers = integral_type::NO_MODIFIER;

  if (!parse_integral_type(str, base_type, modifiers))
    return false;

  // So this is an integral type.
  integral_type int_type(base_type, modifiers);
  type = int_type;
  return true;
}

/// Default constructor of the @ref integral_type.
integral_type::integral_type()
  : base_(INT_BASE_TYPE),
    modifiers_(NO_MODIFIER)
{}

/// Constructor of the @ref integral_type.
///
/// @param b the base type of the integral type.
///
/// @param m the modifiers of the integral type.
integral_type::integral_type(base_type b, modifiers_type m)
  : base_(b), modifiers_(m)
{}

/// Constructor of the @ref integral_type.
///
/// @param the name of the integral type to parse to initialize the
/// current instance of @ref integral_type.
integral_type::integral_type(const string& type_name)
  : base_(INT_BASE_TYPE),
    modifiers_(NO_MODIFIER)
{
  bool could_parse = parse_integral_type(type_name, base_, modifiers_);
  ABG_ASSERT(could_parse);
}

/// Getter of the base type of the @ref integral_type.
///
/// @return the base type of the @ref integral_type.
integral_type::base_type
integral_type::get_base_type() const
{return base_;}

/// Getter of the modifiers bitmap of the @ref integral_type.
///
/// @return the modifiers bitmap of the @ref integral_type.
integral_type::modifiers_type
integral_type::get_modifiers() const
{return modifiers_;}

/// Equality operator for the @ref integral_type.
///
/// @param other the other integral type to compare against.
///
/// @return true iff @p other equals the current instance of @ref
/// integral_type.
bool
integral_type::operator==(const integral_type&other) const
{return base_ == other.base_ && modifiers_ == other.modifiers_;}

/// Return the string representation of the current instance of @ref
/// integral_type.
///
/// @return the string representation of the current instance of @ref
/// integral_type.
string
integral_type::to_string() const
{
  string result;

  // Look at modifiers ...
  if (modifiers_ & SIGNED_MODIFIER)
    result += "signed ";
  if (modifiers_ & UNSIGNED_MODIFIER)
    result += "unsigned ";
  if (modifiers_ & SHORT_MODIFIER)
    result += "short ";
  if (modifiers_ & LONG_MODIFIER)
    result += "long ";
  if (modifiers_ & LONG_LONG_MODIFIER)
    result += "long long ";

  // ... and look at base types.
  if (base_ == INT_BASE_TYPE)
    result += "int";
  else if (base_ == CHAR_BASE_TYPE)
    result += "char";
  else if (base_ == BOOL_BASE_TYPE)
    result += "bool";
  else if (base_ == DOUBLE_BASE_TYPE)
    result += "double";
  else if (base_ == FLOAT_BASE_TYPE)
    result += "float";
  else if (base_ == CHAR16_T_BASE_TYPE)
    result += "char16_t";
    else if (base_ == CHAR32_T_BASE_TYPE)
    result += "char32_t";
    else if (base_ == WCHAR_T_BASE_TYPE)
    result += "wchar_t";

  return result;
}

/// Convert the current instance of @ref integral_type into its string
/// representation.
///
/// @return the string representation of the current instance of @ref
/// integral_type.
integral_type::operator string() const
{return to_string();}

// </integral_type definitions>

//<type_decl definitions>

/// Constructor.
///
/// @param env the environment we are operating from.
///
/// @param name the name of the type declaration.
///
/// @param size_in_bits the size of the current type_decl, in bits.
///
/// @param alignment_in_bits the alignment of the current typ, in
/// bits.
///
/// @param locus the source location of the current type declaration.
///
/// @param linkage_name the linkage_name of the current type declaration.
///
/// @param vis the visibility of the type declaration.
type_decl::type_decl(const environment* env,
		     const string&	name,
		     size_t		size_in_bits,
		     size_t		alignment_in_bits,
		     const location&	locus,
		     const string&	linkage_name,
		     visibility	vis)

  : type_or_decl_base(env,
		      BASIC_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE),
    decl_base(env, name, locus, linkage_name, vis),
    type_base(env, size_in_bits, alignment_in_bits)
{
  runtime_type_instance(this);

  integral_type::base_type base_type = integral_type::INT_BASE_TYPE;
  integral_type::modifiers_type modifiers = integral_type::NO_MODIFIER;
  integral_type int_type(base_type, modifiers);
  if (parse_integral_type(name, int_type))
    {
      // Convert the integral_type into its canonical string
      // representation.
      string integral_type_name = int_type;

      // Set the name of this type_decl to the canonical string
      // representation above
      set_name(integral_type_name);
      set_qualified_name(get_name());

      if (!get_linkage_name().empty())
	set_linkage_name(integral_type_name);
    }
}

/// Compares two instances of @ref type_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const type_decl& l, const type_decl& r, change_kind* k)
{
  bool result = equals(static_cast<const decl_base&>(l),
		       static_cast<const decl_base&>(r),
		       k);
  if (!k && !result)
    return false;

  result &= equals(static_cast<const type_base&>(l),
		   static_cast<const type_base&>(r),
		   k);
  return result;
}

/// Return true if both types equals.
///
/// This operator re-uses the overload that takes a decl_base.
///
/// Note that this does not check the scopes of any of the types.
///
/// @param o the other type_decl to check agains.
bool
type_decl::operator==(const type_base& o) const
{
  const decl_base* other = dynamic_cast<const decl_base*>(&o);
  if (!other)
    return false;
  return *this == *other;
}

/// Return true if both types equals.
///
/// Note that this does not check the scopes of any of the types.
///
/// @param o the other type_decl to check against.
bool
type_decl::operator==(const decl_base& o) const
{
  const type_decl* other = dynamic_cast<const type_decl*>(&o);
  if (!other)
    return false;
  return try_canonical_compare(this, other);
}

/// Return true if both types equals.
///
/// Note that this does not check the scopes of any of the types.
///
/// @param o the other type_decl to check against.
///
/// @return true iff the current isntance equals @p o
bool
type_decl::operator==(const type_decl& o) const
{
  const decl_base& other = o;
  return *this == other;
}

/// Inequality operator.
///
/// @param o the other type to compare against.
///
/// @return true iff the current instance is different from @p o.
bool
type_decl::operator!=(const type_decl& o) const
{return !operator==(o);}

/// Equality operator for @ref type_decl_sptr.
///
/// @param l the first operand to compare.
///
/// @param r the second operand to compare.
///
/// @return true iff @p l equals @p r.
bool
operator==(const type_decl_sptr& l, const type_decl_sptr& r)
{
  if (!!l != !!r)
    return false;
  if (l.get() == r.get())
    return true;
  return *l == *r;
}

/// Inequality operator for @ref type_decl_sptr.
///
/// @param l the first operand to compare.
///
/// @param r the second operand to compare.
///
/// @return true iff @p l is different from @p r.
bool
operator!=(const type_decl_sptr& l, const type_decl_sptr& r)
{return !operator==(l, r);}

/// Get the pretty representation of the current instance of @ref
/// type_decl.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @param qualified_name if true, names emitted in the pretty
/// representation are fully qualified.
///
/// @return the pretty representatin of the @ref type_decl.
string
type_decl::get_pretty_representation(bool internal,
				     bool qualified_name) const
{
  if (qualified_name)
    return get_qualified_name(internal);
  return get_name();
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
type_decl::traverse(ir_node_visitor& v)
{
  if (v.type_node_has_been_visited(this))
    return true;

  v.visit_begin(this);
  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);

  return result;
}

type_decl::~type_decl()
{}
//</type_decl definitions>

// <scope_type_decl definitions>

/// Constructor.
///
/// @param env the environment we are operating from.
///
/// @param name the name of the type.
///
/// @param size_in_bits the size of the type, in bits.
///
/// @param alignment_in_bits the alignment of the type, in bits.
///
/// @param locus the source location where the type is defined.
///
/// @param vis the visibility of the type.
scope_type_decl::scope_type_decl(const environment*	env,
				 const string&		name,
				 size_t		size_in_bits,
				 size_t		alignment_in_bits,
				 const location&	locus,
				 visibility		vis)
  : type_or_decl_base(env,
		      ABSTRACT_SCOPE_TYPE_DECL
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE),
    decl_base(env, name, locus, "", vis),
    type_base(env, size_in_bits, alignment_in_bits),
    scope_decl(env, name, locus)
{}

/// Compares two instances of @ref scope_type_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const scope_type_decl& l, const scope_type_decl& r, change_kind* k)
{
  bool result = equals(static_cast<const scope_decl&>(l),
		  static_cast<const scope_decl&>(r),
		  k);

  if (!k && !result)
    return false;

  result &= equals(static_cast<const type_base&>(l),
		   static_cast<const type_base&>(r),
		   k);

  return result;
}

/// Equality operator between two scope_type_decl.
///
/// Note that this function does not consider the scope of the scope
/// types themselves.
///
/// @return true iff both scope types are equal.
bool
scope_type_decl::operator==(const decl_base& o) const
{
  const scope_type_decl* other = dynamic_cast<const scope_type_decl*>(&o);
  if (!other)
    return false;
  return try_canonical_compare(this, other);
}

/// Equality operator between two scope_type_decl.
///
/// This re-uses the equality operator that takes a decl_base.
///
/// @param o the other scope_type_decl to compare against.
///
/// @return true iff both scope types are equal.
bool
scope_type_decl::operator==(const type_base& o) const
{
  const decl_base* other = dynamic_cast<const decl_base*>(&o);
  if (!other)
    return false;

  return *this == *other;
}

/// Traverses an instance of @ref scope_type_decl, visiting all the
/// sub-types and decls that it might contain.
///
/// @param v the visitor that is used to visit every IR sub-node of
/// the current node.
///
/// @return true if either
///  - all the children nodes of the current IR node were traversed
///    and the calling code should keep going with the traversing.
///  - or the current IR node is already being traversed.
/// Otherwise, returning false means that the calling code should not
/// keep traversing the tree.
bool
scope_type_decl::traverse(ir_node_visitor& v)
{
  if (visiting())
    return true;

  if (v.type_node_has_been_visited(this))
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      for (scope_decl::declarations::const_iterator i =
	     get_member_decls().begin();
	   i != get_member_decls ().end();
	   ++i)
	if (!(*i)->traverse(v))
	  break;
      visiting(false);
    }

  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);

  return result;
}

scope_type_decl::~scope_type_decl()
{}
// </scope_type_decl definitions>

// <namespace_decl>

/// Constructor.
///
/// @param the environment we are operatin from.
///
/// @param name the name of the namespace.
///
/// @param locus the source location where the namespace is defined.
///
/// @param vis the visibility of the namespace.
namespace_decl::namespace_decl(const environment*	env,
			       const string&		name,
			       const location&		locus,
			       visibility		vis)
    // We need to call the constructor of decl_base directly here
    // because it is virtually inherited by scope_decl.  Note that we
    // just implicitely call the default constructor for scope_decl
    // here, as what we really want is to initialize the decl_base
    // subobject.  Wow, virtual inheritance is		useful, but setting it
    // up is ugly.
  : type_or_decl_base(env,
		      NAMESPACE_DECL
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, name, locus, "", vis),
    scope_decl(env, name, locus)
{
  runtime_type_instance(this);
}

/// Build and return a copy of the pretty representation of the
/// namespace.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @param qualified_name if true, names emitted in the pretty
/// representation are fully qualified.
///
/// @return a copy of the pretty representation of the namespace.
string
namespace_decl::get_pretty_representation(bool internal,
					  bool qualified_name) const
{
  string r =
    "namespace " + scope_decl::get_pretty_representation(internal,
							 qualified_name);
  return r;
}

/// Return true iff both namespaces and their members are equal.
///
/// Note that this function does not check if the scope of these
/// namespaces are equal.
bool
namespace_decl::operator==(const decl_base& o) const
{
  const namespace_decl* other = dynamic_cast<const namespace_decl*>(&o);
  if (!other)
    return false;
  return scope_decl::operator==(*other);
}

/// Test if the current namespace_decl is empty or contains empty
/// namespaces itself.
///
/// @return true iff the current namespace_decl is empty or contains
/// empty itself.
bool
namespace_decl::is_empty_or_has_empty_sub_namespaces() const
{
  if (is_empty())
    return true;

  for (declarations::const_iterator i = get_member_decls().begin();
       i != get_member_decls().end();
       ++i)
    {
      if (!is_namespace(*i))
	return false;

      namespace_decl_sptr ns = is_namespace(*i);
      ABG_ASSERT(ns);

      if (!ns->is_empty_or_has_empty_sub_namespaces())
	return false;
    }

  return true;
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance and on its
/// member nodes.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
namespace_decl::traverse(ir_node_visitor& v)
{
  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      scope_decl::declarations::const_iterator i;
      for (i = get_member_decls().begin();
	   i != get_member_decls ().end();
	   ++i)
	{
	  ir_traversable_base_sptr t =
	    dynamic_pointer_cast<ir_traversable_base>(*i);
	  if (t)
	    if (!t->traverse (v))
	      break;
	}
      visiting(false);
    }
  return v.visit_end(this);
}

namespace_decl::~namespace_decl()
{
}

// </namespace_decl>

// <qualified_type_def>

/// Type of the private data of qualified_type_def.
class qualified_type_def::priv
{
  friend class qualified_type_def;

  qualified_type_def::CV	cv_quals_;
  // Before the type is canonicalized, this is used as a temporary
  // internal name.
  interned_string		temporary_internal_name_;
  // Once the type is canonicalized, this is used as the internal
  // name.
  interned_string		internal_name_;
  weak_ptr<type_base>		underlying_type_;

  priv()
    : cv_quals_(CV_NONE)
  {}

  priv(qualified_type_def::CV quals,
       type_base_sptr t)
    : cv_quals_(quals),
      underlying_type_(t)
  {}
};// end class qualified_type_def::priv

/// Build the name of the current instance of qualified type.
///
/// @param fully_qualified if true, build a fully qualified name.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the newly-built name.
string
qualified_type_def::build_name(bool fully_qualified, bool internal) const
{
  ABG_ASSERT(get_underlying_type());

  return get_name_of_qualified_type(get_underlying_type(),
				    get_cv_quals(),
				    fully_qualified, internal);
}

/// This function is automatically invoked whenever an instance of
/// this type is canonicalized.
///
/// It's an overload of the virtual type_base::on_canonical_type_set.
///
/// We put here what is thus meant to be executed only at the point of
/// type canonicalization.
void
qualified_type_def::on_canonical_type_set()
{clear_qualified_name();}

/// Constructor of the qualified_type_def
///
/// @param type the underlying type
///
/// @param quals a bitfield representing the const/volatile qualifiers
///
/// @param locus the location of the qualified type definition
qualified_type_def::qualified_type_def(type_base_sptr		type,
				       CV			quals,
				       const location&		locus)
  : type_or_decl_base(type->get_environment(),
		      QUALIFIED_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE),
    type_base(type->get_environment(), type->get_size_in_bits(),
	      type->get_alignment_in_bits()),
    decl_base(type->get_environment(), "", locus, "",
	      dynamic_pointer_cast<decl_base>(type)->get_visibility()),
    priv_(new priv(quals, type))
{
  runtime_type_instance(this);
  interned_string name = type->get_environment()->intern(build_name(false));
  set_name(name);
}

/// Get the size of the qualified type def.
///
/// This is an overload for type_base::get_size_in_bits().
///
/// @return the size of the qualified type.
size_t
qualified_type_def::get_size_in_bits() const
{
  size_t s = get_underlying_type()->get_size_in_bits();
  if (s != type_base::get_size_in_bits())
    const_cast<qualified_type_def*>(this)->set_size_in_bits(s);
  return type_base::get_size_in_bits();
}

/// Compares two instances of @ref qualified_type_def.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const qualified_type_def& l, const qualified_type_def& r, change_kind* k)
{
  bool result = true;
  if (l.get_cv_quals() != r.get_cv_quals())
    {
      result = false;
      if (k)
	*k |= LOCAL_NON_TYPE_CHANGE_KIND;
      else
	return false;
    }

  if (l.get_underlying_type() != r.get_underlying_type())
    {
      result = false;
      if (k)
	{
	  if (!types_have_similar_structure(l.get_underlying_type().get(),
					    r.get_underlying_type().get()))
	    // Underlying type changes in which the structure of the
	    // type changed are considered local changes to the
	    // qualified type.
	    *k |= LOCAL_TYPE_CHANGE_KIND;
	  else
	    *k |= SUBTYPE_CHANGE_KIND;
	}
      else
	// okay strictly speaking this is not necessary, but I am
	// putting it here to maintenance; that is, so that adding
	// subsequent clauses needed to compare two qualified types
	// later still works.
	return false;
    }

  return result;
}

/// Equality operator for qualified types.
///
/// Note that this function does not check for equality of the scopes.
///
///@param o the other qualified type to compare against.
///
/// @return true iff both qualified types are equal.
bool
qualified_type_def::operator==(const decl_base& o) const
{
  const qualified_type_def* other =
    dynamic_cast<const qualified_type_def*>(&o);
  if (!other)
    return false;
  return try_canonical_compare(this, other);
}

/// Equality operator for qualified types.
///
/// Note that this function does not check for equality of the scopes.
/// Also, this re-uses the equality operator above that takes a
/// decl_base.
///
///@param o the other qualified type to compare against.
///
/// @return true iff both qualified types are equal.
bool
qualified_type_def::operator==(const type_base& o) const
{
  const decl_base* other = dynamic_cast<const decl_base*>(&o);
  if (!other)
    return false;
  return *this == *other;
}

/// Equality operator for qualified types.
///
/// Note that this function does not check for equality of the scopes.
/// Also, this re-uses the equality operator above that takes a
/// decl_base.
///
///@param o the other qualified type to compare against.
///
/// @return true iff both qualified types are equal.
bool
qualified_type_def::operator==(const qualified_type_def& o) const
{
  const decl_base* other = dynamic_cast<const decl_base*>(&o);
  if (!other)
    return false;
  return *this == *other;
}

/// Implementation for the virtual qualified name builder for @ref
/// qualified_type_def.
///
/// @param qualified_name the output parameter to hold the resulting
/// qualified name.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
void
qualified_type_def::get_qualified_name(interned_string& qualified_name,
				       bool internal) const
{qualified_name = get_qualified_name(internal);}

/// Implementation of the virtual qualified name builder/getter.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the resulting qualified name.
const interned_string&
qualified_type_def::get_qualified_name(bool internal) const
{
  const environment* env = get_environment();
  ABG_ASSERT(env);

  if (!get_canonical_type())
    {
      // The type hasn't been canonicalized yet. We want to return a
      // temporary name that is not cached because the structure of
      // this type (and so its name) can change until its
      // canonicalized.
      if (internal)
	{
	  // We are asked to return a temporary *internal* name.
	  // Lets compute it and return a reference to where it's
	  // stored.
	  priv_->temporary_internal_name_ =
	      env->intern(build_name(true, /*internal=*/true));
	  return priv_->temporary_internal_name_;
	}
      else
	{
	  // We are asked to return a temporary non-internal name.
	    set_temporary_qualified_name
	      (env->intern(build_name(true, /*internal=*/false)));
	  return peek_temporary_qualified_name();
	}
    }
  else
    {
      // The type has already been canonicalized. We want to return
      // the definitive name and cache it.
      if (internal)
	{
	  if (priv_->internal_name_.empty())
	    priv_->internal_name_ =
	      env->intern(build_name(/*qualified=*/true,
				     /*internal=*/true));
	  return priv_->internal_name_;
	}
      else
	{
	  if (peek_qualified_name().empty())
	    set_qualified_name
	      (env->intern(build_name(/*qualified=*/true,
				      /*internal=*/false)));
	  return peek_qualified_name();
	}
    }
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
qualified_type_def::traverse(ir_node_visitor& v)
{
  if (v.type_node_has_been_visited(this))
    return true;

  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (type_base_sptr t = get_underlying_type())
	t->traverse(v);
      visiting(false);
    }
  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);
  return result;
}

qualified_type_def::~qualified_type_def()
{
}

/// Getter of the const/volatile qualifier bit field
qualified_type_def::CV
qualified_type_def::get_cv_quals() const
{return priv_->cv_quals_;}

/// Setter of the const/value qualifiers bit field
void
qualified_type_def::set_cv_quals(CV cv_quals)
{priv_->cv_quals_ = cv_quals;}

/// Compute and return the string prefix or suffix representing the
/// qualifiers hold by the current instance of @ref
/// qualified_type_def.
///
/// @return the newly-built cv string.
string
qualified_type_def::get_cv_quals_string_prefix() const
{return get_string_representation_of_cv_quals(priv_->cv_quals_);}

/// Getter of the underlying type
type_base_sptr
qualified_type_def::get_underlying_type() const
{return priv_->underlying_type_.lock();}

/// Setter of the underlying type.
///
/// @param t the new underlying type.
void
qualified_type_def::set_underlying_type(const type_base_sptr& t)
{priv_->underlying_type_ = t;}

/// Non-member equality operator for @ref qualified_type_def
///
/// @param l the left-hand side of the equality operator
///
/// @param r the right-hand side of the equality operator
///
/// @return true iff @p l and @p r equals.
bool
operator==(const qualified_type_def_sptr& l, const qualified_type_def_sptr& r)
{
  if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// Non-member inequality operator for @ref qualified_type_def
///
/// @param l the left-hand side of the equality operator
///
/// @param r the right-hand side of the equality operator
///
/// @return true iff @p l and @p r equals.
bool
operator!=(const qualified_type_def_sptr& l, const qualified_type_def_sptr& r)
{return ! operator==(l, r);}

/// Overloaded bitwise OR operator for cv qualifiers.
qualified_type_def::CV
operator|(qualified_type_def::CV lhs, qualified_type_def::CV rhs)
{
  return static_cast<qualified_type_def::CV>
    (static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs));
}

/// Overloaded bitwise |= operator for cv qualifiers.
qualified_type_def::CV&
operator|=(qualified_type_def::CV& l, qualified_type_def::CV r)
{
  l = l | r;
  return l;
}

/// Overloaded bitwise AND operator for CV qualifiers.
qualified_type_def::CV
operator&(qualified_type_def::CV lhs, qualified_type_def::CV rhs)
{
    return static_cast<qualified_type_def::CV>
    (static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs));
}

/// Overloaded bitwise inverting operator for CV qualifiers.
qualified_type_def::CV
operator~(qualified_type_def::CV q)
{return static_cast<qualified_type_def::CV>(~static_cast<unsigned>(q));}

/// Streaming operator for qualified_type_decl::CV
///
/// @param o the output stream to serialize the cv qualifier to.
///
/// @param cv the cv qualifier to serialize.
///
/// @return the output stream used.
std::ostream&
operator<<(std::ostream& o, qualified_type_def::CV cv)
{
  string str;

  switch (cv)
    {
    case qualified_type_def::CV_NONE:
      str = "none";
      break;
    case qualified_type_def::CV_CONST:
      str = "const";
      break;
    case qualified_type_def::CV_VOLATILE:
      str = "volatile";
      break;
    case qualified_type_def::CV_RESTRICT:
      str = "restrict";
      break;
    }

  o << str;
  return o;
}

// </qualified_type_def>

//<pointer_type_def definitions>

/// Private data structure of the @ref pointer_type_def.
struct pointer_type_def::priv
{
  type_base_wptr pointed_to_type_;
  type_base* naked_pointed_to_type_;
  interned_string internal_qualified_name_;
  interned_string temp_internal_qualified_name_;

  priv(const type_base_sptr& t)
    : pointed_to_type_(type_or_void(t, 0)),
      naked_pointed_to_type_(t.get())
  {}

  priv()
    : naked_pointed_to_type_()
  {}
}; //end struct pointer_type_def

/// This function is automatically invoked whenever an instance of
/// this type is canonicalized.
///
/// It's an overload of the virtual type_base::on_canonical_type_set.
///
/// We put here what is thus meant to be executed only at the point of
/// type canonicalization.
void
pointer_type_def::on_canonical_type_set()
{clear_qualified_name();}

pointer_type_def::pointer_type_def(const type_base_sptr&	pointed_to,
				   size_t			size_in_bits,
				   size_t			align_in_bits,
				   const location&		locus)
  : type_or_decl_base(pointed_to->get_environment(),
		      POINTER_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE),
    type_base(pointed_to->get_environment(), size_in_bits, align_in_bits),
    decl_base(pointed_to->get_environment(), "", locus, ""),
    priv_(new priv(pointed_to))
{
  runtime_type_instance(this);
  try
    {
      ABG_ASSERT(pointed_to);
      const environment* env = pointed_to->get_environment();
      decl_base_sptr pto = dynamic_pointer_cast<decl_base>(pointed_to);
      string name = (pto ? pto->get_name() : string("void")) + "*";
      set_name(env->intern(name));
      if (pto)
	set_visibility(pto->get_visibility());
    }
  catch (...)
    {}
}

/// Compares two instances of @ref pointer_type_def.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const pointer_type_def& l, const pointer_type_def& r, change_kind* k)
{
  bool result = (l.get_pointed_to_type() == r.get_pointed_to_type());
  if (!result)
    if (k)
      {
	if (!types_have_similar_structure(&l, &r))
	  // pointed-to type changes in which the structure of the
	  // type changed are considered local changes to the pointer
	  // type.
	  *k |= LOCAL_TYPE_CHANGE_KIND;
	*k |= SUBTYPE_CHANGE_KIND;
      }

  return result;
}

/// Return true iff both instances of pointer_type_def are equal.
///
/// Note that this function does not check for the scopes of the this
/// types.
bool
pointer_type_def::operator==(const decl_base& o) const
{
  const pointer_type_def* other = is_pointer_type(&o);
  if (!other)
    return false;
  return try_canonical_compare(this, other);
}

/// Return true iff both instances of pointer_type_def are equal.
///
/// Note that this function does not check for the scopes of the
/// types.
///
/// @param other the other type to compare against.
///
/// @return true iff @p other equals the current instance.
bool
pointer_type_def::operator==(const type_base& other) const
{
  const decl_base* o = is_decl(&other);
  if (!o)
    return false;
  return *this == *o;
}

/// Return true iff both instances of pointer_type_def are equal.
///
/// Note that this function does not check for the scopes of the
/// types.
///
/// @param other the other type to compare against.
///
/// @return true iff @p other equals the current instance.
bool
pointer_type_def::operator==(const pointer_type_def& other) const
{
  const decl_base& o = other;
  return *this == o;
}

/// Getter of the pointed-to type.
///
/// @return the pointed-to type.
const type_base_sptr
pointer_type_def::get_pointed_to_type() const
{return priv_->pointed_to_type_.lock();}

/// Getter of a naked pointer to the pointed-to type.
///
/// @return a naked pointed to the pointed-to type.
type_base*
pointer_type_def::get_naked_pointed_to_type() const
{return priv_->naked_pointed_to_type_;}

/// Build and return the qualified name of the current instance of
/// @ref pointer_type_def.
///
/// @param qn output parameter.  The resulting qualified name.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
void
pointer_type_def::get_qualified_name(interned_string& qn, bool internal) const
{qn = get_qualified_name(internal);}

/// Build, cache and return the qualified name of the current instance
/// of @ref pointer_type_def.  Subsequent invocations of this function
/// return the cached value.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the resulting qualified name.
const interned_string&
pointer_type_def::get_qualified_name(bool internal) const
{
  type_base* pointed_to_type = get_naked_pointed_to_type();

  if (internal)
    {
      if (get_canonical_type())
	{
	  if (priv_->internal_qualified_name_.empty())
	    priv_->internal_qualified_name_ =
	      get_name_of_pointer_to_type(*pointed_to_type,
					  /*qualified_name=*/true,
					  /*internal=*/true);
	  return priv_->internal_qualified_name_;
	}
      else
	{
	  // As the type hasn't yet been canonicalized, its structure
	  // (and so its name) can change.  So let's invalidate the
	  // cache where we store its name at each invocation of this
	  // function.
	  priv_->temp_internal_qualified_name_ =
	    get_name_of_pointer_to_type(*pointed_to_type,
					/*qualified_name=*/true,
					/*internal=*/true);
	  return priv_->temp_internal_qualified_name_;
	}
    }
  else
    {
      if (get_naked_canonical_type())
	{
	  if (decl_base::peek_qualified_name().empty())
	    set_qualified_name
	      (get_name_of_pointer_to_type(*pointed_to_type,
					   /*qualified_name=*/true,
					   /*internal=*/false));
	  return decl_base::peek_qualified_name();
	}
      else
	{
	  // As the type hasn't yet been canonicalized, its structure
	  // (and so its name) can change.  So let's invalidate the
	  // cache where we store its name at each invocation of this
	  // function.
	  set_qualified_name
	    (get_name_of_pointer_to_type(*pointed_to_type,
					 /*qualified_name=*/true,
					 /*internal=*/false));
	  return decl_base::peek_qualified_name();
	}
    }
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
pointer_type_def::traverse(ir_node_visitor& v)
{
  if (v.type_node_has_been_visited(this))
    return true;

  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (type_base_sptr t = get_pointed_to_type())
	t->traverse(v);
      visiting(false);
    }

  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);
  return result;
}

pointer_type_def::~pointer_type_def()
{}

/// Turn equality of shared_ptr of @ref pointer_type_def into a deep
/// equality; that is, make it compare the pointed to objects too.
///
/// @param l the shared_ptr of @ref pointer_type_def on left-hand-side
/// of the equality.
///
/// @param r the shared_ptr of @ref pointer_type_def on
/// right-hand-side of the equality.
///
/// @return true if the @ref pointer_type_def pointed to by the
/// shared_ptrs are equal, false otherwise.
bool
operator==(const pointer_type_def_sptr& l, const pointer_type_def_sptr& r)
{
  if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// Turn inequality of shared_ptr of @ref pointer_type_def into a deep
/// equality; that is, make it compare the pointed to objects too.
///
/// @param l the shared_ptr of @ref pointer_type_def on left-hand-side
/// of the equality.
///
/// @param r the shared_ptr of @ref pointer_type_def on
/// right-hand-side of the equality.
///
/// @return true iff the @ref pointer_type_def pointed to by the
/// shared_ptrs are different.
bool
operator!=(const pointer_type_def_sptr& l, const pointer_type_def_sptr& r)
{return !operator==(l, r);}

// </pointer_type_def definitions>

// <reference_type_def definitions>

/// This function is automatically invoked whenever an instance of
/// this type is canonicalized.
///
/// It's an overload of the virtual type_base::on_canonical_type_set.
///
/// We put here what is thus meant to be executed only at the point of
/// type canonicalization.
void
reference_type_def::on_canonical_type_set()
{clear_qualified_name();}

reference_type_def::reference_type_def(const type_base_sptr	pointed_to,
				       bool			lvalue,
				       size_t			size_in_bits,
				       size_t			align_in_bits,
				       const location&		locus)
  : type_or_decl_base(pointed_to->get_environment(),
		      REFERENCE_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE),
    type_base(pointed_to->get_environment(), size_in_bits, align_in_bits),
    decl_base(pointed_to->get_environment(), "", locus, ""),
    is_lvalue_(lvalue)
{
  runtime_type_instance(this);

  try
    {
      decl_base_sptr pto = dynamic_pointer_cast<decl_base>(pointed_to);
      string name;
      if (pto)
	{
	  set_visibility(pto->get_visibility());
	  name = string(pto->get_name()) + "&";
	}
      else
	name = string(get_type_name(is_function_type(pointed_to),
				    /*qualified_name=*/true)) + "&";

      if (!is_lvalue())
	name += "&";
      environment* env = pointed_to->get_environment();
      ABG_ASSERT(env);
      set_name(env->intern(name));

      pointed_to_type_ = type_base_wptr(type_or_void(pointed_to, 0));
    }
  catch (...)
    {}
}

/// Compares two instances of @ref reference_type_def.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const reference_type_def& l, const reference_type_def& r, change_kind* k)
{
  if (l.is_lvalue() != r.is_lvalue())
    {
      if (k)
	*k |= LOCAL_TYPE_CHANGE_KIND;
      return false;
    }

  bool result = (l.get_pointed_to_type() == r.get_pointed_to_type());
  if (!result)
    if (k)
      {
	if (!types_have_similar_structure(&l, &r))
	  *k |= LOCAL_TYPE_CHANGE_KIND;
	*k |= SUBTYPE_CHANGE_KIND;
      }
  return result;
}

/// Equality operator of the @ref reference_type_def type.
///
/// @param o the other instance of @ref reference_type_def to compare
/// against.
///
/// @return true iff the two instances are equal.
bool
reference_type_def::operator==(const decl_base& o) const
{
  const reference_type_def* other =
    dynamic_cast<const reference_type_def*>(&o);
  if (!other)
    return false;
  return try_canonical_compare(this, other);
}

/// Equality operator of the @ref reference_type_def type.
///
/// @param o the other instance of @ref reference_type_def to compare
/// against.
///
/// @return true iff the two instances are equal.
bool
reference_type_def::operator==(const type_base& o) const
{
  const decl_base* other = dynamic_cast<const decl_base*>(&o);
  if (!other)
    return false;
  return *this == *other;
}

/// Equality operator of the @ref reference_type_def type.
///
/// @param o the other instance of @ref reference_type_def to compare
/// against.
///
/// @return true iff the two instances are equal.
bool
reference_type_def::operator==(const reference_type_def& o) const
{
  const decl_base* other = dynamic_cast<const decl_base*>(&o);
  if (!other)
    return false;
  return *this == *other;
}

type_base_sptr
reference_type_def::get_pointed_to_type() const
{return pointed_to_type_.lock();}

bool
reference_type_def::is_lvalue() const
{return is_lvalue_;}

/// Build and return the qualified name of the current instance of the
/// @ref reference_type_def.
///
/// @param qn output parameter.  Is set to the newly-built qualified
/// name of the current instance of @ref reference_type_def.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
void
reference_type_def::get_qualified_name(interned_string& qn, bool internal) const
{qn = get_qualified_name(internal);}

/// Build, cache and return the qualified name of the current instance
/// of the @ref reference_type_def.  Subsequent invocations of this
/// function return the cached value.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the newly-built qualified name of the current instance of
/// @ref reference_type_def.
const interned_string&
reference_type_def::get_qualified_name(bool internal) const
{
  if (peek_qualified_name().empty()
      || !get_canonical_type())
    set_qualified_name(get_name_of_reference_to_type(*get_pointed_to_type(),
						     is_lvalue(),
						     /*qualified_name=*/true,
						     internal));
  return peek_qualified_name();
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
reference_type_def::traverse(ir_node_visitor& v)
{
  if (v.type_node_has_been_visited(this))
    return true;

  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (type_base_sptr t = get_pointed_to_type())
	t->traverse(v);
      visiting(false);
    }

  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);
  return result;
}

reference_type_def::~reference_type_def()
{}

/// Turn equality of shared_ptr of @ref reference_type_def into a deep
/// equality; that is, make it compare the pointed to objects too.
///
/// @param l the shared_ptr of @ref reference_type_def on left-hand-side
/// of the equality.
///
/// @param r the shared_ptr of @ref reference_type_def on
/// right-hand-side of the equality.
///
/// @return true if the @ref reference_type_def pointed to by the
/// shared_ptrs are equal, false otherwise.
bool
operator==(const reference_type_def_sptr& l, const reference_type_def_sptr& r)
{
  if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// Turn inequality of shared_ptr of @ref reference_type_def into a deep
/// equality; that is, make it compare the pointed to objects too.
///
/// @param l the shared_ptr of @ref reference_type_def on left-hand-side
/// of the equality.
///
/// @param r the shared_ptr of @ref reference_type_def on
/// right-hand-side of the equality.
///
/// @return true iff the @ref reference_type_def pointed to by the
/// shared_ptrs are different.
bool
operator!=(const reference_type_def_sptr& l, const reference_type_def_sptr& r)
{return !operator==(l, r);}

// </reference_type_def definitions>

// <array_type_def definitions>

// <array_type_def::subrange_type>

// <array_type_def::subrante_type::bound_value>

/// Default constructor of the @ref
/// array_type_def::subrange_type::bound_value class.
///
/// Constructs an unsigned bound_value of value zero.
array_type_def::subrange_type::bound_value::bound_value()
  : s_(UNSIGNED_SIGNEDNESS)
{
  v_.unsigned_ = 0;
}

/// Initialize an unsigned bound_value with a given value.
///
/// @param v the initial bound value.
array_type_def::subrange_type::bound_value::bound_value(uint64_t v)
  : s_(UNSIGNED_SIGNEDNESS)
{
  v_.unsigned_ = v;
}

/// Initialize a signed bound_value with a given value.
///
/// @param v the initial bound value.
array_type_def::subrange_type::bound_value::bound_value(int64_t v)
  : s_(SIGNED_SIGNEDNESS)
{
  v_.signed_ = v;
}

/// Getter of the signedness (unsigned VS signed) of the bound value.
///
/// @return the signedness of the bound value.
enum array_type_def::subrange_type::bound_value::signedness
array_type_def::subrange_type::bound_value::get_signedness() const
{return s_;}

/// Setter of the signedness (unsigned VS signed) of the bound value.
///
/// @param s the new signedness of the bound value.
void
array_type_def::subrange_type::bound_value::set_signedness(enum signedness s)
{ s_ = s;}

/// Getter of the bound value as a signed value.
///
/// @return the bound value as signed.
int64_t
array_type_def::subrange_type::bound_value::get_signed_value() const
{return v_.signed_;
}

/// Getter of the bound value as an unsigned value.
///
/// @return the bound value as unsigned.
uint64_t
array_type_def::subrange_type::bound_value::get_unsigned_value()
{return v_.unsigned_;}

/// Setter of the bound value as unsigned.
///
/// @param v the new unsigned value.
void
array_type_def::subrange_type::bound_value::set_unsigned(uint64_t v)
{
    s_ = UNSIGNED_SIGNEDNESS;
  v_.unsigned_ = v;
}

/// Setter of the bound value as signed.
///
/// @param v the new signed value.
void
array_type_def::subrange_type::bound_value::set_signed(int64_t v)
{
  s_ = SIGNED_SIGNEDNESS;
  v_.signed_ = v;
}

/// Equality operator of the bound value.
///
/// @param v the other bound value to compare with.
///
/// @return true iff the current bound value equals @p v.
bool
array_type_def::subrange_type::bound_value::operator==(const bound_value& v) const
{
  return s_ == v.s_ && v_.unsigned_ == v.v_.unsigned_;
}

// </array_type_def::subrante_type::bound_value>

struct array_type_def::subrange_type::priv
{
  bound_value		lower_bound_;
  bound_value		upper_bound_;
  type_base_wptr	underlying_type_;
  translation_unit::language language_;
  bool			infinite_;

  priv(bound_value ub,
       translation_unit::language l = translation_unit::LANG_C11)
    : upper_bound_(ub), language_(l), infinite_(false)
  {}

  priv(bound_value lb, bound_value ub,
       translation_unit::language l = translation_unit::LANG_C11)
    : lower_bound_(lb), upper_bound_(ub),
      language_(l), infinite_(false)
  {}

  priv(bound_value lb, bound_value ub, const type_base_sptr &u,
       translation_unit::language l = translation_unit::LANG_C11)
    : lower_bound_(lb), upper_bound_(ub), underlying_type_(u),
      language_(l), infinite_(false)
  {}
};

/// Constructor of an array_type_def::subrange_type type.
///
/// @param env the environment this type was created from.
///
/// @param name the name of the subrange type.
///
/// @param lower_bound the lower bound of the array.  This is
/// generally zero (at least for C and C++).
///
/// @param upper_bound the upper bound of the array.
///
/// @param underlying_type the underlying type of the subrange type.
///
/// @param loc the source location where the type is defined.
array_type_def::subrange_type::subrange_type(const environment* env,
					     const string&	name,
					     bound_value	lower_bound,
					     bound_value	upper_bound,
					     const type_base_sptr& utype,
					     const location&	loc,
					     translation_unit::language l)
  : type_or_decl_base(env, ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE),
    type_base(env,
	      upper_bound.get_unsigned_value()
	      - lower_bound.get_unsigned_value(),
	      0),
    decl_base(env, name, loc, ""),
    priv_(new priv(lower_bound, upper_bound, utype, l))
{
  runtime_type_instance(this);
}

/// Constructor of the array_type_def::subrange_type type.
///
/// @param env the environment this type is being created in.
///
/// @param name the name of the subrange type.
///
/// @param lower_bound the lower bound of the array.  This is
/// generally zero (at least for C and C++).
///
/// @param upper_bound the upper bound of the array.
///
/// @param loc the source location where the type is defined.
///
/// @param l the language that generated this subrange.
array_type_def::subrange_type::subrange_type(const environment* env,
					     const string&	name,
					     bound_value	lower_bound,
					     bound_value	upper_bound,
					     const location&	loc,
					     translation_unit::language l)
  : type_or_decl_base(env, ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE),
    type_base(env,
	      upper_bound.get_unsigned_value()
	      - lower_bound.get_unsigned_value(), 0),
    decl_base(env, name, loc, ""),
    priv_(new priv(lower_bound, upper_bound, l))
{
  runtime_type_instance(this);
}

/// Constructor of the array_type_def::subrange_type type.
///
/// @param env the environment this type is being created from.
///
/// @param name of the name of type.
///
/// @param upper_bound the upper bound of the array.  The lower bound
/// is considered to be zero.
///
/// @param loc the source location of the type.
///
/// @param the language that generated this type.
array_type_def::subrange_type::subrange_type(const environment* env,
					     const string&	name,
					     bound_value	upper_bound,
					     const location&	loc,
					     translation_unit::language l)
  : type_or_decl_base(env, ABSTRACT_TYPE_BASE | ABSTRACT_DECL_BASE),
    type_base(env, upper_bound.get_unsigned_value(), 0),
    decl_base(env, name, loc, ""),
    priv_(new priv(upper_bound, l))
{
  runtime_type_instance(this);
}

/// Getter of the underlying type of the subrange, that is, the type
/// that defines the range.
///
/// @return the underlying type.
type_base_sptr
array_type_def::subrange_type::get_underlying_type() const
{return priv_->underlying_type_.lock();}

/// Setter of the underlying type of the subrange, that is, the type
/// that defines the range.
///
/// @param u the new underlying type.
void
array_type_def::subrange_type::set_underlying_type(const type_base_sptr &u)
{
  ABG_ASSERT(priv_->underlying_type_.expired());
  priv_->underlying_type_ = u;
}

/// Getter of the upper bound of the subrange type.
///
/// @return the upper bound of the subrange type.
int64_t
array_type_def::subrange_type::get_upper_bound() const
{return priv_->upper_bound_.get_signed_value();}

/// Getter of the lower bound of the subrange type.
///
/// @return the lower bound of the subrange type.
int64_t
array_type_def::subrange_type::get_lower_bound() const
{return priv_->lower_bound_.get_signed_value();}

/// Setter of the upper bound of the subrange type.
///
/// @param ub the new value of the upper bound.
void
array_type_def::subrange_type::set_upper_bound(int64_t ub)
{priv_->upper_bound_ = ub;}

/// Setter of the lower bound.
///
/// @param lb the new value of the lower bound.
void
array_type_def::subrange_type::set_lower_bound(int64_t lb)
{priv_->lower_bound_ = lb;}

/// Getter of the length of the subrange type.
///
/// Note that a length of zero means the array has an infinite (or
/// rather a non-known) size.
///
/// @return the length of the subrange type.
uint64_t
array_type_def::subrange_type::get_length() const
{
  if (is_infinite())
    return 0;

  ABG_ASSERT(get_upper_bound() >= get_lower_bound());
  return get_upper_bound() - get_lower_bound() + 1;
}

/// Test if the length of the subrange type is infinite.
///
/// @return true iff the length of the subrange type is infinite.
bool
array_type_def::subrange_type::is_infinite() const
{return priv_->infinite_;}

/// Set the infinite-ness status of the subrange type.
///
/// @param f true iff the length of the subrange type should be set to
/// being infinite.
void
array_type_def::subrange_type::is_infinite(bool f)
{priv_->infinite_ = f;}

/// Getter of the language that generated this type.
///
/// @return the language of this type.
translation_unit::language
array_type_def::subrange_type::get_language() const
{return priv_->language_;}

/// Return a string representation of the sub range.
///
/// @return the string representation of the sub range.
string
array_type_def::subrange_type::as_string() const
{
  std::ostringstream o;

  if (is_ada_language(get_language()))
    {
      type_base_sptr underlying_type = get_underlying_type();
      if (underlying_type)
	o << ir::get_pretty_representation(underlying_type, false) << " ";
      o << "range "<< get_lower_bound() << " .. " << get_upper_bound();
    }
  else if (is_infinite())
    o << "[]";
  else
    o << "["  << get_length() << "]";

  return o.str();
}

/// Return a string representation of a vector of subranges
///
/// @return the string representation of a vector of sub ranges.
string
array_type_def::subrange_type::vector_as_string(const vector<subrange_sptr>& v)
{
  if (v.empty())
    return "[]";

  string r;
  for (vector<subrange_sptr>::const_iterator i = v.begin();
       i != v.end();
       ++i)
    r += (*i)->as_string();

  return r;
}

/// Compares two isntances of @ref array_type_def::subrange_type.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const array_type_def::subrange_type& l,
       const array_type_def::subrange_type& r,
       change_kind* k)
{
  bool result = true;

  if (l.get_lower_bound() != r.get_lower_bound()
      || l.get_upper_bound() != r.get_upper_bound()
      || l.get_name() != r.get_name())
    {
      result = false;
      if (k)
	*k |= LOCAL_TYPE_CHANGE_KIND;
      else
	return result;
    }

#if 0
  // If we enable this, we need to update the reporting code too, to
  // report changes about range underlying types too.
  if (l.get_underlying_type() != r.get_underlying_type())
    {
      result = false;
      if (k)
	{
	  if (!types_have_similar_structure(l.get_underlying_type().get(),
					    r.get_underlying_type().get()))
	    *k |= LOCAL_TYPE_CHANGE_KIND;
	  else
	    *k |= SUBTYPE_CHANGE_KIND;
	}
      else
	return result;
    }
#endif
  return result;
}

/// Equality operator.
///
/// @param o the other subrange to test against.
///
/// @return true iff @p o equals the current instance of
/// array_type_def::subrange_type.
bool
array_type_def::subrange_type::operator==(const decl_base& o) const
{
  const subrange_type* other =
    dynamic_cast<const subrange_type*>(&o);
  if (!other)
    return false;
  return try_canonical_compare(this, other);
}

/// Equality operator.
///
/// @param o the other subrange to test against.
///
/// @return true iff @p o equals the current instance of
/// array_type_def::subrange_type.
bool
array_type_def::subrange_type::operator==(const type_base& o) const
{
  const decl_base* other = dynamic_cast<const decl_base*>(&o);
  if (!other)
    return false;
  return *this == *other;
}

/// Equality operator.
///
/// @param o the other subrange to test against.
///
/// @return true iff @p o equals the current instance of
/// array_type_def::subrange_type.
bool
array_type_def::subrange_type::operator==(const subrange_type& o) const
{
  const type_base &t = o;
  return operator==(t);
}

/// Inequality operator.
///
/// @param o the other subrange to test against.
///
/// @return true iff @p o is different from the current instance of
/// array_type_def::subrange_type.
bool
array_type_def::subrange_type::operator!=(const subrange_type& o) const
{return !operator==(o);}

/// Build a pretty representation for an
/// array_type_def::subrange_type.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the pretty representation of the current
/// instance of typedef_decl.
string
array_type_def::subrange_type::get_pretty_representation(bool, bool) const
{
  string name = get_name();
  string repr;

  if (name.empty())
    repr += "<anonymous range>";
  else
    repr += "<range " + get_name() + ">";
  repr += as_string();

  return repr;
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
array_type_def::subrange_type::traverse(ir_node_visitor& v)
{
  if (v.type_node_has_been_visited(this))
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (type_base_sptr u = get_underlying_type())
	u->traverse(v);
      visiting(false);
    }

  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);
  return result;
}

// </array_type_def::subrange_type>

struct array_type_def::priv
{
  type_base_wptr	element_type_;
  subranges_type	subranges_;
  interned_string	temp_internal_qualified_name_;
  interned_string	internal_qualified_name_;

  priv(type_base_sptr t)
    : element_type_(t) {}
  priv(type_base_sptr t, subranges_type subs)
    : element_type_(t), subranges_(subs) {}
};

/// Constructor for the type array_type_def
///
/// Note how the constructor expects a vector of subrange
/// objects. Parsing of the array information always entails
/// parsing the subrange info as well, thus the class subrange_type
/// is defined inside class array_type_def and also parsed
/// simultaneously.
///
/// @param e_type the type of the elements contained in the array
///
/// @param subs a vector of the array's subranges(dimensions)
///
/// @param locus the source location of the array type definition.
array_type_def::array_type_def(const type_base_sptr			e_type,
			       const std::vector<subrange_sptr>&	subs,
			       const location&				locus)
  : type_or_decl_base(e_type->get_environment(),
		      ARRAY_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE),
    type_base(e_type->get_environment(), 0, e_type->get_alignment_in_bits()),
    decl_base(e_type->get_environment(), locus),
    priv_(new priv(e_type))
{
  runtime_type_instance(this);
  append_subranges(subs);
}

string
array_type_def::get_subrange_representation() const
{
  string r = subrange_type::vector_as_string(get_subranges());
  return r;
}

/// Get the string representation of an @ref array_type_def.
///
/// @param a the array type to consider.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
static string
get_type_representation(const array_type_def& a, bool internal)
{
  type_base_sptr e_type = a.get_element_type();
  decl_base_sptr d = get_type_declaration(e_type);
  string r;

  if (is_ada_language(a.get_language()))
    {
      std::ostringstream o;
      o << "array ("
	<< a.get_subrange_representation()
	<< ") of "
	<<  e_type->get_pretty_representation(internal);
    }
  else
    {
      if (internal)
	r = get_type_name(e_type, /*qualified=*/true, /*internal=*/true)
	  + a.get_subrange_representation();
      else
	r = get_type_name(e_type, /*qualified=*/false, /*internal=*/false)
	  + a.get_subrange_representation();
    }

  return r;
}

/// Get the pretty representation of the current instance of @ref
/// array_type_def.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
string
array_type_def::get_pretty_representation(bool internal,
					  bool /*qualified_name*/) const
{return get_type_representation(*this, internal);}

/// Compares two instances of @ref array_type_def.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const array_type_def& l, const array_type_def& r, change_kind* k)
{
  std::vector<array_type_def::subrange_sptr > this_subs = l.get_subranges();
  std::vector<array_type_def::subrange_sptr > other_subs = r.get_subranges();

  bool result = true;
  if (this_subs.size() != other_subs.size())
    {
      result = false;
      if (k)
	*k |= LOCAL_TYPE_CHANGE_KIND;
      else
	return false;
    }

  std::vector<array_type_def::subrange_sptr >::const_iterator i,j;
  for (i = this_subs.begin(), j = other_subs.begin();
       i != this_subs.end() && j != other_subs.end();
       ++i, ++j)
    if (**i != **j)
      {
	result = false;
	if (k)
	  {
	    *k |= LOCAL_TYPE_CHANGE_KIND;
	    break;
	  }
	else
	  return false;
      }

  if (l.get_element_type() != r.get_element_type())
    {
      result = false;
      if (k)
	*k |= SUBTYPE_CHANGE_KIND;
      else
	return false;
    }

  return result;
}

/// Test if two variables are equals modulo CV qualifiers.
///
/// @param l the first array of the comparison.
///
/// @param r the second array of the comparison.
///
/// @return true iff @p l equals @p r or, if they are different, the
/// difference between the too is just a matter of CV qualifiers.
bool
equals_modulo_cv_qualifier(const array_type_def* l, const array_type_def* r)
{
  if (l == r)
    return true;

  if (!l || !r)
    return false;

  l = is_array_type(peel_qualified_or_typedef_type(l));
  r = is_array_type(peel_qualified_or_typedef_type(r));

  std::vector<array_type_def::subrange_sptr > this_subs = l->get_subranges();
  std::vector<array_type_def::subrange_sptr > other_subs = r->get_subranges();

  if (this_subs.size() != other_subs.size())
    return false;

  std::vector<array_type_def::subrange_sptr >::const_iterator i,j;
  for (i = this_subs.begin(), j = other_subs.begin();
       i != this_subs.end() && j != other_subs.end();
       ++i, ++j)
    if (**i != **j)
      return false;

  type_base *first_element_type =
    peel_qualified_or_typedef_type(l->get_element_type().get());
  type_base *second_element_type =
    peel_qualified_or_typedef_type(r->get_element_type().get());

  if (*first_element_type != *second_element_type)
    return false;

  return true;
}

/// Get the language of the array.
///
/// @return the language of the array.
translation_unit::language
array_type_def::get_language() const
{
  const std::vector<subrange_sptr>& subranges =
    get_subranges();

  if (subranges.empty())
    return translation_unit::LANG_C11;
  return subranges.front()->get_language();
}

bool
array_type_def::operator==(const decl_base& o) const
{
  const array_type_def* other =
    dynamic_cast<const array_type_def*>(&o);
  if (!other)
    return false;
  return try_canonical_compare(this, other);
}

bool
array_type_def::operator==(const type_base& o) const
{
  const decl_base* other = dynamic_cast<const decl_base*>(&o);
  if (!other)
    return false;
  return *this == *other;
}

/// Getter of the type of an array element.
///
/// @return the type of an array element.
const type_base_sptr
array_type_def::get_element_type() const
{return priv_->element_type_.lock();}

/// Setter of the type of array element.
///
/// Beware that after using this function, one might want to
/// re-compute the canonical type of the array, if one has already
/// been computed.
///
/// The intended use of this method is to permit in-place adjustment
/// of the element type's qualifiers. In particular, the size of the
/// element type should not be changed.
///
/// @param element_type the new element type to set.
void
array_type_def::set_element_type(const type_base_sptr& element_type)
{
  priv_->element_type_ = element_type;
}

/// Append subranges from the vector @param subs to the current
/// vector of subranges.
void
array_type_def::append_subranges(const std::vector<subrange_sptr>& subs)
{
  size_t s = get_element_type()->get_size_in_bits();

  for (std::vector<shared_ptr<subrange_type> >::const_iterator i = subs.begin();
       i != subs.end();
       ++i)
    {
      priv_->subranges_.push_back(*i);
      s *= (*i)->get_length();
    }

  const environment* env = get_environment();
  ABG_ASSERT(env);
  set_name(env->intern(get_pretty_representation()));
  set_size_in_bits(s);
}

/// @return true if one of the sub-ranges of the array is infinite, or
/// if the array has no sub-range at all, also meaning that the size
/// of the array is infinite.
bool
array_type_def::is_infinite() const
{
  if (priv_->subranges_.empty())
    return true;

  for (std::vector<shared_ptr<subrange_type> >::const_iterator i =
	 priv_->subranges_.begin();
       i != priv_->subranges_.end();
       ++i)
    if ((*i)->is_infinite())
      return true;

  return false;
}

int
array_type_def::get_dimension_count() const
{return priv_->subranges_.size();}

/// Build and return the qualified name of the current instance of the
/// @ref array_type_def.
///
/// @param qn output parameter.  Is set to the newly-built qualified
/// name of the current instance of @ref array_type_def.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
void
array_type_def::get_qualified_name(interned_string& qn, bool internal) const
{qn = get_qualified_name(internal);}

/// Compute the qualified name of the array.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the resulting qualified name.
const interned_string&
array_type_def::get_qualified_name(bool internal) const
{
  const environment* env = get_environment();
  ABG_ASSERT(env);

  if (internal)
    {
      if (get_canonical_type())
	{
	  if (priv_->internal_qualified_name_.empty())
	    priv_->internal_qualified_name_ =
	      env->intern(get_type_representation(*this, /*internal=*/true));
	  return priv_->internal_qualified_name_;
	}
      else
	{
	  priv_->temp_internal_qualified_name_ =
	    env->intern(get_type_representation(*this, /*internal=*/true));
	  return priv_->temp_internal_qualified_name_;
	}
    }
  else
    {
      if (get_canonical_type())
	{
	  if (decl_base::peek_qualified_name().empty())
	    set_qualified_name(env->intern(get_type_representation
					   (*this, /*internal=*/false)));
	  return decl_base::peek_qualified_name();
	}
      else
	{
	  set_qualified_name(env->intern(get_type_representation
					 (*this, /*internal=*/false)));
	  return decl_base::peek_qualified_name();
	}
    }
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
array_type_def::traverse(ir_node_visitor& v)
{
  if (v.type_node_has_been_visited(this))
    return true;

  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (type_base_sptr t = get_element_type())
	t->traverse(v);
      visiting(false);
    }

  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);
  return result;
}

const location&
array_type_def::get_location() const
{return decl_base::get_location();}

/// Get the array's subranges
const std::vector<array_type_def::subrange_sptr>&
array_type_def::get_subranges() const
{return priv_->subranges_;}

array_type_def::~array_type_def()
{}

// </array_type_def definitions>

// <enum_type_decl definitions>

class enum_type_decl::priv
{
  type_base_sptr	underlying_type_;
  enumerators		enumerators_;

  friend class enum_type_decl;

  priv();

public:
  priv(type_base_sptr underlying_type,
       enumerators& enumerators)
    : underlying_type_(underlying_type),
      enumerators_(enumerators)
  {}
}; // end class enum_type_decl::priv

/// Constructor.
///
/// @param name the name of the type declaration.
///
/// @param locus the source location where the type was defined.
///
/// @param underlying_type the underlying type of the enum.
///
/// @param enums the enumerators of this enum type.
///
/// @param linkage_name the linkage name of the enum.
///
/// @param vis the visibility of the enum type.
enum_type_decl::enum_type_decl(const string&	name,
			       const location&	locus,
			       type_base_sptr	underlying_type,
			       enumerators&	enums,
			       const string&	linkage_name,
			       visibility	vis)
  : type_or_decl_base(underlying_type->get_environment(),
		      ENUM_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE),
    type_base(underlying_type->get_environment(),
	      underlying_type->get_size_in_bits(),
	      underlying_type->get_alignment_in_bits()),
    decl_base(underlying_type->get_environment(),
	      name, locus, linkage_name, vis),
    priv_(new priv(underlying_type, enums))
{
  runtime_type_instance(this);
  for (enumerators::iterator e = get_enumerators().begin();
       e != get_enumerators().end();
       ++e)
    e->set_enum_type(this);
}

/// Return the underlying type of the enum.
type_base_sptr
enum_type_decl::get_underlying_type() const
{return priv_->underlying_type_;}

/// @return the list of enumerators of the enum.
const enum_type_decl::enumerators&
enum_type_decl::get_enumerators() const
{return priv_->enumerators_;}

/// @return the list of enumerators of the enum.
enum_type_decl::enumerators&
enum_type_decl::get_enumerators()
{return priv_->enumerators_;}

/// Get the pretty representation of the current instance of @ref
/// enum_type_decl.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @param qualified_name if true, names emitted in the pretty
/// representation are fully qualified.
///
/// @return the pretty representation of the enum type.
string
enum_type_decl::get_pretty_representation(bool internal,
					  bool qualified_name) const
{
  string r = "enum " + decl_base::get_pretty_representation(internal,
							    qualified_name);
  return r;
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
enum_type_decl::traverse(ir_node_visitor &v)
{
  if (v.type_node_has_been_visited(this))
    return true;

  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (type_base_sptr t = get_underlying_type())
	t->traverse(v);
      visiting(false);
    }

  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);
  return result;
}

/// Destructor for the enum type declaration.
enum_type_decl::~enum_type_decl()
{}

/// Test if two enums differ, but not by a name change.
///
/// @param l the first enum to consider.
///
/// @param r the second enum to consider.
///
/// @return true iff @p l differs from @p r by anything but a name
/// change.
bool
enum_has_non_name_change(const enum_type_decl& l,
			 const enum_type_decl& r,
			 change_kind* k)
{
  bool result = false;
  if (*l.get_underlying_type() != *r.get_underlying_type())
    {
      result = true;
      if (k)
	*k |= SUBTYPE_CHANGE_KIND;
      else
	return true;
    }

  enum_type_decl::enumerators::const_iterator i, j;
  for (i = l.get_enumerators().begin(), j = r.get_enumerators().begin();
       i != l.get_enumerators().end() && j != r.get_enumerators().end();
       ++i, ++j)
    if (*i != *j)
      {
	result = true;
	if (k)
	  {
	    *k |= LOCAL_TYPE_CHANGE_KIND;
	    break;
	  }
	else
	  return true;
      }

  if (i != l.get_enumerators().end() || j != r.get_enumerators().end())
    {
      result = true;
      if (k)
	*k |= LOCAL_TYPE_CHANGE_KIND;
      else
	return true;
    }

  enum_type_decl &local_r = const_cast<enum_type_decl&>(r);
  interned_string qn_r = l.get_environment()->intern(r.get_qualified_name());
  interned_string qn_l = l.get_environment()->intern(l.get_qualified_name());
  string n_l = l.get_name();
  string n_r = r.get_name();
  local_r.set_qualified_name(qn_l);
  local_r.set_name(n_l);

  if (!(l.decl_base::operator==(r) && l.type_base::operator==(r)))
    {
      result = true;
      if (k)
	{
	  if (!l.decl_base::operator==(r))
	    *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	  if (!l.type_base::operator==(r))
	    *k |= LOCAL_TYPE_CHANGE_KIND;
	}
      else
	{
	  local_r.set_name(n_r);
	  local_r.set_qualified_name(qn_r);
	  return true;
	}
    }
  local_r.set_qualified_name(qn_r);
  local_r.set_name(n_r);

  return result;
}

/// Compares two instances of @ref enum_type_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const enum_type_decl& l, const enum_type_decl& r, change_kind* k)
{
  bool result = true;
  if (*l.get_underlying_type() != *r.get_underlying_type())
    {
      result = false;
      if (k)
	*k |= SUBTYPE_CHANGE_KIND;
      else
	return false;
    }

  enum_type_decl::enumerators::const_iterator i, j;
  for (i = l.get_enumerators().begin(), j = r.get_enumerators().begin();
       i != l.get_enumerators().end() && j != r.get_enumerators().end();
       ++i, ++j)
    if (*i != *j)
      {
	result = false;
	if (k)
	  {
	    *k |= LOCAL_TYPE_CHANGE_KIND;
	    break;
	  }
	else
	  return false;
      }

  if (i != l.get_enumerators().end() || j != r.get_enumerators().end())
    {
      result = false;
      if (k)
	*k |= LOCAL_TYPE_CHANGE_KIND;
      else
	return false;
    }

  if (!(l.decl_base::operator==(r) && l.type_base::operator==(r)))
    {
      result = false;
      if (k)
	{
	  if (!l.decl_base::operator==(r))
	    *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	  if (!l.type_base::operator==(r))
	    *k |= LOCAL_TYPE_CHANGE_KIND;
	}
      else
	return false;
    }

  return result;
}

/// Equality operator.
///
/// @param o the other enum to test against.
///
/// @return true iff @p o equals the current instance of enum type
/// decl.
bool
enum_type_decl::operator==(const decl_base& o) const
{
  const enum_type_decl* op = dynamic_cast<const enum_type_decl*>(&o);
  if (!op)
    return false;
  return try_canonical_compare(this, op);
}

/// Equality operator.
///
/// @param o the other enum to test against.
///
/// @return true iff @p o is equals the current instance of enum type
/// decl.
bool
enum_type_decl::operator==(const type_base& o) const
{
  const decl_base* other = dynamic_cast<const decl_base*>(&o);
  if (!other)
    return false;
  return *this == *other;
}

/// Equality operator for @ref enum_type_decl_sptr.
///
/// @param l the first operand to compare.
///
/// @param r the second operand to compare.
///
/// @return true iff @p l equals @p r.
bool
operator==(const enum_type_decl_sptr& l, const enum_type_decl_sptr& r)
{
  if (!!l != !!r)
    return false;
  if (l.get() == r.get())
    return true;
  decl_base_sptr o = r;
  return *l == *o;
}

/// Inequality operator for @ref enum_type_decl_sptr.
///
/// @param l the first operand to compare.
///
/// @param r the second operand to compare.
///
/// @return true iff @p l equals @p r.
bool
operator!=(const enum_type_decl_sptr& l, const enum_type_decl_sptr& r)
{return !operator==(l, r);}

/// The type of the private data of an @ref
/// enum_type_decl::enumerator.
class enum_type_decl::enumerator::priv
{
  const environment*	env_;
  interned_string	name_;
  int64_t		value_;
  interned_string	qualified_name_;
  enum_type_decl*	enum_type_;

  friend class	enum_type_decl::enumerator;

public:

  priv()
    : env_(),
      enum_type_()
  {}

  priv(const environment* env,
       const string& name,
       int64_t value,
       enum_type_decl* e = 0)
    : env_(env),
      name_(env ? env->intern(name) : interned_string()),
      value_(value),
      enum_type_(e)
  {}
}; // end class enum_type_def::enumerator::priv

/// Default constructor of the @ref enum_type_decl::enumerator type.
enum_type_decl::enumerator::enumerator()
  : priv_(new priv)
{}

/// Constructor of the @ref enum_type_decl::enumerator type.
///
/// @param env the environment we are operating from.
///
/// @param name the name of the enumerator.
///
/// @param value the value of the enumerator.
enum_type_decl::enumerator::enumerator(const environment* env,
				       const string& name,
				       int64_t value)
  : priv_(new priv(env, name, value))
{}

/// Copy constructor of the @ref enum_type_decl::enumerator type.
///
/// @param other enumerator to copy.
enum_type_decl::enumerator::enumerator(const enumerator& other)
  : priv_(new priv(other.get_environment(),
		   other.get_name(),
		   other.get_value(),
		   other.get_enum_type()))
{}

/// Assignment operator of the @ref enum_type_decl::enumerator type.
///
/// @param o
enum_type_decl::enumerator&
enum_type_decl::enumerator::operator=(const enumerator& o)
{
  priv_->env_ = o.get_environment();
  priv_->name_ = o.get_name();
  priv_->value_ = o.get_value();
  priv_->enum_type_ = o.get_enum_type();
  return *this;
}
/// Equality operator
///
/// @param other the enumerator to compare to the current instance of
/// enum_type_decl::enumerator.
///
/// @return true if @p other equals the current instance of
/// enum_type_decl::enumerator.
bool
enum_type_decl::enumerator::operator==(const enumerator& other) const
{return (get_name() == other.get_name()
	 && get_value() == other.get_value());}

/// Inequality operator.
///
/// @param other the other instance to compare against.
///
/// @return true iff @p other is different from the current instance.
bool
enum_type_decl::enumerator::operator!=(const enumerator& other) const
{return !operator==(other);}

/// Getter of the environment of this enumerator.
///
/// @return the environment of this enumerator.
const environment*
enum_type_decl::enumerator::get_environment() const
{return priv_->env_;}

/// Getter for the name of the current instance of
/// enum_type_decl::enumerator.
///
/// @return a reference to the name of the current instance of
/// enum_type_decl::enumerator.
const interned_string&
enum_type_decl::enumerator::get_name() const
{return priv_->name_;}

/// Getter for the qualified name of the current instance of
/// enum_type_decl::enumerator.  The first invocation of the method
/// builds the qualified name, caches it and return a reference to the
/// cached qualified name.  Subsequent invocations just return the
/// cached value.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the qualified name of the current instance of
/// enum_type_decl::enumerator.
const interned_string&
enum_type_decl::enumerator::get_qualified_name(bool internal) const
{
  if (priv_->qualified_name_.empty())
    {
      const environment* env = priv_->enum_type_->get_environment();
      ABG_ASSERT(env);
      priv_->qualified_name_ =
	env->intern(get_enum_type()->get_qualified_name(internal)
		    + "::"
		    + get_name());
    }
  return priv_->qualified_name_;
}

/// Setter for the name of @ref enum_type_decl::enumerator.
///
/// @param n the new name.
void
enum_type_decl::enumerator::set_name(const string& n)
{
  const environment* env = get_environment();
  ABG_ASSERT(env);
  priv_->name_ = env->intern(n);
}

/// Getter for the value of @ref enum_type_decl::enumerator.
///
/// @return the value of the current instance of
/// enum_type_decl::enumerator.
int64_t
enum_type_decl::enumerator::get_value() const
{return priv_->value_;}

/// Setter for the value of @ref enum_type_decl::enumerator.
///
/// @param v the new value of the enum_type_decl::enumerator.
void
enum_type_decl::enumerator::set_value(int64_t v)
{priv_->value_= v;}

/// Getter for the enum type that this enumerator is for.
///
/// @return the enum type that this enumerator is for.
enum_type_decl*
enum_type_decl::enumerator::get_enum_type() const
{return priv_->enum_type_;}

/// Setter for the enum type that this enumerator is for.
///
/// @param e the new enum type.
void
enum_type_decl::enumerator::set_enum_type(enum_type_decl* e)
{priv_->enum_type_ = e;}
// </enum_type_decl definitions>

// <typedef_decl definitions>

/// Private data structure of the @ref typedef_decl.
struct typedef_decl::priv
{
  type_base_wptr	underlying_type_;
  string		internal_qualified_name_;
  string		temp_internal_qualified_name_;

  priv(const type_base_sptr& t)
    : underlying_type_(t)
  {}
}; // end struct typedef_decl::priv

/// Constructor of the typedef_decl type.
///
/// @param name the name of the typedef.
///
/// @param underlying_type the underlying type of the typedef.
///
/// @param locus the source location of the typedef declaration.
///
/// @param linkage_name the mangled name of the typedef.
///
/// @param vis the visibility of the typedef type.
typedef_decl::typedef_decl(const string&		name,
			   const type_base_sptr	underlying_type,
			   const location&		locus,
			   const string&		linkage_name,
			   visibility vis)
  : type_or_decl_base(underlying_type->get_environment(),
		      TYPEDEF_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE),
    type_base(underlying_type->get_environment(),
	      underlying_type->get_size_in_bits(),
	      underlying_type->get_alignment_in_bits()),
    decl_base(underlying_type->get_environment(),
	      name, locus, linkage_name, vis),
    priv_(new priv(underlying_type))
{
  runtime_type_instance(this);
}

/// Return the size of the typedef.
///
/// This function looks at the size of the underlying type and ensures
/// that it's the same as the size of the typedef.
///
/// @return the size of the typedef.
size_t
typedef_decl::get_size_in_bits() const
{
  size_t s = get_underlying_type()->get_size_in_bits();
  if (s != type_base::get_size_in_bits())
    const_cast<typedef_decl*>(this)->set_size_in_bits(s);
  return type_base::get_size_in_bits();
}

/// Return the alignment of the typedef.
///
/// This function looks at the alignment of the underlying type and
/// ensures that it's the same as the alignment of the typedef.
///
/// @return the size of the typedef.
size_t
typedef_decl::get_alignment_in_bits() const
{
    size_t s = get_underlying_type()->get_alignment_in_bits();
  if (s != type_base::get_alignment_in_bits())
    const_cast<typedef_decl*>(this)->set_alignment_in_bits(s);
  return type_base::get_alignment_in_bits();
}

/// Compares two instances of @ref typedef_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const typedef_decl& l, const typedef_decl& r, change_kind* k)
{
  bool result = true;
  if (!l.decl_base::operator==(r))
    {
      result = false;
      if (k)
	*k |= LOCAL_NON_TYPE_CHANGE_KIND;
      else
	return false;
    }

  if (*l.get_underlying_type() != *r.get_underlying_type())
    {
      // Changes to the underlying type of a typedef are considered
      // local, a bit like for pointers.
      result = false;
      if (k)
	*k |= LOCAL_TYPE_CHANGE_KIND;
      else
	return false;
    }

  return result;
}

/// Equality operator
///
/// @param o the other typedef_decl to test against.
bool
typedef_decl::operator==(const decl_base& o) const
{
  const typedef_decl* other = dynamic_cast<const typedef_decl*>(&o);
  if (!other)
    return false;
  return try_canonical_compare(this, other);
}

/// Equality operator
///
/// @param o the other typedef_decl to test against.
///
/// @return true if the current instance of @ref typedef_decl equals
/// @p o.
bool
typedef_decl::operator==(const type_base& o) const
{
  const decl_base* other = dynamic_cast<const decl_base*>(&o);
  if (!other)
    return false;
  return *this == *other;
}

/// Build a pretty representation for a typedef_decl.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @param qualified_name if true, names emitted in the pretty
/// representation are fully qualified.
///
/// @return a copy of the pretty representation of the current
/// instance of typedef_decl.
string
typedef_decl::get_pretty_representation(bool internal,
					bool qualified_name) const
{

  string result = "typedef ";
  if (qualified_name)
    result += get_qualified_name(internal);
  else
    result += get_name();

  return result;
}

/// Getter of the underlying type of the typedef.
///
/// @return the underlying_type.
type_base_sptr
typedef_decl::get_underlying_type() const
{return priv_->underlying_type_.lock();}

/// Setter ofthe underlying type of the typedef.
///
/// @param t the new underlying type of the typedef.
void
typedef_decl::set_underlying_type(const type_base_sptr& t)
{priv_->underlying_type_ = t;}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
typedef_decl::traverse(ir_node_visitor& v)
{
  if (v.type_node_has_been_visited(this))
    return true;

  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (type_base_sptr t = get_underlying_type())
	t->traverse(v);
      visiting(false);
    }

  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);
  return result;
}

typedef_decl::~typedef_decl()
{}
// </typedef_decl definitions>

// <var_decl definitions>

struct var_decl::priv
{
  type_base_wptr	type_;
  type_base*		naked_type_;
  decl_base::binding	binding_;
  elf_symbol_sptr	symbol_;
  interned_string	id_;

  priv()
    : naked_type_(),
    binding_(decl_base::BINDING_GLOBAL)
  {}

  priv(type_base_sptr t,
       decl_base::binding b)
    : type_(t),
      naked_type_(t.get()),
      binding_(b)
  {}
}; // end struct var_decl::priv

/// Constructor
///
/// @param name the name of the variable declaration
///
/// @param name the type of the variable declaration
///
/// @param locus the source location where the variable was defined.
///
/// @param linkage_name the linkage name of the variable.
///
/// @param vis the visibility of of the variable.
///
/// @param bind the binding kind of the variable.
var_decl::var_decl(const string&	name,
		   type_base_sptr	type,
		   const location&	locus,
		   const string&	linkage_name,
		   visibility		vis,
		   binding		bind)
  : type_or_decl_base(type->get_environment(),
		      VAR_DECL | ABSTRACT_DECL_BASE),
    decl_base(type->get_environment(), name, locus, linkage_name, vis),
    priv_(new priv(type, bind))
{
  runtime_type_instance(this);
}

/// Getter of the type of the variable.
///
/// @return the type of the variable.
const type_base_sptr
var_decl::get_type() const
{return priv_->type_.lock();}

/// Getter of the type of the variable.
///
/// This getter returns a bare pointer, as opposed to a smart pointer.
/// It's to be used on performance sensitive code paths identified by
/// careful profiling.
///
/// @return the type of the variable, as a bare pointer.
const type_base*
var_decl::get_naked_type() const
{return priv_->naked_type_;}

/// Getter of the binding of the variable.
///
/// @return the biding of the variable.
decl_base::binding
var_decl::get_binding() const
{return priv_->binding_;}

/// Setter of the binding of the variable.
///
/// @param b the new binding value.
void
var_decl::set_binding(decl_base::binding b)
{priv_->binding_ = b;}

/// Sets the underlying ELF symbol for the current variable.
///
/// And underlyin$g ELF symbol for the current variable might exist
/// only if the corpus that this variable originates from was
/// constructed from an ELF binary file.
///
/// Note that comparing two variables that have underlying ELF symbols
/// involves comparing their underlying elf symbols.  The decl name
/// for the variable thus becomes irrelevant in the comparison.
///
/// @param sym the new ELF symbol for this variable decl.
void
var_decl::set_symbol(const elf_symbol_sptr& sym)
{
  priv_->symbol_ = sym;
  // The variable id cache that depends on the symbol must be
  // invalidated because the symbol changed.
  priv_->id_ = get_environment()->intern("");
}

/// Gets the the underlying ELF symbol for the current variable,
/// that was set using var_decl::set_symbol().  Please read the
/// documentation for that member function for more information about
/// "underlying ELF symbols".
///
/// @return sym the underlying ELF symbol for this variable decl, if
/// one exists.
const elf_symbol_sptr&
var_decl::get_symbol() const
{return priv_->symbol_;}

/// Create a new var_decl that is a clone of the current one.
///
/// @return the cloned var_decl.
var_decl_sptr
var_decl::clone() const
{
  var_decl_sptr v(new var_decl(get_name(),
			       get_type(),
			       get_location(),
			       get_linkage_name(),
			       get_visibility(),
			       get_binding()));

  v->set_symbol(get_symbol());

  if (is_member_decl(*this))
    {
      class_decl* scope = dynamic_cast<class_decl*>(get_scope());
      scope->add_data_member(v, get_member_access_specifier(*this),
			     get_data_member_is_laid_out(*this),
			     get_member_is_static(*this),
			     get_data_member_offset(*this));
    }
  else
    add_decl_to_scope(v, get_scope());

  return v;
}
/// Setter of the scope of the current var_decl.
///
/// Note that the decl won't hold a reference on the scope.  It's
/// rather the scope that holds a reference on its members.
///
/// @param scope the new scope.
void
var_decl::set_scope(scope_decl* scope)
{
  if (!get_context_rel())
    set_context_rel(new dm_context_rel(scope));
  else
    get_context_rel()->set_scope(scope);
}

/// Compares two instances of @ref var_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const var_decl& l, const var_decl& r, change_kind* k)
{
  bool result = true;

  // First test types of variables.  This should be fast because in
  // the general case, most types should be canonicalized.
  if (*l.get_naked_type() != *r.get_naked_type())
    {
      result = false;
      if (k)
	{
	  if (!types_have_similar_structure(l.get_naked_type(),
					   r.get_naked_type()))
	    *k |= (LOCAL_TYPE_CHANGE_KIND);
	  else
	    *k |= SUBTYPE_CHANGE_KIND;
	}
      else
	return false;
    }

  // If there are underlying elf symbols for these variables,
  // compare them.  And then compare the other parts.
  const elf_symbol_sptr &s0 = l.get_symbol(), &s1 = r.get_symbol();
  if (!!s0 != !!s1)
    {
      result = false;
      if (k)
	*k |= LOCAL_NON_TYPE_CHANGE_KIND;
      else
	return false;
    }
  else if (s0 && s0 != s1)
    {
      result = false;
      if (k)
	*k |= LOCAL_NON_TYPE_CHANGE_KIND;
      else
	return false;
    }
  bool symbols_are_equal = (s0 && s1 && result);

  if (symbols_are_equal)
    {
      // The variables have underlying elf symbols that are equal, so
      // now, let's compare the decl_base part of the variables w/o
      // considering their decl names.
      const interned_string &n1 = l.get_name(), &n2 = r.get_name();
      const_cast<var_decl&>(l).set_name("");
      const_cast<var_decl&>(r).set_name("");
      bool decl_bases_different = !l.decl_base::operator==(r);
      const_cast<var_decl&>(l).set_name(n1);
      const_cast<var_decl&>(r).set_name(n2);

      if (decl_bases_different)
	{
	  result = false;
	  if (k)
	    *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	  else
	    return false;
	}
    }
  else
    if (!l.decl_base::operator==(r))
      {
	result = false;
	if (k)
	  *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	else
	  return false;
      }

  const dm_context_rel* c0 =
    dynamic_cast<const dm_context_rel*>(l.get_context_rel());
  const dm_context_rel* c1 =
    dynamic_cast<const dm_context_rel*>(r.get_context_rel());
  ABG_ASSERT(c0 && c1);

  if (*c0 != *c1)
    {
      result = false;
      if (k)
	*k |= LOCAL_NON_TYPE_CHANGE_KIND;
      else
	return false;
    }

  return result;
}

/// Comparison operator of @ref var_decl.
///
/// @param o the instance of @ref var_decl to compare against.
///
/// @return true iff the current instance of @ref var_decl equals @p o.
bool
var_decl::operator==(const decl_base& o) const
{
  const var_decl* other = dynamic_cast<const var_decl*>(&o);
  if (!other)
    return false;

  return equals(*this, *other, 0);
}

/// Return an ID that tries to uniquely identify the variable inside a
/// program or a library.
///
/// So if the variable has an underlying elf symbol, the ID is the
/// concatenation of the symbol name and its version.  Otherwise, the
/// ID is the linkage name if its non-null.  Otherwise, it's the
/// pretty representation of the variable.
///
/// @return the ID.
interned_string
var_decl::get_id() const
{
  if (priv_->id_.empty())
    {
      string repr = get_name();
      string sym_str;
      if (elf_symbol_sptr s = get_symbol())
	sym_str = s->get_id_string();
      else if (!get_linkage_name().empty())
	sym_str = get_linkage_name();
      const environment* env = get_type()->get_environment();
      ABG_ASSERT(env);
      priv_->id_ = env->intern(repr);
      if (!sym_str.empty())
	priv_->id_ = env->intern(priv_->id_ + "{" + sym_str + "}");
    }
  return priv_->id_;
}

/// Return the hash value for the current instance.
///
/// @return the hash value.
size_t
var_decl::get_hash() const
{
  var_decl::hash hash_var;
  return hash_var(this);
}

/// Get the qualified name of a given variable or data member.
///
///
/// Note that if the current instance of @ref var_decl is an anonymous
/// data member, then the qualified name is actually the flat
/// representation (the definition) of the type of the anonymous data
/// member.  We chose the flat representation because otherwise, the
/// name of an *anonymous* data member is empty, by construction, e.g:
///
///   struct foo {
///     int a;
///     union {
///       char b;
///       char c;
///     }; // <---- this data member is anonymous.
///     int d;
///   }
///
///   The string returned for the anonymous member here is going to be:
///
///     "union {char b; char c}"
///
/// @param internal if true then this is for a purpose to the library,
/// otherwise, it's for being displayed to users.
///
/// @return the resulting qualified name.
const interned_string&
var_decl::get_qualified_name(bool internal) const
{
  if (is_anonymous_data_member(this)
      && decl_base::get_qualified_name().empty())
    {
      // Display the anonymous data member in a way that makes sense.
      string r = get_pretty_representation(internal);
      set_qualified_name(get_environment()->intern(r));
    }

    return decl_base::get_qualified_name(internal);
}

/// Build and return the pretty representation of this variable.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @param qualified_name if true, names emitted in the pretty
/// representation are fully qualified.
///
/// @return a copy of the pretty representation of this variable.
string
var_decl::get_pretty_representation(bool internal, bool qualified_name) const
{
  string result;

  if (is_member_decl(this) && get_member_is_static(this))
    result = "static ";

  // Detect if the current instance of var_decl is a member of
  // an anonymous class or union.
  bool member_of_anonymous_class = false;
  if (class_or_union* scope = is_at_class_scope(this))
    if (scope->get_is_anonymous())
      member_of_anonymous_class = true;

  if (array_type_def_sptr t = is_array_type(get_type()))
    {
      string name;
      if (member_of_anonymous_class || !qualified_name)
	name = get_name();
      else
	name = get_qualified_name(internal);

      result +=
	get_type_declaration(t->get_element_type())->get_qualified_name(internal)
	+ " " + name + t->get_subrange_representation();
    }
  else
    {
      if (/*The current var_decl is to be used as an anonymous data
	    member.  */
	  get_name().empty())
	{
	  // Display the anonymous data member in a way that
	  // makes sense.
	  result +=
	    get_class_or_union_flat_representation
	    (is_class_or_union_type(get_type()),
	     "", /*one_line=*/true, internal);
	}
      else if (data_member_has_anonymous_type(this))
	{
	  result += get_class_or_union_flat_representation
	    (is_class_or_union_type(get_type()),
	     "", /*one_line=*/true, internal);
	  result += " ";
	  if (member_of_anonymous_class || !qualified_name)
	    // It doesn't make sense to name the member of an
	    // anonymous class or union like:
	    // "__anonymous__::data_member_name".  So let's just use
	    // its non-qualified name.
	    result += get_name();
	  else
	    result += get_qualified_name(internal);
	}
      else
	{
	  result +=
	    get_type_declaration(get_type())->get_qualified_name(internal)
	    + " ";

	  if (member_of_anonymous_class || !qualified_name)
	    // It doesn't make sense to name the member of an
	    // anonymous class or union like:
	    // "__anonymous__::data_member_name".  So let's just use
	    // its non-qualified name.
	    result += get_name();
	  else
	    result += get_qualified_name(internal);
	}
    }
  return result;
}

/// Get a name that is valid even for an anonymous data member.
///
/// If the current @ref var_decl is an anonymous data member, then
/// return its pretty representation. As of now, that pretty
/// representation is actually its flat representation as returned by
/// get_class_or_union_flat_representation().
///
/// Otherwise, just return the name of the current @ref var_decl.
///
/// @param qualified if true, return the qualified name.  This doesn't
/// have an effet if the current @ref var_decl represents an anonymous
/// data member.
string
var_decl::get_anon_dm_reliable_name(bool qualified) const
{
  string name;
  if (is_anonymous_data_member(this))
    name = get_pretty_representation(true, qualified);
  else
    name = get_name();

  return name;
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
var_decl::traverse(ir_node_visitor& v)
{
  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (type_base_sptr t = get_type())
	t->traverse(v);
      visiting(false);
    }
  return v.visit_end(this);
}

var_decl::~var_decl()
{}

// </var_decl definitions>

// <function_type>

/// The type of the private data of the @ref function_type type.
struct function_type::priv
{
  parameters parms_;
  type_base_wptr return_type_;
  interned_string cached_name_;
  interned_string internal_cached_name_;

  priv()
  {}

  priv(const parameters&	parms,
       type_base_sptr		return_type)
    : parms_(parms),
      return_type_(return_type)
  {}

  priv(type_base_sptr return_type)
    : return_type_(return_type)
  {}

  /// Mark a given @ref function_type as being compared.
  ///
  /// @param type the @ref function_type to mark as being compared.
  void
  mark_as_being_compared(const function_type& type) const
  {
    const environment* env = type.get_environment();
    ABG_ASSERT(env);
    env->priv_->fn_types_being_compared_.insert(&type);
  }

  /// If a given @ref function_type was marked as being compared, this
  /// function unmarks it.
  ///
  /// @param type the @ref function_type to mark as *NOT* being
  /// compared.
  void
  unmark_as_being_compared(const function_type& type) const
  {
    const environment* env = type.get_environment();
    ABG_ASSERT(env);
    env->priv_->fn_types_being_compared_.erase(&type);
  }

  /// Tests if a @ref function_type is currently being compared.
  ///
  /// @param type the function type to take into account.
  ///
  /// @return true if @p type is being compared.
  bool
  comparison_started(const function_type& type) const
  {
    const environment* env = type.get_environment();
    ABG_ASSERT(env);
    return env->priv_->fn_types_being_compared_.count(&type);
  }
};// end struc function_type::priv

/// This function is automatically invoked whenever an instance of
/// this type is canonicalized.
///
/// It's an overload of the virtual type_base::on_canonical_type_set.
///
/// We put here what is thus meant to be executed only at the point of
/// type canonicalization.
void
function_type::on_canonical_type_set()
{
  priv_->cached_name_.clear();
  priv_->internal_cached_name_.clear();
}

/// The most straightforward constructor for the function_type class.
///
/// @param return_type the return type of the function type.
///
/// @param parms the list of parameters of the function type.
/// Stricto sensu, we just need a list of types; we are using a list
/// of parameters (where each parameter also carries the name of the
/// parameter and its source location) to try and provide better
/// diagnostics whenever it makes sense.  If it appears that this
/// wasts too many resources, we can fall back to taking just a
/// vector of types here.
///
/// @param size_in_bits the size of this type, in bits.
///
/// @param alignment_in_bits the alignment of this type, in bits.
///
/// @param size_in_bits the size of this type.
function_type::function_type(type_base_sptr	return_type,
			     const parameters&	parms,
			     size_t		size_in_bits,
			     size_t		alignment_in_bits)
  : type_or_decl_base(return_type->get_environment(),
		      FUNCTION_TYPE | ABSTRACT_TYPE_BASE),
    type_base(return_type->get_environment(), size_in_bits, alignment_in_bits),
    priv_(new priv(parms, return_type))
{
  runtime_type_instance(this);

  for (parameters::size_type i = 0, j = 1;
       i < priv_->parms_.size();
       ++i, ++j)
    {
      if (i == 0 && priv_->parms_[i]->get_is_artificial())
	// If the first parameter is artificial, then it certainly
	// means that this is a member function, and the first
	// parameter is the implicit this pointer.  In that case, set
	// the index of that implicit parameter to zero.  Otherwise,
	// the index of the first parameter starts at one.
	j = 0;
      priv_->parms_[i]->set_index(j);
    }
}

/// A constructor for a function_type that takes no parameters.
///
/// @param return_type the return type of this function_type.
///
/// @param size_in_bits the size of this type, in bits.
///
/// @param alignment_in_bits the alignment of this type, in bits.
function_type::function_type(type_base_sptr return_type,
			     size_t size_in_bits, size_t alignment_in_bits)
  : type_or_decl_base(return_type->get_environment(),
		      FUNCTION_TYPE | ABSTRACT_TYPE_BASE),
    type_base(return_type->get_environment(), size_in_bits, alignment_in_bits),
    priv_(new priv(return_type))
{
  runtime_type_instance(this);
}

/// A constructor for a function_type that takes no parameter and
/// that has no return_type yet.  These missing parts can (and must)
/// be added later.
///
/// @param env the environment we are operating from.
///
/// @param size_in_bits the size of this type, in bits.
///
/// @param alignment_in_bits the alignment of this type, in bits.
function_type::function_type(const environment* env,
			     size_t		size_in_bits,
			     size_t		alignment_in_bits)
  : type_or_decl_base(env, FUNCTION_TYPE | ABSTRACT_TYPE_BASE),
    type_base(env, size_in_bits, alignment_in_bits),
    priv_(new priv)
{
  runtime_type_instance(this);
}

/// Getter for the return type of the current instance of @ref
/// function_type.
///
/// @return the return type.
type_base_sptr
function_type::get_return_type() const
{return priv_->return_type_.lock();}

/// Setter of the return type of the current instance of @ref
/// function_type.
///
/// @param t the new return type to set.
void
function_type::set_return_type(type_base_sptr t)
{priv_->return_type_ = t;}

/// Getter for the set of parameters of the current intance of @ref
/// function_type.
///
/// @return the parameters of the current instance of @ref
/// function_type.
const function_decl::parameters&
function_type::get_parameters() const
{return priv_->parms_;}

/// Get the Ith parameter of the vector of parameters of the current
/// instance of @ref function_type.
///
/// Note that the first parameter is at index 0.  That parameter is
/// the first parameter that comes after the possible implicit "this"
/// parameter, when the current instance @ref function_type is for a
/// member function.  Otherwise, if the current instance of @ref
/// function_type is for a non-member function, the parameter at index
/// 0 is the first parameter of the function.
///
///
/// @param i the index of the parameter to return.  If i is greater
/// than the index of the last parameter, then this function returns
/// an empty parameter (smart) pointer.
///
/// @return the @p i th parameter that is not implicit.
const function_decl::parameter_sptr
function_type::get_parm_at_index_from_first_non_implicit_parm(size_t i) const
{
  parameter_sptr result;
  if (dynamic_cast<const method_type*>(this))
    {
      if (i + 1 < get_parameters().size())
	result = get_parameters()[i + 1];
    }
  else
    {
      if (i < get_parameters().size())
	result = get_parameters()[i];
    }
  return result;
}

/// Setter for the parameters of the current instance of @ref
/// function_type.
///
/// @param p the new vector of parameters to set.
void
function_type::set_parameters(const parameters &p)
{
  priv_->parms_ = p;
  for (parameters::size_type i = 0, j = 1;
       i < priv_->parms_.size();
       ++i, ++j)
    {
      if (i == 0 && priv_->parms_[i]->get_is_artificial())
	// If the first parameter is artificial, then it certainly
	// means that this is a member function, and the first
	// parameter is the implicit this pointer.  In that case, set
	// the index of that implicit parameter to zero.  Otherwise,
	// the index of the first parameter starts at one.
	j = 0;
      priv_->parms_[i]->set_index(j);
    }
}

/// Append a new parameter to the vector of parameters of the current
/// instance of @ref function_type.
///
/// @param parm the parameter to append.
void
function_type::append_parameter(parameter_sptr parm)
{
  parm->set_index(priv_->parms_.size());
  priv_->parms_.push_back(parm);
}

/// Test if the current instance of @ref function_type is for a
/// variadic function.
///
/// A variadic function is a function that takes a variable number of
/// arguments.
///
/// @return true iff the current instance of @ref function_type is for
/// a variadic function.
bool
function_type::is_variadic() const
{
  return (!priv_->parms_.empty()
	 && priv_->parms_.back()->get_variadic_marker());
}

/// Compare two function types.
///
/// In case these function types are actually method types, this
/// function avoids comparing two parameters (of the function types)
/// if the types of the parameters are actually the types of the
/// classes of the method types.  This prevents infinite recursion
/// during the comparison of two classes that are structurally
/// identical.
///
/// This is a subroutine of the equality operator of function_type.
///
/// @param lhs the first function type to consider
///
/// @param rhs the second function type to consider
///
/// @param k a pointer to a bitfield set by the function to give
/// information about the kind of changes carried by @p lhs and @p
/// rhs.  It is set iff @p k is non-null and the function returns
/// false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
///@return true if lhs == rhs, false otherwise.
bool
equals(const function_type& lhs,
       const function_type& rhs,
       change_kind* k)
{
#define RETURN(value)				\
  do {						\
    lhs.priv_->unmark_as_being_compared(lhs);	\
    lhs.priv_->unmark_as_being_compared(rhs);	\
    if (value == true)				\
      maybe_propagate_canonical_type(lhs, rhs); \
    return value;				\
  } while(0)

  if (lhs.priv_->comparison_started(lhs)
      || lhs.priv_->comparison_started(rhs))
    return true;

  lhs.priv_->mark_as_being_compared(lhs);
  lhs.priv_->mark_as_being_compared(rhs);

  bool result = true;

  if (!lhs.type_base::operator==(rhs))
    {
      result = false;
      if (k)
	*k |= LOCAL_TYPE_CHANGE_KIND;
      else
	RETURN(result);
    }

  class_or_union* lhs_class = 0, *rhs_class = 0;
  if (const method_type* m = dynamic_cast<const method_type*>(&lhs))
    lhs_class = m->get_class_type().get();

  if (const method_type* m = dynamic_cast<const method_type*>(&rhs))
    rhs_class = m->get_class_type().get();

  // Compare the names of the class of the method

  if (!!lhs_class != !!rhs_class)
    {
      result = false;
      if (k)
	*k |= LOCAL_TYPE_CHANGE_KIND;
      else
	RETURN(result);
    }
  else if (lhs_class
	   && (lhs_class->get_qualified_name()
	       != rhs_class->get_qualified_name()))
    {
      result = false;
      if (k)
	*k |= LOCAL_TYPE_CHANGE_KIND;
      else
	RETURN(result);
    }

  // Then compare the return type; Beware if it's t's a class type
  // that is the same as the method class name; we can recurse for
  // ever in that case.

  decl_base* lhs_return_type_decl =
    get_type_declaration(lhs.get_return_type()).get();
  decl_base* rhs_return_type_decl =
    get_type_declaration(rhs.get_return_type()).get();
  bool compare_result_types = true;
  string lhs_rt_name = lhs_return_type_decl
    ? lhs_return_type_decl->get_qualified_name()
    : string();
  string rhs_rt_name = rhs_return_type_decl
    ? rhs_return_type_decl->get_qualified_name()
    : string();

  if ((lhs_class && (lhs_class->get_qualified_name() == lhs_rt_name))
      ||
      (rhs_class && (rhs_class->get_qualified_name() == rhs_rt_name)))
    compare_result_types = false;

  if (compare_result_types)
    {
      if (lhs.get_return_type() != rhs.get_return_type())
	{
	  result = false;
	  if (k)
	    {
	      if (!types_have_similar_structure(lhs.get_return_type(),
						rhs.get_return_type()))
		*k |= LOCAL_TYPE_CHANGE_KIND;
	      else
		*k |= SUBTYPE_CHANGE_KIND;
	    }
	  else
	    RETURN(result);
	}
    }
  else
    if (lhs_rt_name != rhs_rt_name)
      {
	result = false;
	if (k)
	  *k |= SUBTYPE_CHANGE_KIND;
	else
	  RETURN(result);
      }

  vector<shared_ptr<function_decl::parameter> >::const_iterator i,j;
  for (i = lhs.get_first_parm(), j = rhs.get_first_parm();
       i != lhs.get_parameters().end() && j != rhs.get_parameters().end();
       ++i, ++j)
    {
      if (**i != **j)
	{
	  result = false;
	  if (k)
	    {
	      if (!types_have_similar_structure((*i)->get_type(),
						(*j)->get_type()))
		*k |= LOCAL_TYPE_CHANGE_KIND;
	      else
		*k |= SUBTYPE_CHANGE_KIND;
	    }
	  else
	    RETURN(result);
	}
    }

  if ((i != lhs.get_parameters().end()
       || j != rhs.get_parameters().end()))
    {
      result = false;
      if (k)
	*k |= LOCAL_NON_TYPE_CHANGE_KIND;
      else
	RETURN(result);
    }

  RETURN(result);
#undef RETURN
}

/// Get the first parameter of the function.
///
/// If the function is a non-static member function, the parameter
/// returned is the first one following the implicit 'this' parameter.
///
/// @return the first non implicit parameter of the function.
function_type::parameters::const_iterator
function_type::get_first_non_implicit_parm() const
{
  if (get_parameters().empty())
    return get_parameters().end();

  bool is_method = dynamic_cast<const method_type*>(this);

  parameters::const_iterator i = get_parameters().begin();

  if (is_method)
    ++i;

  return i;
}

/// Get the first parameter of the function.
///
/// Note that if the function is a non-static member function, the
/// parameter returned is the implicit 'this' parameter.
///
/// @return the first parameter of the function.
function_type::parameters::const_iterator
function_type::get_first_parm() const
{return get_parameters().begin();}

/// Get the name of the current @ref function_type.
///
/// The name is retrieved from a cache.  If the cache is empty, this
/// function computes the name of the type, stores it in the cache and
/// returns it.  Subsequent invocation of the function are going to
/// just hit the cache.
///
/// Note that if the type is *NOT* canonicalized then function type
/// name is never cached.
///
/// @param internal if true then it means the function type name is
/// going to be used for purposes that are internal to libabigail
/// itself.  If you don't know what this is then you probably should
/// set this parameter to 'false'.
///
/// @return the name of the function type.
const interned_string&
function_type::get_cached_name(bool internal) const
{
  if (internal)
    {
      if (!get_naked_canonical_type() || priv_->internal_cached_name_.empty())
	priv_->internal_cached_name_ = get_function_type_name(this, internal);

      return priv_->internal_cached_name_;
    }

  if (!get_naked_canonical_type() || priv_->cached_name_.empty())
    priv_->cached_name_ = get_function_type_name(this, internal);

  return priv_->cached_name_;
}

/// Equality operator for function_type.
///
/// @param o the other function_type to compare against.
///
/// @return true iff the two function_type are equal.
bool
function_type::operator==(const type_base& other) const
{
  const function_type* o = dynamic_cast<const function_type*>(&other);
  if (!o)
    return false;
  return try_canonical_compare(this, o);
}

/// Return a copy of the pretty representation of the current @ref
/// function_type.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the pretty representation of the current @ref
/// function_type.
string
function_type::get_pretty_representation(bool internal,
					 bool /*qualified_name*/) const
{return ir::get_pretty_representation(this, internal);}

/// Traverses an instance of @ref function_type, visiting all the
/// sub-types and decls that it might contain.
///
/// @param v the visitor that is used to visit every IR sub-node of
/// the current node.
///
/// @return true if either
///  - all the children nodes of the current IR node were traversed
///    and the calling code should keep going with the traversing.
///  - or the current IR node is already being traversed.
/// Otherwise, returning false means that the calling code should not
/// keep traversing the tree.
bool
function_type::traverse(ir_node_visitor& v)
{
  // TODO: should we allow the walker to avoid visiting function type
  // twice?  I think that if we do, then ir_node_visitor needs an
  // option to specifically disallow this feature for function types.

  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      bool keep_going = true;

      if (type_base_sptr t = get_return_type())
	{
	  if (!t->traverse(v))
	    keep_going = false;
	}

      if (keep_going)
	for (parameters::const_iterator i = get_parameters().begin();
	     i != get_parameters().end();
	     ++i)
	  if (type_base_sptr parm_type = (*i)->get_type())
	    if (!parm_type->traverse(v))
	      break;

      visiting(false);
    }
  return v.visit_end(this);
}

function_type::~function_type()
{}
// </function_type>

// <method_type>

struct method_type::priv
{
  class_or_union_wptr class_type_;
  bool is_const;

  priv()
    : is_const()
  {}
}; // end struct method_type::priv

/// Constructor for instances of method_type.
///
/// Instances of method_decl must be of type method_type.
///
/// @param return_type the type of the return value of the method.
///
/// @param class_type the base type of the method type.  That is, the
/// type of the class the method belongs to.
///
/// @param p the vector of the parameters of the method.
///
/// @param is_const whether this method type is for a const method.
/// Note that const-ness is a property of the method *type* and of the
/// relationship between a method *declaration* and its scope.
///
/// @param size_in_bits the size of an instance of method_type,
/// expressed in bits.
///
/// @param alignment_in_bits the alignment of an instance of
/// method_type, expressed in bits.
method_type::method_type (type_base_sptr return_type,
			  class_or_union_sptr class_type,
			  const std::vector<function_decl::parameter_sptr>& p,
			  bool is_const,
			  size_t size_in_bits,
			  size_t alignment_in_bits)
  : type_or_decl_base(class_type->get_environment(),
		      METHOD_TYPE | ABSTRACT_TYPE_BASE | FUNCTION_TYPE),
    type_base(class_type->get_environment(), size_in_bits, alignment_in_bits),
    function_type(return_type, p, size_in_bits, alignment_in_bits),
    priv_(new priv)
{
  runtime_type_instance(this);
  set_class_type(class_type);
  set_is_const(is_const);
}

/// Constructor of instances of method_type.
///
///Instances of method_decl must be of type method_type.
///
/// @param return_type the type of the return value of the method.
///
/// @param class_type the type of the class the method belongs to.
/// The actual (dynamic) type of class_type must be a pointer
/// class_type.  We are setting it to pointer to type_base here to
/// help client code that is compiled without rtti and thus cannot
/// perform dynamic casts.
///
/// @param p the vector of the parameters of the method type.
///
/// @param is_const whether this method type is for a const method.
/// Note that const-ness is a property of the method *type* and of the
/// relationship between a method *declaration* and its scope.
///
/// @param size_in_bits the size of an instance of method_type,
/// expressed in bits.
///
/// @param alignment_in_bits the alignment of an instance of
/// method_type, expressed in bits.
method_type::method_type(type_base_sptr return_type,
			 type_base_sptr class_type,
			 const std::vector<function_decl::parameter_sptr>& p,
			 bool is_const,
			 size_t size_in_bits,
			 size_t alignment_in_bits)
  : type_or_decl_base(class_type->get_environment(),
		      METHOD_TYPE | ABSTRACT_TYPE_BASE | FUNCTION_TYPE),
    type_base(class_type->get_environment(), size_in_bits, alignment_in_bits),
    function_type(return_type, p, size_in_bits, alignment_in_bits),
    priv_(new priv)
{
  runtime_type_instance(this);
  set_class_type(is_class_type(class_type));
  set_is_const(is_const);
}

/// Constructor of the qualified_type_def
///
/// @param env the environment we are operating from.
///
/// @param size_in_bits the size of the type, expressed in bits.
///
/// @param alignment_in_bits the alignment of the type, expressed in bits
method_type::method_type(const environment*	env,
			 size_t		size_in_bits,
			 size_t		alignment_in_bits)
  : type_or_decl_base(env, METHOD_TYPE | ABSTRACT_TYPE_BASE | FUNCTION_TYPE),
    type_base(env, size_in_bits, alignment_in_bits),
    function_type(env, size_in_bits, alignment_in_bits),
    priv_(new priv)
{
  runtime_type_instance(this);
}

/// Constructor of instances of method_type.
///
/// When constructed with this constructor, and instane of method_type
/// must set a return type using method_type::set_return_type
///
/// @param class_typ the base type of the method type.  That is, the
/// type of the class (or union) the method belongs to.
///
/// @param size_in_bits the size of an instance of method_type,
/// expressed in bits.
///
/// @param alignment_in_bits the alignment of an instance of
/// method_type, expressed in bits.
method_type::method_type(class_or_union_sptr class_type,
			 bool is_const,
			 size_t size_in_bits,
			 size_t alignment_in_bits)
  : type_or_decl_base(class_type->get_environment(),
		      METHOD_TYPE | ABSTRACT_TYPE_BASE | FUNCTION_TYPE),
    type_base(class_type->get_environment(), size_in_bits, alignment_in_bits),
    function_type(class_type->get_environment(),
		  size_in_bits,
		  alignment_in_bits),
    priv_(new priv)
{
  runtime_type_instance(this);
  set_class_type(class_type);
  set_is_const(is_const);
}

/// Get the class type this method belongs to.
///
/// @return the class type.
class_or_union_sptr
method_type::get_class_type() const
{return class_or_union_sptr(priv_->class_type_);}

/// Sets the class type of the current instance of method_type.
///
/// The class type is the type of the class the method belongs to.
///
/// @param t the new class type to set.
void
method_type::set_class_type(const class_or_union_sptr& t)
{
  if (!t)
    return;

  priv_->class_type_ = t;
}

/// Return a copy of the pretty representation of the current @ref
/// method_type.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the pretty representation of the current @ref
/// method_type.
string
method_type::get_pretty_representation(bool internal,
				       bool /*qualified_name*/) const
{return ir::get_pretty_representation(*this, internal);}

/// Setter of the "is-const" property of @ref method_type.
///
/// @param the new value of the "is-const" property.
void
method_type::set_is_const(bool f)
{priv_->is_const = f;}

/// Getter of the "is-const" property of @ref method_type.
///
/// @return true iff the "is-const" property was set.
bool
method_type::get_is_const() const
{return priv_->is_const;}

/// The destructor of method_type
method_type::~method_type()
{}

// </method_type>

// <function_decl definitions>

struct function_decl::priv
{
  bool			declared_inline_;
  decl_base::binding	binding_;
  function_type_wptr	type_;
  function_type*	naked_type_;
  elf_symbol_sptr	symbol_;
  interned_string id_;

  priv()
    : declared_inline_(false),
      binding_(decl_base::BINDING_GLOBAL),
      naked_type_()
  {}

  priv(function_type_sptr t,
       bool declared_inline,
       decl_base::binding binding)
    : declared_inline_(declared_inline),
      binding_(binding),
      type_(t),
      naked_type_(t.get())
  {}

  priv(function_type_sptr t,
       bool declared_inline,
       decl_base::binding binding,
       elf_symbol_sptr s)
    : declared_inline_(declared_inline),
      binding_(binding),
      type_(t),
      naked_type_(t.get()),
      symbol_(s)
  {}
}; // end sruct function_decl::priv

function_decl::function_decl(const string& name,
			     function_type_sptr function_type,
			     bool declared_inline,
			     const location& locus,
			     const string& mangled_name,
			     visibility vis,
			     binding bind)
  : type_or_decl_base(function_type->get_environment(),
		      FUNCTION_DECL | ABSTRACT_DECL_BASE),
    decl_base(function_type->get_environment(), name, locus, mangled_name, vis),
    priv_(new priv(function_type, declared_inline, bind))
{
  runtime_type_instance(this);
}

/// Constructor of the function_decl type.
///
/// This flavour of constructor is for when the pointer to the
/// instance of function_type that the client code has is presented as
/// a pointer to type_base.  In that case, this constructor saves the
/// client code from doing a dynamic_cast to get the function_type
/// pointer.
///
/// @param name the name of the function declaration.
///
/// @param fn_type the type of the function declaration.  The dynamic
/// type of this parameter should be 'pointer to function_type'
///
/// @param declared_inline whether this function was declared inline
///
/// @param locus the source location of the function declaration.
///
/// @param linkage_name the mangled name of the function declaration.
///
/// @param vis the visibility of the function declaration.
///
/// @param bind  the kind of the binding of the function
/// declaration.
function_decl::function_decl(const string&	name,
			     type_base_sptr	fn_type,
			     bool		declared_inline,
			     const location&	locus,
			     const string&	linkage_name,
			     visibility	vis,
			     binding		bind)
  : type_or_decl_base(fn_type->get_environment(),
		      FUNCTION_DECL | ABSTRACT_DECL_BASE),
    decl_base(fn_type->get_environment(), name, locus, linkage_name, vis),
    priv_(new priv(dynamic_pointer_cast<function_type>(fn_type),
		   declared_inline,
		   bind))
{
  runtime_type_instance(this);
}

/// Get the pretty representation of the current instance of @ref function_decl.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the pretty representation for a function.
string
function_decl::get_pretty_representation(bool internal,
					 bool /*qualified_name*/) const
{
  const method_decl* mem_fn =
    dynamic_cast<const method_decl*>(this);

  string result = mem_fn ? "method ": "function ";

  if (mem_fn
      && is_member_function(mem_fn)
      && get_member_function_is_virtual(mem_fn))
    result += "virtual ";

  decl_base_sptr type;
  if ((mem_fn
       && is_member_function(mem_fn)
       && (get_member_function_is_dtor(*mem_fn)
	   || get_member_function_is_ctor(*mem_fn))))
    /*cdtors do not have return types.  */;
  else
    type = mem_fn
      ? get_type_declaration(mem_fn->get_type()->get_return_type())
      : get_type_declaration(get_type()->get_return_type());

  if (type)
    result += type->get_qualified_name(internal) + " ";

  result += get_pretty_representation_of_declarator(internal);

  return result;
}

/// Compute and return the pretty representation for the part of the
/// function declaration that starts at the declarator.  That is, the
/// return type and the other specifiers of the beginning of the
/// function's declaration ar omitted.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return the pretty representation for the part of the function
/// declaration that starts at the declarator.
string
function_decl::get_pretty_representation_of_declarator (bool internal) const
{
  const method_decl* mem_fn =
    dynamic_cast<const method_decl*>(this);

  string result;

  if (mem_fn)
    {
      result += mem_fn->get_type()->get_class_type()->get_qualified_name()
	+ "::" + mem_fn->get_name();
    }
  else
    result += get_qualified_name();

  result += "(";

  parameters::const_iterator i = get_parameters().begin(),
    end = get_parameters().end();

  // Skip the first parameter if this is a method.
  if (mem_fn && i != end)
    ++i;
  parameter_sptr parm;
  parameter_sptr first_parm;
  if (i != end)
    first_parm = *i;
  for (; i != end; ++i)
    {
      parm = *i;
      if (parm.get() != first_parm.get())
	result += ", ";
      if (parm->get_variadic_marker()
	  || get_environment()->is_variadic_parameter_type(parm->get_type()))
	result += "...";
      else
	{
	  decl_base_sptr type_decl = get_type_declaration(parm->get_type());
	  result += type_decl->get_qualified_name(internal);
	}
    }
  result += ")";

  if (mem_fn
      &&((is_member_function(mem_fn) && get_member_function_is_const(*mem_fn))
	 || is_method_type(mem_fn->get_type())->get_is_const()))
    result += " const";

  return result;
}

/// Getter for the first non-implicit parameter of a function decl.
///
/// If the function is a non-static member function, the parameter
/// returned is the first one following the implicit 'this' parameter.
///
/// @return the first non implicit parm.
function_decl::parameters::const_iterator
function_decl::get_first_non_implicit_parm() const
{
  if (get_parameters().empty())
    return get_parameters().end();

  bool is_method = dynamic_cast<const method_decl*>(this);

  parameters::const_iterator i = get_parameters().begin();
  if (is_method)
    ++i;

  return i;
}

/// Return the type of the current instance of @ref function_decl.
///
/// It's either a function_type or method_type.
/// @return the type of the current instance of @ref function_decl.
const shared_ptr<function_type>
function_decl::get_type() const
{return priv_->type_.lock();}

/// Fast getter of the type of the current instance of @ref function_decl.
///
/// Note that this function returns the underlying pointer managed by
/// the smart pointer returned by function_decl::get_type().  It's
/// faster than function_decl::get_type().  This getter is to be used
/// in code paths that are proven to be performance hot spots;
/// especially (for instance) when comparing function types.  Those
/// are compared extremely frequently when libabigail is used to
/// handle huge binaries with a lot of functions.
///
/// @return the type of the current instance of @ref function_decl.
const function_type*
function_decl::get_naked_type() const
{return priv_->naked_type_;}

void
function_decl::set_type(const function_type_sptr& fn_type)
{
  priv_->type_ = fn_type;
  priv_->naked_type_ = fn_type.get();
}

/// This sets the underlying ELF symbol for the current function decl.
///
/// And underlyin$g ELF symbol for the current function decl might
/// exist only if the corpus that this function decl originates from
/// was constructed from an ELF binary file.
///
/// Note that comparing two function decls that have underlying ELF
/// symbols involves comparing their underlying elf symbols.  The decl
/// name for the function thus becomes irrelevant in the comparison.
///
/// @param sym the new ELF symbol for this function decl.
void
function_decl::set_symbol(const elf_symbol_sptr& sym)
{
  priv_->symbol_ = sym;
  // The function id cache that depends on the symbol must be
  // invalidated because the symbol changed.
  priv_->id_ = get_environment()->intern("");
}

/// Gets the the underlying ELF symbol for the current variable,
/// that was set using function_decl::set_symbol().  Please read the
/// documentation for that member function for more information about
/// "underlying ELF symbols".
///
/// @return sym the underlying ELF symbol for this function decl, if
/// one exists.
const elf_symbol_sptr&
function_decl::get_symbol() const
{return priv_->symbol_;}

bool
function_decl::is_declared_inline() const
{return priv_->declared_inline_;}

decl_base::binding
function_decl::get_binding() const
{return priv_->binding_;}

/// @return the return type of the current instance of function_decl.
const shared_ptr<type_base>
function_decl::get_return_type() const
{return get_type()->get_return_type();}

/// @return the parameters of the function.
const std::vector<shared_ptr<function_decl::parameter> >&
function_decl::get_parameters() const
{return get_type()->get_parameters();}

/// Append a parameter to the type of this function.
///
/// @param parm the parameter to append.
void
function_decl::append_parameter(shared_ptr<parameter> parm)
{get_type()->append_parameter(parm);}

/// Append a vector of parameters to the type of this function.
///
/// @param parms the vector of parameters to append.
void
function_decl::append_parameters(std::vector<shared_ptr<parameter> >& parms)
{
  for (std::vector<shared_ptr<parameter> >::const_iterator i = parms.begin();
       i != parms.end();
       ++i)
    get_type()->append_parameter(*i);
}

/// Create a new instance of function_decl that is a clone of the
/// current one.
///
/// @return the new clone.
function_decl_sptr
function_decl::clone() const
{
  function_decl_sptr f;
  if (is_member_function(*this))
    {
      method_decl_sptr
	m(new method_decl(get_name(),
				      get_type(),
				      is_declared_inline(),
				      get_location(),
				      get_linkage_name(),
				      get_visibility(),
				      get_binding()));
      class_or_union* scope = is_class_or_union_type(get_scope());
      ABG_ASSERT(scope);
      scope->add_member_function(m, get_member_access_specifier(*this),
				 get_member_function_is_virtual(*this),
				 get_member_function_vtable_offset(*this),
				 get_member_is_static(*this),
				 get_member_function_is_ctor(*this),
				 get_member_function_is_dtor(*this),
				 get_member_function_is_const(*this));
      f = m;
    }
  else
    {
      f.reset(new function_decl(get_name(),
				get_type(),
				is_declared_inline(),
				get_location(),
				get_linkage_name(),
				get_visibility(),
				get_binding()));
      add_decl_to_scope(f, get_scope());
    }
  f->set_symbol(get_symbol());

  return f;
}

/// Compares two instances of @ref function_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const function_decl& l, const function_decl& r, change_kind* k)
{
  bool result = true;

  // Compare function types
  const type_base* t0 = l.get_naked_type(), *t1 = r.get_naked_type();
  if (t0 == t1 || *t0 == *t1)
    ; // the types are equal, let's move on to compare the other
      // properties of the functions.
  else
    {
      result = false;
      if (k)
	{
	  if (!types_have_similar_structure(t0, t1))
	    *k |= LOCAL_TYPE_CHANGE_KIND;
	  else
	    *k |= SUBTYPE_CHANGE_KIND;
	}
      else
	return false;
    }

  const elf_symbol_sptr &s0 = l.get_symbol(), &s1 = r.get_symbol();
  if (!!s0 != !!s1)
    {
      result = false;
      if (k)
	*k |= LOCAL_NON_TYPE_CHANGE_KIND;
      else
	return false;
    }
  else if (s0 && s0 != s1)
    {
      if (!elf_symbols_alias(s0, s1))
	{
	  result = false;
	  if (k)
	    *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	  else
	    return false;
	}
    }
  bool symbols_are_equal = (s0 && s1 && result);

  if (symbols_are_equal)
    {
      // The functions have underlying elf symbols that are equal,
      // so now, let's compare the decl_base part of the functions
      // w/o considering their decl names.
      interned_string n1 = l.get_name(), n2 = r.get_name();
      interned_string ln1 = l.get_linkage_name(), ln2 = r.get_linkage_name();
      const_cast<function_decl&>(l).set_name("");
      const_cast<function_decl&>(l).set_linkage_name("");
      const_cast<function_decl&>(r).set_name("");
      const_cast<function_decl&>(r).set_linkage_name("");

      bool decl_bases_different = !l.decl_base::operator==(r);

      const_cast<function_decl&>(l).set_name(n1);
      const_cast<function_decl&>(l).set_linkage_name(ln1);
      const_cast<function_decl&>(r).set_name(n2);
      const_cast<function_decl&>(r).set_linkage_name(ln2);

      if (decl_bases_different)
	{
	  result = false;
	  if (k)
	    *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	  else
	    return false;
	}
    }
  else
    if (!l.decl_base::operator==(r))
      {
	result = false;
	if (k)
	  *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	else
	  return false;
      }

  // Compare the remaining properties
  if (l.is_declared_inline() != r.is_declared_inline()
      || l.get_binding() != r.get_binding())
    {
      result = false;
      if (k)
	*k |= LOCAL_NON_TYPE_CHANGE_KIND;
      else
	return false;
    }

  if (is_member_function(l) != is_member_function(r))
    {
      result = false;
      if (k)
	  *k |= LOCAL_NON_TYPE_CHANGE_KIND;
      else
	return false;
    }

  if (is_member_function(l) && is_member_function(r))
    {
      if (!((get_member_function_is_ctor(l)
	     == get_member_function_is_ctor(r))
	    && (get_member_function_is_dtor(l)
		== get_member_function_is_dtor(r))
	    && (get_member_is_static(l)
		== get_member_is_static(r))
	    && (get_member_function_is_const(l)
		== get_member_function_is_const(r))
	    && (get_member_function_is_virtual(l)
		== get_member_function_is_virtual(r))
	    && (get_member_function_vtable_offset(l)
		== get_member_function_vtable_offset(r))))
	{
	  result = false;
	  if (k)
	    *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	  else
	    return false;
	}
    }

  return result;
}

/// Comparison operator for @ref function_decl.
///
/// @param other the other instance of @ref function_decl to compare
/// against.
///
/// @return true iff the current instance of @ref function_decl equals
/// @p other.
bool
function_decl::operator==(const decl_base& other) const
{
  const function_decl* o = dynamic_cast<const function_decl*>(&other);
  if (!o)
    return false;
  return equals(*this, *o, 0);
}

/// Return true iff the function takes a variable number of
/// parameters.
///
/// @return true if the function taks a variable number
/// of parameters.
bool
function_decl::is_variadic() const
{
  return (!get_parameters().empty()
	  && get_parameters().back()->get_variadic_marker());
}

/// The virtual implementation of 'get_hash' for a function_decl.
///
/// This allows decl_base::get_hash to work for function_decls.
///
/// @return the hash value for function decl.
size_t
function_decl::get_hash() const
{
  function_decl::hash hash_fn;
  return hash_fn(*this);
}

/// Return an ID that tries to uniquely identify the function inside a
/// program or a library.
///
/// So if the function has an underlying elf symbol, the ID is the
/// concatenation of the symbol name and its version.  Otherwise, the
/// ID is the linkage name if its non-null.  Otherwise, it's the
/// pretty representation of the function.
///
/// @return the ID.
interned_string
function_decl::get_id() const
{
  if (priv_->id_.empty())
    {
      const environment* env = get_type()->get_environment();
      if (elf_symbol_sptr s = get_symbol())
	{
	  if (s->has_aliases())
	    // The symbol has several aliases, so let's use the
	    // linkage name of the function as its ID.
	    priv_->id_ = env->intern(get_linkage_name());
	  else
	    // Let's use the full symbol name with its version as ID.
	    priv_->id_ = env->intern(s->get_id_string());
	}
      else if (!get_linkage_name().empty())
	priv_->id_= env->intern(get_linkage_name());
      else
	priv_->id_ = env->intern(get_pretty_representation());
    }
  return priv_->id_;
}

/// Test if two function declarations are aliases.
///
/// Two functions declarations are aliases if their symbols are
/// aliases, in the ELF sense.
///
/// @param f1 the first function to consider.
///
/// @param f2 the second function to consider.
///
/// @return true iff @p f1 is an alias of @p f2
bool
function_decls_alias(const function_decl& f1, const function_decl& f2)
{
  elf_symbol_sptr s1 = f1.get_symbol(), s2 = f2.get_symbol();

  if (!s1 || !s2)
    return false;

  return elf_symbols_alias(s1, s2);
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
function_decl::traverse(ir_node_visitor& v)
{
  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (type_base_sptr t = get_type())
	t->traverse(v);
      visiting(false);
    }
  return v.visit_end(this);
}

/// Destructor of the @ref function_decl type.
function_decl::~function_decl()
{delete priv_;}

/// A deep comparison operator for a shared pointer to @ref function_decl
///
/// This function compares to shared pointers to @ref function_decl by
/// looking at the pointed-to instances of @ref function_dec
/// comparing them too.  If the two pointed-to objects are equal then
/// this function returns true.
///
/// @param l the left-hand side argument of the equality operator.
///
/// @param r the right-hand side argument of the equality operator.
///
/// @return true iff @p l equals @p r.
bool
operator==(const function_decl_sptr& l, const function_decl_sptr& r)
{
  if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// A deep inequality operator for smart pointers to functions.
///
/// @param l the left-hand side argument of the inequality operator.
///
/// @pram r the right-hand side argument of the inequality operator.
///
/// @return true iff @p is not equal to @p r.
bool
operator!=(const function_decl_sptr& l, const function_decl_sptr& r)
{return !operator==(l, r);}

// <function_decl definitions>

// <function_decl::parameter definitions>

struct function_decl::parameter::priv
{
  type_base_wptr	type_;
  unsigned		index_;
  bool			variadic_marker_;

  priv()
    : index_(),
      variadic_marker_()
  {}

  priv(type_base_sptr type,
       unsigned index,
       bool variadic_marker)
    : type_(type),
      index_(index),
      variadic_marker_(variadic_marker)
  {}
};// end struct function_decl::parameter::priv

function_decl::parameter::parameter(const type_base_sptr	type,
				    unsigned			index,
				    const string&		name,
				    const location&		loc,
				    bool			is_variadic)
  : type_or_decl_base(type->get_environment(),
		      FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE),
    decl_base(type->get_environment(), name, loc),
    priv_(new priv(type, index, is_variadic))
{
  runtime_type_instance(this);
}

function_decl::parameter::parameter(const type_base_sptr	type,
				    unsigned			index,
				    const string&		name,
				    const location&		loc,
				    bool			is_variadic,
				    bool			is_artificial)
  : type_or_decl_base(type->get_environment(),
		      FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE),
    decl_base(type->get_environment(), name, loc),
    priv_(new priv(type, index, is_variadic))
{
  runtime_type_instance(this);
  set_is_artificial(is_artificial);
}

function_decl::parameter::parameter(const type_base_sptr	type,
				    const string&		name,
				    const location&		loc,
				    bool			is_variadic,
				    bool			is_artificial)
  : type_or_decl_base(type->get_environment(),
		      FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE),
    decl_base(type->get_environment(), name, loc),
    priv_(new priv(type, 0, is_variadic))
{
  runtime_type_instance(this);
  set_is_artificial(is_artificial);
}

function_decl::parameter::parameter(const type_base_sptr	type,
				    unsigned			index,
				    bool			variad)
  : type_or_decl_base(type->get_environment(),
		      FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE),
    decl_base(type->get_environment(), "", location()),
    priv_(new priv(type, index, variad))
{
  runtime_type_instance(this);
}

const type_base_sptr
function_decl::parameter::get_type()const
{return priv_->type_.lock();}

/// @return a copy of the type name of the parameter.
interned_string
function_decl::parameter::get_type_name() const
{
  const environment* env = get_environment();
  ABG_ASSERT(env);

  type_base_sptr t = get_type();
  string str;
  if (get_variadic_marker() || env->is_variadic_parameter_type(t))
    str = "...";
  else
    {
	ABG_ASSERT(t);
	str = abigail::ir::get_type_name(t);
    }
  return env->intern(str);
}

/// @return a copy of the pretty representation of the type of the
/// parameter.
const string
function_decl::parameter::get_type_pretty_representation() const
{
  type_base_sptr t = get_type();
  string str;
  if (get_variadic_marker()
      || get_environment()->is_variadic_parameter_type(t))
    str = "...";
  else
    {
	ABG_ASSERT(t);
	str += get_type_declaration(t)->get_pretty_representation();
    }
  return str;
}

/// Get a name uniquely identifying the parameter in the function.
///
///@return the unique parm name id.
interned_string
function_decl::parameter::get_name_id() const
{
  const environment* env = get_environment();
  ABG_ASSERT(env);

  std::ostringstream o;
  o << "parameter-" << get_index();

  return env->intern(o.str());
}

unsigned
function_decl::parameter::get_index() const
{return priv_->index_;}

void
function_decl::parameter::set_index(unsigned i)
{priv_->index_ = i;}


bool
function_decl::parameter::get_variadic_marker() const
{return priv_->variadic_marker_;}

/// Compares two instances of @ref function_decl::parameter.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const function_decl::parameter& l,
       const function_decl::parameter& r,
       change_kind* k)
{
  bool result = true;

  if ((l.get_variadic_marker() != r.get_variadic_marker())
      || (l.get_index() != r.get_index())
      || (!!l.get_type() != !!r.get_type()))
    {
      result = false;
      if (k)
	{
	  if (l.get_index() != r.get_index())
	    *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	  if (l.get_variadic_marker() != r.get_variadic_marker()
	      || !!l.get_type() != !!r.get_type())
	    *k |= LOCAL_TYPE_CHANGE_KIND;
	}
      else
	return false;
    }


  // Sometimes, function parameters can be wrapped into a no-op
  // qualifier.  Let's strip that qualifier out.
  type_base_sptr l_type = look_through_no_op_qualified_type(l.get_type());
  type_base_sptr r_type = look_through_no_op_qualified_type(r.get_type());
  if (l_type != r_type)
    {
      result = false;
      if (k)
	{
	  if (!types_have_similar_structure(l_type, r_type))
	    *k |= LOCAL_TYPE_CHANGE_KIND;
	  else
	    *k |= SUBTYPE_CHANGE_KIND;
	}
      else
	return false;
    }

  return result;
}

bool
function_decl::parameter::operator==(const parameter& o) const
{return equals(*this, o, 0);}

bool
function_decl::parameter::operator==(const decl_base& o) const
{
  const function_decl::parameter* p =
    dynamic_cast<const function_decl::parameter*>(&o);
  if (!p)
    return false;
  return function_decl::parameter::operator==(*p);
}

/// Non-member equality operator for @ref function_decl::parameter.
///
/// @param l the left-hand side of the equality operator
///
/// @param r the right-hand side of the equality operator
///
/// @return true iff @p l and @p r equals.
bool
operator==(const function_decl::parameter_sptr& l,
	   const function_decl::parameter_sptr& r)
{
  if (!!l != !!r)
    return false;
  if (!l)
    return true;
  return *l == *r;
}

/// Non-member inequality operator for @ref function_decl::parameter.
///
/// @param l the left-hand side of the equality operator
///
/// @param r the right-hand side of the equality operator
///
/// @return true iff @p l and @p r different.
bool
operator!=(const function_decl::parameter_sptr& l,
	   const function_decl::parameter_sptr& r)
{return !operator==(l, r);}

/// Traverse the diff sub-tree under the current instance
/// function_decl.
///
/// @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
function_decl::parameter::traverse(ir_node_visitor& v)
{
  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (type_base_sptr t = get_type())
	t->traverse(v);
      visiting(false);
    }
  return v.visit_end(this);
}

/// Get the hash of a decl.  If the hash hasn't been computed yet,
/// compute it ans store its value; otherwise, just return the hash.
///
/// @return the hash of the decl.
size_t
function_decl::parameter::get_hash() const
{
  function_decl::parameter::hash hash_fn_parm;
  return hash_fn_parm(this);
}

/// Compute the qualified name of the parameter.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @param qn the resulting qualified name.
void
function_decl::parameter::get_qualified_name(interned_string& qualified_name,
					     bool /*internal*/) const
{qualified_name = get_name();}

/// Compute and return a copy of the pretty representation of the
/// current function parameter.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @return a copy of the textual representation of the current
/// function parameter.
string
function_decl::parameter::get_pretty_representation(bool internal,
						    bool /*qualified_name*/) const
{
  const environment* env = get_environment();
  ABG_ASSERT(env);

  string type_repr;
  type_base_sptr t = get_type();
  if (!t)
    type_repr = "void";
  else if (env->is_variadic_parameter_type(t))
    type_repr = "...";
  else
    type_repr = ir::get_pretty_representation(t, internal);

  string result = type_repr;
  string parm_name = get_name_id();

  if (!parm_name.empty())
    result += " " + parm_name;

  return result;
}

// </function_decl::parameter definitions>

// <class_or_union definitions>
struct class_or_union::priv
{
  typedef_decl_wptr		naming_typedef_;
  member_types			member_types_;
  data_members			data_members_;
  data_members			non_static_data_members_;
  member_functions		member_functions_;
  // A map that associates a linkage name to a member function.
  string_mem_fn_ptr_map_type	mem_fns_map_;
  // A map that associates function signature strings to member
  // function.
  string_mem_fn_ptr_map_type	signature_2_mem_fn_map_;
  member_function_templates	member_function_templates_;
  member_class_templates	member_class_templates_;

  priv()
  {}

  priv(class_or_union::member_types& mbr_types,
       class_or_union::data_members& data_mbrs,
       class_or_union::member_functions& mbr_fns)
    : member_types_(mbr_types),
      data_members_(data_mbrs),
      member_functions_(mbr_fns)
  {
    for (data_members::const_iterator i = data_members_.begin();
	 i != data_members_.end();
	 ++i)
      if (!get_member_is_static(*i))
	non_static_data_members_.push_back(*i);
  }

  /// Mark a class or union or union as being currently compared using
  /// the class_or_union== operator.
  ///
  /// Note that is marking business is to avoid infinite loop when
  /// comparing a class or union or union. If via the comparison of a
  /// data member or a member function a recursive re-comparison of
  /// the class or union is attempted, the marking business help to
  /// detect that infinite loop possibility and avoid it.
  ///
  /// @param klass the class or union or union to mark as being
  /// currently compared.
  void
  mark_as_being_compared(const class_or_union& klass) const
  {
    const environment* env = klass.get_environment();
    ABG_ASSERT(env);
    env->priv_->classes_being_compared_.insert(&klass);
  }

  /// Mark a class or union as being currently compared using the
  /// class_or_union== operator.
  ///
  /// Note that is marking business is to avoid infinite loop when
  /// comparing a class or union. If via the comparison of a data
  /// member or a member function a recursive re-comparison of the
  /// class or union is attempted, the marking business help to detect
  /// that infinite loop possibility and avoid it.
  ///
  /// @param klass the class or union to mark as being currently
  /// compared.
  void
  mark_as_being_compared(const class_or_union* klass) const
  {mark_as_being_compared(*klass);}

  /// Mark a class or union as being currently compared using the
  /// class_or_union== operator.
  ///
  /// Note that is marking business is to avoid infinite loop when
  /// comparing a class or union. If via the comparison of a data
  /// member or a member function a recursive re-comparison of the
  /// class or union is attempted, the marking business help to detect
  /// that infinite loop possibility and avoid it.
  ///
  /// @param klass the class or union to mark as being currently
  /// compared.
  void
  mark_as_being_compared(const class_or_union_sptr& klass) const
  {mark_as_being_compared(*klass);}

  /// If the instance of class_or_union has been previously marked as
  /// being compared -- via an invocation of mark_as_being_compared()
  /// this method unmarks it.  Otherwise is has no effect.
  ///
  /// This method is not thread safe because it uses the static data
  /// member classes_being_compared_.  If you wish to use it in a
  /// multi-threaded environment you should probably protect the
  /// access to that static data member with a mutex or somesuch.
  ///
  /// @param klass the instance of class_or_union to unmark.
  void
  unmark_as_being_compared(const class_or_union& klass) const
  {
    const environment* env = klass.get_environment();
    ABG_ASSERT(env);
    env->priv_->classes_being_compared_.erase(&klass);
  }

  /// If the instance of class_or_union has been previously marked as
  /// being compared -- via an invocation of mark_as_being_compared()
  /// this method unmarks it.  Otherwise is has no effect.
  ///
  /// @param klass the instance of class_or_union to unmark.
  void
  unmark_as_being_compared(const class_or_union* klass) const
  {
    if (klass)
      return unmark_as_being_compared(*klass);
  }

  /// Test if a given instance of class_or_union is being currently
  /// compared.
  ///
  ///@param klass the class or union to test.
  ///
  /// @return true if @p klass is being compared, false otherwise.
  bool
  comparison_started(const class_or_union& klass) const
  {
    const environment* env = klass.get_environment();
    ABG_ASSERT(env);
    return env->priv_->classes_being_compared_.count(&klass);
  }

  /// Test if a given instance of class_or_union is being currently
  /// compared.
  ///
  ///@param klass the class or union to test.
  ///
  /// @return true if @p klass is being compared, false otherwise.
  bool
  comparison_started(const class_or_union* klass) const
  {
    if (klass)
      return comparison_started(*klass);
    return false;
  }
}; // end struct class_or_union::priv

/// A Constructor for instances of @ref class_or_union
///
/// @param env the environment we are operating from.
///
/// @param name the identifier of the class.
///
/// @param size_in_bits the size of an instance of @ref
/// class_or_union, expressed in bits
///
/// @param align_in_bits the alignment of an instance of @ref class_or_union,
/// expressed in bits.
///
/// @param locus the source location of declaration point this class.
///
/// @param vis the visibility of instances of @ref class_or_union.
///
/// @param mem_types the vector of member types of this instance of
/// @ref class_or_union.
///
/// @param data_members the vector of data members of this instance of
/// @ref class_or_union.
///
/// @param member_fns the vector of member functions of this instance
/// of @ref class_or_union.
class_or_union::class_or_union(const environment* env, const string& name,
			       size_t size_in_bits, size_t align_in_bits,
			       const location& locus, visibility vis,
			       member_types& mem_types,
			       data_members& data_members,
			       member_functions& member_fns)
  : type_or_decl_base(env,
		      ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_TYPE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, name, locus, name, vis),
    type_base(env, size_in_bits, align_in_bits),
    scope_type_decl(env, name, size_in_bits, align_in_bits, locus, vis),
    priv_(new priv(mem_types, data_members, member_fns))
{
  for (member_types::iterator i = mem_types.begin();
       i != mem_types.end();
       ++i)
    if (!has_scope(get_type_declaration(*i)))
      add_decl_to_scope(get_type_declaration(*i), this);

  for (data_members::iterator i = data_members.begin();
       i != data_members.end();
       ++i)
    if (!has_scope(*i))
      add_decl_to_scope(*i, this);

  for (member_functions::iterator i = member_fns.begin();
       i != member_fns.end();
       ++i)
    if (!has_scope(static_pointer_cast<decl_base>(*i)))
      add_decl_to_scope(*i, this);
}

/// A constructor for instances of @ref class_or_union.
///
/// @param env the environment we are operating from.
///
/// @param name the name of the class.
///
/// @param size_in_bits the size of an instance of @ref
/// class_or_union, expressed in bits
///
/// @param align_in_bits the alignment of an instance of @ref class_or_union,
/// expressed in bits.
///
/// @param locus the source location of declaration point this class.
///
/// @param vis the visibility of instances of @ref class_or_union.
class_or_union::class_or_union(const environment* env, const string& name,
			       size_t size_in_bits, size_t align_in_bits,
			       const location& locus, visibility vis)
  : type_or_decl_base(env,
		      ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_TYPE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, name, locus, name, vis),
    type_base(env, size_in_bits, align_in_bits),
    scope_type_decl(env, name, size_in_bits, align_in_bits, locus, vis),
    priv_(new priv)
{}

/// Constructor of the @ref class_or_union type.
///
/// @param env the @ref environment we are operating from.
///
/// @param name the name of the @ref class_or_union.
///
/// @param is_declaration_only a boolean saying whether the instance
/// represents a declaration only, or not.
class_or_union::class_or_union(const environment* env, const string& name,
			       bool is_declaration_only)
  : type_or_decl_base(env,
		      ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_TYPE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, name, location(), name),
    type_base(env, 0, 0),
    scope_type_decl(env, name, 0, 0, location()),
    priv_(new priv)
{
  set_is_declaration_only(is_declaration_only);
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the member nodes of the translation
/// unit during the traversal.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
class_or_union::traverse(ir_node_visitor& v)
{
  if (v.type_node_has_been_visited(this))
    return true;

  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      bool stop = false;

      if (!stop)
	for (data_members::const_iterator i = get_data_members().begin();
	     i != get_data_members().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }

      if (!stop)
	for (member_functions::const_iterator i= get_member_functions().begin();
	     i != get_member_functions().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }

      if (!stop)
	for (member_types::const_iterator i = get_member_types().begin();
	     i != get_member_types().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }

      if (!stop)
	for (member_function_templates::const_iterator i =
	       get_member_function_templates().begin();
	     i != get_member_function_templates().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }

      if (!stop)
	for (member_class_templates::const_iterator i =
	       get_member_class_templates().begin();
	     i != get_member_class_templates().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }
      visiting(false);
    }

  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);
  return result;
}

/// Destrcutor of the @ref class_or_union type.
class_or_union::~class_or_union()
{delete priv_;}

/// Add a member declaration to the current instance of class_or_union.
/// The member declaration can be either a member type, data member,
/// member function, or member template.
///
/// @param d the member declaration to add.
decl_base_sptr
class_or_union::add_member_decl(const decl_base_sptr& d)
{return insert_member_decl(d, get_member_decls().end());}

/// Remove a given decl from the current @ref class_or_union scope.
///
/// Note that only type declarations are supported by this method for
/// now.  Support for the other kinds of declaration is left as an
/// exercise for the interested reader of the code.
///
/// @param decl the declaration to remove from this @ref
/// class_or_union scope.
void
class_or_union::remove_member_decl(decl_base_sptr decl)
{
  type_base_sptr t = is_type(decl);

  // For now we want to support just removing types from classes.  For
  // other kinds of IR node, we need more work.
  ABG_ASSERT(t);

  remove_member_type(t);
}

/// Fixup the members of the type of an anonymous data member.
///
/// Walk all data members of (the type of) a given anonymous data
/// member and set a particular property of the relationship between
/// each data member and its containing type.
///
/// That property records the fact that the data member belongs to the
/// anonymous data member we consider.
///
/// In the future, if there are other properties of this relationship
/// to set in this manner, they ought to be added here.
///
/// @param anon_dm the anonymous data member to consider.
void
class_or_union::maybe_fixup_members_of_anon_data_member(var_decl_sptr& anon_dm)
{
  class_or_union * anon_dm_type =
    anonymous_data_member_to_class_or_union(anon_dm.get());
  if (!anon_dm_type)
    return;

  for (class_or_union::data_members::const_iterator it =
	 anon_dm_type->get_non_static_data_members().begin();
       it != anon_dm_type->get_non_static_data_members().end();
       ++it)
    {
      dm_context_rel *rel =
	dynamic_cast<dm_context_rel*>((*it)->get_context_rel());
      ABG_ASSERT(rel);
      rel->set_anonymous_data_member(anon_dm.get());
    }
}

/// Insert a member type.
///
/// @param t the type to insert in the @ref class_or_union type.
///
/// @param an iterator right before which @p t has to be inserted.
void
class_or_union::insert_member_type(type_base_sptr t,
				   declarations::iterator before)
{
  decl_base_sptr d = get_type_declaration(t);
  ABG_ASSERT(d);
  ABG_ASSERT(!has_scope(d));

  priv_->member_types_.push_back(t);
  scope_decl::insert_member_decl(d, before);
}

/// Add a member type to the current instance of class_or_union.
///
/// @param t the member type to add.  It must not have been added to a
/// scope, otherwise this will violate an ABG_ASSERTion.
void
class_or_union::add_member_type(type_base_sptr t)
{insert_member_type(t, get_member_decls().end());}

/// Add a member type to the current instance of class_or_union.
///
/// @param t the type to be added as a member type to the current
/// instance of class_or_union.  An instance of class_or_union::member_type
/// will be created out of @p t and and added to the the class.
///
/// @param a the access specifier for the member type to be created.
type_base_sptr
class_or_union::add_member_type(type_base_sptr t, access_specifier a)
{
  decl_base_sptr d = get_type_declaration(t);
  ABG_ASSERT(d);
  ABG_ASSERT(!is_member_decl(d));
  add_member_type(t);
  set_member_access_specifier(d, a);
  return t;
}

/// Remove a member type from the current @ref class_or_union scope.
///
/// @param t the type to remove.
void
class_or_union::remove_member_type(type_base_sptr t)
{
  for (member_types::iterator i = priv_->member_types_.begin();
       i != priv_->member_types_.end();
       ++i)
    {
      if (*((*i)) == *t)
	{
	  priv_->member_types_.erase(i);
	  return;
	}
    }
}

/// Getter of the alignment of the @ref class_or_union type.
///
/// If this @ref class_or_union is a declaration of a definition that
/// is elsewhere, then the size of the definition is returned.
///
/// @return the alignment of the @ref class_or_union type.
size_t
class_or_union::get_alignment_in_bits() const
{
  if (get_is_declaration_only() && get_definition_of_declaration())
    return is_class_or_union_type
      (get_definition_of_declaration())->get_alignment_in_bits();

   return type_base::get_alignment_in_bits();
}

/// Setter of the alignment of the class type.
///
/// If this class is a declaration of a definition that is elsewhere,
/// then the new alignment is set to the definition.
///
/// @param s the new alignment.
void
class_or_union::set_alignment_in_bits(size_t a)
{
  if (get_is_declaration_only() && get_definition_of_declaration())
    is_class_or_union_type
      (get_definition_of_declaration()) ->set_alignment_in_bits(a);
  else
    type_base::set_alignment_in_bits(a);
}

/// Setter of the size of the @ref class_or_union type.
///
/// If this @ref class_or_union is a declaration of a definition that
/// is elsewhere, then the new size is set to the definition.
///
/// @param s the new size.
void
class_or_union::set_size_in_bits(size_t s)
{
  if (get_is_declaration_only() && get_definition_of_declaration())
    is_class_or_union_type
      (get_definition_of_declaration())->set_size_in_bits(s);
  else
    type_base::set_size_in_bits(s);
}

/// Getter of the size of the @ref class_or_union type.
///
/// If this @ref class_or_union is a declaration of a definition that
/// is elsewhere, then the size of the definition is returned.
///
/// @return the size of the @ref class_or_union type.
size_t
class_or_union::get_size_in_bits() const
{
  if (get_is_declaration_only() && get_definition_of_declaration())
    return is_class_or_union_type
      (get_definition_of_declaration())->get_size_in_bits();

  return type_base::get_size_in_bits();
}


/// Getter for the naming typedef of the current class.
///
/// Consider the C idiom:
///
///    typedef struct {int member;} foo_type;
///
/// In that idiom, foo_type is the naming typedef of the anonymous
/// struct that is declared.
///
/// @return the naming typedef, if any.  Otherwise, returns nil.
typedef_decl_sptr
class_or_union::get_naming_typedef() const
{return priv_->naming_typedef_.lock();}

/// Set the naming typedef of the current instance of @ref class_decl.
///
/// Consider the C idiom:
///
///    typedef struct {int member;} foo_type;
///
/// In that idiom, foo_type is the naming typedef of the anonymous
/// struct that is declared.
///
/// @param typedef_type the new naming typedef.
void
class_or_union::set_naming_typedef(const typedef_decl_sptr& typedef_type)
{
  priv_->naming_typedef_ = typedef_type;
}

/// Get the member types of this @ref class_or_union.
///
/// @return a vector of the member types of this ref class_or_union.
const class_or_union::member_types&
class_or_union::get_member_types() const
{return priv_->member_types_;}

/// Get the number of anonymous member classes contained in this
/// class.
///
/// @return the number of anonymous member classes contained in this
/// class.
size_t
class_or_union::get_num_anonymous_member_classes() const
{
  int result = 0;
  for (member_types::const_iterator it = get_member_types().begin();
       it != get_member_types().end();
       ++it)
    if (class_decl_sptr t = is_class_type(*it))
      if (t->get_is_anonymous())
	++result;

  return result;
}

/// Get the number of anonymous member unions contained in this class.
///
/// @return the number of anonymous member unions contained in this
/// class.
size_t
class_or_union::get_num_anonymous_member_unions() const
{
  int result = 0;
  for (member_types::const_iterator it = get_member_types().begin();
       it != get_member_types().end();
       ++it)
    if (union_decl_sptr t = is_union_type(*it))
      if (t->get_is_anonymous())
	++result;

  return result;
}

/// Get the number of anonymous member enums contained in this class.
///
/// @return the number of anonymous member enums contained in this
/// class.
size_t
class_or_union::get_num_anonymous_member_enums() const
{
  int result = 0;
  for (member_types::const_iterator it = get_member_types().begin();
       it != get_member_types().end();
       ++it)
    if (enum_type_decl_sptr t = is_enum_type(*it))
      if (t->get_is_anonymous())
	++result;

  return result;
}

/// Find a member type of a given name, inside the current @ref
/// class_or_union.
///
/// @param name the name of the member type to look for.
///
/// @return a pointer to the @ref type_base that represents the member
/// type of name @p name, for the current class.
type_base_sptr
class_or_union::find_member_type(const string& name) const
{
  for (member_types::const_iterator i = get_member_types().begin();
       i != get_member_types().end();
       ++i)
    if (get_type_name(*i, /*qualified*/false) == name)
      return *i;
  return type_base_sptr();
}

/// Add a data member to the current instance of class_or_union.
///
/// @param v a var_decl to add as a data member.  A proper
/// class_or_union::data_member is created from @p v and added to the
/// class_or_union.  This var_decl should not have been already added
/// to a scope.
///
/// @param access the access specifier for the data member.
///
/// @param is_laid_out whether the data member was laid out.  That is,
/// if its offset has been computed.  In the pattern of a class
/// template for instance, this would be set to false.
///
/// @param is_static whether the data memer is static.
///
/// @param offset_in_bits if @p is_laid_out is true, this is the
/// offset of the data member, expressed (oh, surprise) in bits.
void
class_or_union::add_data_member(var_decl_sptr v, access_specifier access,
				bool is_laid_out, bool is_static,
				size_t offset_in_bits)
{
  ABG_ASSERT(!has_scope(v));

  priv_->data_members_.push_back(v);
  scope_decl::add_member_decl(v);
  set_data_member_is_laid_out(v, is_laid_out);
  set_data_member_offset(v, offset_in_bits);
  set_member_access_specifier(v, access);
  set_member_is_static(v, is_static);

  if (!is_static)
    {
      // If this is a non-static variable, add it to the set of
      // non-static variables, if it's not only in there.
      bool is_already_in = false;
      for (data_members::const_iterator i =
	     priv_->non_static_data_members_.begin();
	   i != priv_->non_static_data_members_.end();
	   ++i)
	if (*i == v)
	  {
	    is_already_in = true;
	    break;
	  }
      if (!is_already_in)
	priv_->non_static_data_members_.push_back(v);
    }

  // If v is an anonymous data member, then fixup its data members.
  // For now, the only thing the fixup does is to make the data
  // members of the anonymous data member be aware of their containing
  // anonymous data member.  That is helpful to compute the absolute
  // bit offset of each of the members of the anonymous data member.
  maybe_fixup_members_of_anon_data_member(v);
}

/// Get the data members of this @ref class_or_union.
///
/// @return a vector of the data members of this @ref class_or_union.
const class_or_union::data_members&
class_or_union::get_data_members() const
{return priv_->data_members_;}

/// Find a data member of a given name in the current @ref class_or_union.
///
/// @param name the name of the data member to find in the current
/// @ref class_or_union.
///
/// @return a pointer to the @ref var_decl that represents the data
/// member to find inside the current @ref class_or_union.
const var_decl_sptr
class_or_union::find_data_member(const string& name) const
{
  for (data_members::const_iterator i = get_data_members().begin();
       i != get_data_members().end();
       ++i)
    if ((*i)->get_name() == name)
      return *i;

  // We haven't found a data member with the name 'name'.  Let's look
  // closer again, this time in our anonymous data members.
  for (data_members::const_iterator i = get_data_members().begin();
       i != get_data_members().end();
       ++i)
    if (is_anonymous_data_member(*i))
      {
	class_or_union_sptr type = is_class_or_union_type((*i)->get_type());
	ABG_ASSERT(type);
	if (var_decl_sptr data_member = type->find_data_member(name))
	  return data_member;
      }

  return var_decl_sptr();
}

/// Find an anonymous data member in the class.
///
/// @param v the anonymous data member to find.
///
/// @return the anonymous data member found, or nil if none was found.
const var_decl_sptr
class_or_union::find_anonymous_data_member(const var_decl_sptr& v) const
{
  if (!v->get_name().empty())
    return var_decl_sptr();

  for (data_members::const_iterator it = get_non_static_data_members().begin();
       it != get_non_static_data_members().end();
       ++it)
    {
      if (is_anonymous_data_member(*it))
	if ((*it)->get_pretty_representation(true, true)
	    == v->get_pretty_representation(true, true))
	  return *it;
    }

  return var_decl_sptr();
}

/// Find a given data member.
///
/// This function takes a @ref var_decl as an argument.  If it has a
/// non-empty name, then it tries to find a data member which has the
/// same name as the argument.
///
/// If it has an empty name, then the @ref var_decl is considered as
/// an anonymous data member.  In that case, this function tries to
/// find an anonymous data member which type equals that of the @ref
/// var_decl argument.
///
/// @param v this carries either the name of the data member we need
/// to look for, or the type of the anonymous data member we are
/// looking for.
const var_decl_sptr
class_or_union::find_data_member(const var_decl_sptr& v) const
{
  if (!v)
    return var_decl_sptr();

  if (v->get_name().empty())
    return find_anonymous_data_member(v);

  return find_data_member(v->get_name());
}


/// Get the non-static data memebers of this @ref class_or_union.
///
/// @return a vector of the non-static data members of this @ref
/// class_or_union.
const class_or_union::data_members&
class_or_union::get_non_static_data_members() const
{return priv_->non_static_data_members_;}

/// Add a member function.
///
/// @param f the new member function to add.
///
/// @param a the access specifier to use for the new member function.
///
/// @param is_static whether the new member function is static.
///
/// @param is_ctor whether the new member function is a constructor.
///
/// @param is_dtor whether the new member function is a destructor.
///
/// @param is_const whether the new member function is const.
void
class_or_union::add_member_function(method_decl_sptr f,
				    access_specifier a,
				    bool is_static, bool is_ctor,
				    bool is_dtor, bool is_const)
{
  ABG_ASSERT(!has_scope(f));

  scope_decl::add_member_decl(f);

  set_member_function_is_ctor(f, is_ctor);
  set_member_function_is_dtor(f, is_dtor);
  set_member_access_specifier(f, a);
  set_member_is_static(f, is_static);
  set_member_function_is_const(f, is_const);

  priv_->member_functions_.push_back(f);

  // Update the map of linkage name -> member functions.  It's useful,
  // so that class_or_union::find_member_function() can function.
  if (!f->get_linkage_name().empty())
    priv_->mem_fns_map_[f->get_linkage_name()] = f.get();
}

/// Get the member functions of this @ref class_or_union.
///
/// @return a vector of the member functions of this @ref
/// class_or_union.
const class_or_union::member_functions&
class_or_union::get_member_functions() const
{return priv_->member_functions_;}

/// Find a method, using its linkage name as a key.
///
/// @param linkage_name the linkage name of the method to find.
///
/// @return the method found, or nil if none was found.
const method_decl*
class_or_union::find_member_function(const string& linkage_name) const
{
  return const_cast<class_or_union*>(this)->find_member_function(linkage_name);
}

/// Find a method, using its linkage name as a key.
///
/// @param linkage_name the linkage name of the method to find.
///
/// @return the method found, or nil if none was found.
method_decl*
class_or_union::find_member_function(const string& linkage_name)
{
  string_mem_fn_ptr_map_type::const_iterator i =
    priv_->mem_fns_map_.find(linkage_name);
  if (i == priv_->mem_fns_map_.end())
    return 0;
  return i->second;
}

/// Find a method (member function) using its signature (pretty
/// representation) as a key.
///
/// @param s the signature of the method.
///
/// @return the method found, or nil if none was found.
const method_decl*
class_or_union::find_member_function_from_signature(const string& s) const
{
  return const_cast<class_or_union*>(this)->find_member_function_from_signature(s);
}

/// Find a method (member function) using its signature (pretty
/// representation) as a key.
///
/// @param s the signature of the method.
///
/// @return the method found, or nil if none was found.
method_decl*
class_or_union::find_member_function_from_signature(const string& s)
{
  string_mem_fn_ptr_map_type::const_iterator i =
    priv_->signature_2_mem_fn_map_.find(s);
  if (i == priv_->signature_2_mem_fn_map_.end())
    return 0;
  return i->second;
}

/// Get the member function templates of this class.
///
/// @return a vector of the member function templates of this class.
const member_function_templates&
class_or_union::get_member_function_templates() const
{return priv_->member_function_templates_;}

/// Get the member class templates of this class.
///
/// @return a vector of the member class templates of this class.
const member_class_templates&
class_or_union::get_member_class_templates() const
{return priv_->member_class_templates_;}

/// Append a member function template to the @ref class_or_union.
///
/// @param m the member function template to append.
void
class_or_union::add_member_function_template(member_function_template_sptr m)
{
  decl_base* c = m->as_function_tdecl()->get_scope();
  /// TODO: use our own ABG_ASSERTion facility that adds a meaningful
  /// error message or something like a structured error.
  priv_->member_function_templates_.push_back(m);
  if (!c)
    scope_decl::add_member_decl(m->as_function_tdecl());
}

/// Append a member class template to the @ref class_or_union.
///
/// @param m the member function template to append.
void
class_or_union::add_member_class_template(member_class_template_sptr m)
{
  decl_base* c = m->as_class_tdecl()->get_scope();
  /// TODO: use our own ABG_ASSERTion facility that adds a meaningful
  /// error message or something like a structured error.
  m->set_scope(this);
  priv_->member_class_templates_.push_back(m);
  if (!c)
    scope_decl::add_member_decl(m->as_class_tdecl());
}

///@return true iff the current instance has no member.
bool
class_or_union::has_no_member() const
{
  return (priv_->member_types_.empty()
	  && priv_->data_members_.empty()
	  && priv_->member_functions_.empty()
	  && priv_->member_function_templates_.empty()
	  && priv_->member_class_templates_.empty());
}

/// Insert a data member to this @ref class_or_union type.
///
/// @param d the data member to insert.
///
/// @param before an iterator to the point before which to insert the
/// the data member, in the coontainer that contains all the data
/// members.
decl_base_sptr
class_or_union::insert_member_decl(decl_base_sptr d,
				   declarations::iterator before)
{
  if (type_base_sptr t = dynamic_pointer_cast<type_base>(d))
    insert_member_type(t, before);
  else if (var_decl_sptr v = dynamic_pointer_cast<var_decl>(d))
    {
      add_data_member(v, public_access,
		      /*is_laid_out=*/false,
		      /*is_static=*/true,
		      /*offset_in_bits=*/0);
      d = v;
    }
  else if (method_decl_sptr f = dynamic_pointer_cast<method_decl>(d))
    add_member_function(f, public_access,
			/*is_static=*/false,
			/*is_ctor=*/false,
			/*is_dtor=*/false,
			/*is_const=*/false);
  else if (member_function_template_sptr f =
	   dynamic_pointer_cast<member_function_template>(d))
    add_member_function_template(f);
  else if (member_class_template_sptr c =
	   dynamic_pointer_cast<member_class_template>(d))
    add_member_class_template(c);
  else
    scope_decl::add_member_decl(d);

  return d;
}

/// Equality operator.
///
/// @param other the other @ref class_or_union to compare against.
///
/// @return true iff @p other equals the current @ref class_or_union.
bool
class_or_union::operator==(const decl_base& other) const
{
  const class_or_union* op = dynamic_cast<const class_or_union*>(&other);
  if (!op)
    return false;

  // If this is a decl-only type (and thus with no canonical type),
  // use the canonical type of the definition, if any.
  const class_or_union *l = 0;
  if (get_is_declaration_only())
    l = dynamic_cast<const class_or_union*>(get_naked_definition_of_declaration());
  if (l == 0)
    l = this;

  // Likewise for the other class.
  const class_or_union *r = 0;
  if (op->get_is_declaration_only())
    r = dynamic_cast<const class_or_union*>(op->get_naked_definition_of_declaration());
  if (r == 0)
    r = op;

  return try_canonical_compare(l, r);
}

/// Equality operator.
///
/// @param other the other @ref class_or_union to compare against.
///
/// @return true iff @p other equals the current @ref class_or_union.
bool
class_or_union::operator==(const type_base& other) const
{
  const decl_base* o = dynamic_cast<const decl_base*>(&other);
  if (!o)
    return false;
  return *this == *o;
}

/// Equality operator.
///
/// @param other the other @ref class_or_union to compare against.
///
/// @return true iff @p other equals the current @ref class_or_union.
bool
class_or_union::operator==(const class_or_union& other) const
{
  const decl_base& o = other;
  return class_or_union::operator==(o);
}

/// Compares two instances of @ref class_or_union.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff it's non-null and if the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const class_or_union& l, const class_or_union& r, change_kind* k)
{
#define RETURN(value)				\
  do {						\
    l.priv_->unmark_as_being_compared(l);	\
    l.priv_->unmark_as_being_compared(r);	\
    return value;				\
  } while(0)

  // if one of the classes is declaration-only, look through it to
  // get its definition.
  bool l_is_decl_only = l.get_is_declaration_only();
  bool r_is_decl_only = r.get_is_declaration_only();
  if (l_is_decl_only || r_is_decl_only)
    {
      const class_or_union* def1 = l_is_decl_only
	? is_class_or_union_type(l.get_naked_definition_of_declaration())
	: &l;

      const class_or_union* def2 = r_is_decl_only
	? is_class_or_union_type(r.get_naked_definition_of_declaration())
	: &r;

      if (!def1 || !def2)
	{
	  if (!l.get_is_anonymous()
	      && !r.get_is_anonymous()
	      && l_is_decl_only && r_is_decl_only
	      && comparison::filtering::is_decl_only_class_with_size_change(l, r))
	    // The two decl-only classes differ from their size.  A
	    // true decl-only class should not have a size property to
	    // begin with.  This comes from a DWARF oddity and can
	    // results in a false positive, so let's not consider that
	    // change.
	    return true;

	  if (l.get_environment()->decl_only_class_equals_definition()
	      && !l.get_is_anonymous()
	      && !r.get_is_anonymous())
	    {
	      const interned_string& q1 = l.get_scoped_name();
	      const interned_string& q2 = r.get_scoped_name();
	      if (q1 == q2)
		// Not using RETURN(true) here, because that causes
		// performance issues.  We don't need to do
		// l.priv_->unmark_as_being_compared({l,r}) here because
		// we haven't marked l or r as being compared yet, and
		// doing so has a peformance cost that shows up on
		// performance profiles for *big* libraries.
		return true;
	      else
		{
		  if (k)
		    *k |= LOCAL_TYPE_CHANGE_KIND;
		  // Not using RETURN(true) here, because that causes
		  // performance issues.  We don't need to do
		  // l.priv_->unmark_as_being_compared({l,r}) here because
		  // we haven't marked l or r as being compared yet, and
		  // doing so has a peformance cost that shows up on
		  // performance profiles for *big* libraries.
		  return false;
		}
	    }
	  else // A decl-only class is considered different from a
	       // class definition of the same name.
	    {
	      if (!!def1 != !!def2)
		{
		  if (k)
		    *k |= LOCAL_TYPE_CHANGE_KIND;
		  return false;
		}

	      // both definitions are empty
	      if (!(l.decl_base::operator==(r)
		       && l.type_base::operator==(r)))
		{
		  if (k)
		    *k |= LOCAL_TYPE_CHANGE_KIND;
		  return false;
		}

	      return true;
	    }
	}

      if (l.priv_->comparison_started(l)
	  || l.priv_->comparison_started(r))
	return true;

      l.priv_->mark_as_being_compared(l);
      l.priv_->mark_as_being_compared(r);

      bool val = *def1 == *def2;
      if (!val)
	if (k)
	  *k |= LOCAL_TYPE_CHANGE_KIND;
      RETURN(val);
    }

  // No need to go further if the classes have different names or
  // different size / alignment.
  if (!(l.decl_base::operator==(r) && l.type_base::operator==(r)))
    {
      if (k)
	*k |= LOCAL_TYPE_CHANGE_KIND;
      return false;
    }

  if (types_defined_same_linux_kernel_corpus_public(l, r))
    return true;

  if (l.priv_->comparison_started(l)
      || l.priv_->comparison_started(r))
    return true;

  l.priv_->mark_as_being_compared(l);
  l.priv_->mark_as_being_compared(r);

  bool result = true;

  //compare data_members
  {
    if (l.get_non_static_data_members().size()
	!= r.get_non_static_data_members().size())
      {
	result = false;
	if (k)
	  *k |= LOCAL_TYPE_CHANGE_KIND;
	else
	  RETURN(result);
      }

    for (class_or_union::data_members::const_iterator
	   d0 = l.get_non_static_data_members().begin(),
	   d1 = r.get_non_static_data_members().begin();
	 (d0 != l.get_non_static_data_members().end()
	  && d1 != r.get_non_static_data_members().end());
	 ++d0, ++d1)
      if (**d0 != **d1)
	{
	  result = false;
	  if (k)
	    {
	      // Report any representation change as being local.
	      if (!types_have_similar_structure((*d0)->get_type(),
						(*d1)->get_type()))
		*k |= LOCAL_TYPE_CHANGE_KIND;
	      else
		*k |= SUBTYPE_CHANGE_KIND;
	    }
	  else
	    RETURN(result);
	}
  }

  // Do not compare member functions.  DWARF does not necessarily
  // all the member functions, be they virtual or not, in all
  // translation units.  So we cannot have a clear view of them, per
  // class

  // compare member function templates
  {
    if (l.get_member_function_templates().size()
	!= r.get_member_function_templates().size())
      {
	result = false;
	if (k)
	  *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	else
	  RETURN(result);
      }

    for (member_function_templates::const_iterator
	   fn_tmpl_it0 = l.get_member_function_templates().begin(),
	   fn_tmpl_it1 = r.get_member_function_templates().begin();
	 fn_tmpl_it0 != l.get_member_function_templates().end()
	   &&  fn_tmpl_it1 != r.get_member_function_templates().end();
	 ++fn_tmpl_it0, ++fn_tmpl_it1)
      if (**fn_tmpl_it0 != **fn_tmpl_it1)
	{
	  result = false;
	  if (k)
	    {
	      *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	      break;
	    }
	  else
	    RETURN(result);
	}
  }

  // compare member class templates
  {
    if (l.get_member_class_templates().size()
	!= r.get_member_class_templates().size())
      {
	result = false;
	if (k)
	  *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	else
	  RETURN(result);
      }

    for (member_class_templates::const_iterator
	   cl_tmpl_it0 = l.get_member_class_templates().begin(),
	   cl_tmpl_it1 = r.get_member_class_templates().begin();
	 cl_tmpl_it0 != l.get_member_class_templates().end()
	   &&  cl_tmpl_it1 != r.get_member_class_templates().end();
	 ++cl_tmpl_it0, ++cl_tmpl_it1)
      if (**cl_tmpl_it0 != **cl_tmpl_it1)
	{
	  result = false;
	  if (k)
	    {
	      *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	      break;
	    }
	  else
	    RETURN(result);
	}
  }

  RETURN(result);
#undef RETURN
}


/// Copy a method of a @ref class_or_union into a new @ref
/// class_or_union.
///
/// @param t the @ref class_or_union into which the method is to be copied.
///
/// @param method the method to copy into @p t.
///
/// @return the resulting newly copied method.
method_decl_sptr
copy_member_function(const class_or_union_sptr& t,
		     const method_decl_sptr& method)
{return copy_member_function(t, method.get());}


/// Copy a method of a @ref class_or_union into a new @ref
/// class_or_union.
///
/// @param t the @ref class_or_union into which the method is to be copied.
///
/// @param method the method to copy into @p t.
///
/// @return the resulting newly copied method.
method_decl_sptr
copy_member_function(const class_or_union_sptr& t, const method_decl* method)
{
  ABG_ASSERT(t);
  ABG_ASSERT(method);

  method_type_sptr old_type = method->get_type();
  ABG_ASSERT(old_type);
  method_type_sptr new_type(new method_type(old_type->get_return_type(),
					    t,
					    old_type->get_parameters(),
					    old_type->get_is_const(),
					    old_type->get_size_in_bits(),
					    old_type->get_alignment_in_bits()));
  new_type->set_environment(t->get_environment());
  keep_type_alive(new_type);

  method_decl_sptr
    new_method(new method_decl(method->get_name(),
			       new_type,
			       method->is_declared_inline(),
			       method->get_location(),
			       method->get_linkage_name(),
			       method->get_visibility(),
			       method->get_binding()));
  new_method->set_symbol(method->get_symbol());

  if (class_decl_sptr class_type = is_class_type(t))
    class_type->add_member_function(new_method,
				    get_member_access_specifier(*method),
				    get_member_function_is_virtual(*method),
				    get_member_function_vtable_offset(*method),
				    get_member_is_static(*method),
				    get_member_function_is_ctor(*method),
				    get_member_function_is_dtor(*method),
				    get_member_function_is_const(*method));
  else
    t->add_member_function(new_method,
			   get_member_access_specifier(*method),
			   get_member_is_static(*method),
			   get_member_function_is_ctor(*method),
			   get_member_function_is_dtor(*method),
			   get_member_function_is_const(*method));
  return new_method;
}

// </class_or_union definitions>

/// Test if two @ref class_decl or @ref function_type are already
/// being compared.
///
/// @param lhs_type the first type that would be involved in a
/// potential comparison.
///
/// @param rhs_type the second type that would involved in a potential
/// comparison.
///
/// @return true iff @p lhs_type and @p rhs_type are being compared.
static bool
types_are_being_compared(const type_base& lhs_type,
			 const type_base& rhs_type)
{
  type_base *l = &const_cast<type_base&>(lhs_type);
  type_base *r = &const_cast<type_base&>(rhs_type);

  if (class_or_union *l_cou = is_class_or_union_type(l))
    if (class_or_union *r_cou = is_class_or_union_type(r))
      return (l_cou->priv_->comparison_started(*l_cou)
	      || l_cou->priv_->comparison_started(*r_cou));

  if (function_type *l_fn_type = is_function_type(l))
    if (function_type *r_fn_type = is_function_type(r))
      return (l_fn_type->priv_->comparison_started(*l_fn_type)
	      || l_fn_type->priv_->comparison_started(*r_fn_type));

  return false;
}

/// @defgroup OnTheFlyCanonicalization On-the-fly Canonicalization
/// @{
///
/// During the canonicalization of a type T (which doesn't yet have a
/// canonical type), T is compared structurally (member-wise) against
/// a type C which already has a canonical type.  The comparison
/// expression is C == T.
///
/// During that structural comparison, if a subtype of C (which also
/// already has a canonical type) is structurally compared to a
/// subtype of T (which doesn't yet have a canonical type) and if they
/// are equal, then we can deduce that the canonical type of the
/// subtype of C is the canonical type of the subtype of C.
///
/// Thus, we can canonicalize the sub-type of the T, during the
/// canonicalization of T itself.  That canonicalization of the
/// sub-type of T is what we call the "on-the-fly canonicalization".
/// It's on the fly because it happens during a comparison -- which
/// itself happens during the canonicalization of T.
///
/// For now this on-the-fly canonicalization only happens when
/// comparing @ref class_decl and @ref function_type.
///
/// @}


/// If on-the-fly canonicalization is turned on, then this function
/// sets the canonical type of its second parameter to the canonical
/// type of the first parameter.
///
/// @param lhs_type the type which canonical type to propagate.
///
/// @param rhs_type the type which canonical type to set.
static void
maybe_propagate_canonical_type(const type_base& lhs_type,
			       const type_base& rhs_type)
{

  if (const environment *env = lhs_type.get_environment())
    if (env->do_on_the_fly_canonicalization())
      if (type_base_sptr canonical_type = lhs_type.get_canonical_type())
	if (!rhs_type.get_canonical_type()
	    && !types_are_being_compared(lhs_type, rhs_type))
	  {
	    const_cast<type_base&>(rhs_type).priv_->canonical_type =
	      canonical_type;
	    const_cast<type_base&>(rhs_type).priv_->naked_canonical_type =
	      canonical_type.get();
	  }
}

// <class_decl definitions>

static void
sort_virtual_member_functions(class_decl::member_functions& mem_fns);

/// The private data for the class_decl type.
struct class_decl::priv
{
  base_specs					bases_;
  unordered_map<string, base_spec_sptr>	bases_map_;
  member_functions				virtual_mem_fns_;
  virtual_mem_fn_map_type			virtual_mem_fns_map_;
  bool						is_struct_;

  priv()
    : is_struct_(false)
  {}

  priv(bool is_struct, class_decl::base_specs& bases)
    : bases_(bases),
      is_struct_(is_struct)
  {
  }

  priv(bool is_struct)
    : is_struct_(is_struct)
  {}
};// end struct class_decl::priv

/// A Constructor for instances of \ref class_decl
///
/// @param env the environment we are operating from.
///
/// @param name the identifier of the class.
///
/// @param size_in_bits the size of an instance of class_decl, expressed
/// in bits
///
/// @param align_in_bits the alignment of an instance of class_decl,
/// expressed in bits.
///
/// @param locus the source location of declaration point this class.
///
/// @param vis the visibility of instances of class_decl.
///
/// @param bases the vector of base classes for this instance of class_decl.
///
/// @param mbrs the vector of member types of this instance of
/// class_decl.
///
/// @param data_mbrs the vector of data members of this instance of
/// class_decl.
///
/// @param mbr_fns the vector of member functions of this instance of
/// class_decl.
class_decl::class_decl(const environment* env, const string& name,
		       size_t size_in_bits, size_t align_in_bits,
		       bool is_struct, const location& locus,
		       visibility vis, base_specs& bases,
		       member_types& mbr_types,
		       data_members& data_mbrs,
		       member_functions& mbr_fns)
  : type_or_decl_base(env,
		      CLASS_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_TYPE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, name, locus, name, vis),
    type_base(env, size_in_bits, align_in_bits),
    class_or_union(env, name, size_in_bits, align_in_bits,
		   locus, vis, mbr_types, data_mbrs, mbr_fns),
    priv_(new priv(is_struct, bases))
{
  runtime_type_instance(this);
}

/// A Constructor for instances of @ref class_decl
///
/// @param env the environment we are operating from.
///
/// @param name the identifier of the class.
///
/// @param size_in_bits the size of an instance of class_decl, expressed
/// in bits
///
/// @param align_in_bits the alignment of an instance of class_decl,
/// expressed in bits.
///
/// @param locus the source location of declaration point this class.
///
/// @param vis the visibility of instances of class_decl.
///
/// @param bases the vector of base classes for this instance of class_decl.
///
/// @param mbrs the vector of member types of this instance of
/// class_decl.
///
/// @param data_mbrs the vector of data members of this instance of
/// class_decl.
///
/// @param mbr_fns the vector of member functions of this instance of
/// class_decl.
///
/// @param is_anonymous whether the newly created instance is
/// anonymous.
class_decl::class_decl(const environment* env, const string& name,
		       size_t size_in_bits, size_t align_in_bits,
		       bool is_struct, const location& locus,
		       visibility vis, base_specs& bases,
		       member_types& mbr_types, data_members& data_mbrs,
		       member_functions& mbr_fns, bool is_anonymous)
  : type_or_decl_base(env,
		      CLASS_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_TYPE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, name, locus,
	      // If the class is anonymous then by default it won't
	      // have a linkage name.  Also, the anonymous class does
	      // have an internal-only unique name that is generally
	      // not taken into account when comparing classes; such a
	      // unique internal-only name, when used as a linkage
	      // name might introduce spurious comparison false
	      // negatives.
	      /*linkage_name=*/is_anonymous ? string() : name,
	      vis),
    type_base(env, size_in_bits, align_in_bits),
    class_or_union(env, name, size_in_bits, align_in_bits,
		   locus, vis, mbr_types, data_mbrs, mbr_fns),
    priv_(new priv(is_struct, bases))
{
  runtime_type_instance(this);
  set_is_anonymous(is_anonymous);
}

/// A constructor for instances of class_decl.
///
/// @param env the environment we are operating from.
///
/// @param name the name of the class.
///
/// @param size_in_bits the size of an instance of class_decl, expressed
/// in bits
///
/// @param align_in_bits the alignment of an instance of class_decl,
/// expressed in bits.
///
/// @param locus the source location of declaration point this class.
///
/// @param vis the visibility of instances of class_decl.
class_decl::class_decl(const environment* env, const string& name,
		       size_t size_in_bits, size_t align_in_bits,
		       bool is_struct, const location& locus,
		       visibility vis)
  : type_or_decl_base(env,
		      CLASS_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_TYPE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, name, locus, name, vis),
    type_base(env, size_in_bits, align_in_bits),
    class_or_union(env, name, size_in_bits, align_in_bits,
		   locus, vis),
    priv_(new priv(is_struct))
{
  runtime_type_instance(this);
}

/// A constructor for instances of @ref class_decl.
///
/// @param env the environment we are operating from.
///
/// @param name the name of the class.
///
/// @param size_in_bits the size of an instance of class_decl, expressed
/// in bits
///
/// @param align_in_bits the alignment of an instance of class_decl,
/// expressed in bits.
///
/// @param locus the source location of declaration point this class.
///
/// @param vis the visibility of instances of class_decl.
///
/// @param is_anonymous whether the newly created instance is
/// anonymous.
class_decl:: class_decl(const environment* env, const string& name,
			size_t size_in_bits, size_t align_in_bits,
			bool is_struct, const location& locus,
			visibility vis, bool is_anonymous)
  : type_or_decl_base(env,
		      CLASS_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_TYPE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, name, locus,
	      // If the class is anonymous then by default it won't
	      // have a linkage name.  Also, the anonymous class does
	      // have an internal-only unique name that is generally
	      // not taken into account when comparing classes; such a
	      // unique internal-only name, when used as a linkage
	      // name might introduce spurious comparison false
	      // negatives.
	      /*linkage_name=*/ is_anonymous ? string() : name,
	      vis),
    type_base(env, size_in_bits, align_in_bits),
    class_or_union(env, name, size_in_bits, align_in_bits,
		   locus, vis),
  priv_(new priv(is_struct))
{
  runtime_type_instance(this);
  set_is_anonymous(is_anonymous);
}

/// A constuctor for instances of class_decl that represent a
/// declaration without definition.
///
/// @param env the environment we are operating from.
///
/// @param name the name of the class.
///
/// @param is_declaration_only a boolean saying whether the instance
/// represents a declaration only, or not.
class_decl::class_decl(const environment* env, const string& name,
		       bool is_struct, bool is_declaration_only)
  : type_or_decl_base(env,
		      CLASS_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_TYPE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, name, location(), name),
    type_base(env, 0, 0),
    class_or_union(env, name, is_declaration_only),
    priv_(new priv(is_struct))
{
  runtime_type_instance(this);
}

/// This method is invoked automatically right after the current
/// instance of @ref class_decl has been canonicalized.
///
/// Currently, the only thing it does is to sort the virtual member
/// functions vector.
void
class_decl::on_canonical_type_set()
{
  sort_virtual_mem_fns();

  for (class_decl::virtual_mem_fn_map_type::iterator i =
	 priv_->virtual_mem_fns_map_.begin();
       i != priv_->virtual_mem_fns_map_.end();
       ++i)
    sort_virtual_member_functions(i->second);
}

/// Set the "is-struct" flag of the class.
///
/// @param f the new value of the flag.
void
class_decl::is_struct(bool f)
{priv_->is_struct_ = f;}

/// Test if the class is a struct.
///
/// @return true iff the class is a struct.
bool
class_decl::is_struct() const
{return priv_->is_struct_;}

/// Add a base specifier to this class.
///
/// @param b the new base specifier.
void
class_decl::add_base_specifier(base_spec_sptr b)
{
  ABG_ASSERT(get_environment());
  ABG_ASSERT(b->get_environment() == get_environment());
  priv_->bases_.push_back(b);
  priv_->bases_map_[b->get_base_class()->get_qualified_name()] = b;
  if (const environment* env = get_environment())
    b->set_environment(env);
}

/// Get the base specifiers for this class.
///
/// @return a vector of the base specifiers.
const class_decl::base_specs&
class_decl::get_base_specifiers() const
{return priv_->bases_;}

/// Find a base class of a given qualified name for the current class.
///
/// @param qualified_name the qualified name of the base class to look for.
///
/// @return a pointer to the @ref class_decl that represents the base
/// class of name @p qualified_name, if found.
class_decl_sptr
class_decl::find_base_class(const string& qualified_name) const
{
  unordered_map<string, base_spec_sptr>::iterator i =
    priv_->bases_map_.find(qualified_name);

  if (i != priv_->bases_map_.end())
    return i->second->get_base_class();

  return class_decl_sptr();
}

/// Get the virtual member functions of this class.
///
/// @param return a vector of the virtual member functions of this
/// class.
const class_decl::member_functions&
class_decl::get_virtual_mem_fns() const
{return priv_->virtual_mem_fns_;}

/// Get the map that associates a virtual table offset to the virtual
/// member functions with that virtual table offset.
///
/// Usually, there should be a 1:1 mapping between a given vtable
/// offset and virtual member functions of that vtable offset.  But
/// because of some implementation details, there can be several C++
/// destructor functions that are *generated* by compilers, for a
/// given destructor that is defined in the source code.  If the
/// destructor is virtual then those generated functions have some
/// DWARF attributes in common with the constructor that the user
/// actually defined in its source code.  Among those attributes are
/// the vtable offset of the destructor.
///
/// @return the map that associates a virtual table offset to the
/// virtual member functions with that virtual table offset.
const class_decl::virtual_mem_fn_map_type&
class_decl::get_virtual_mem_fns_map() const
{return priv_->virtual_mem_fns_map_;}

/// Sort the virtual member functions by their virtual index.
void
class_decl::sort_virtual_mem_fns()
{sort_virtual_member_functions(priv_->virtual_mem_fns_);}

/// Getter of the pretty representation of the current instance of
/// @ref class_decl.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @param qualified_name if true, names emitted in the pretty
/// representation are fully qualified.
///
/// @return the pretty representaion for a class_decl.
string
class_decl::get_pretty_representation(bool internal,
				      bool qualified_name) const
{
  string cl = "class ";
  if (!internal && is_struct())
    cl = "struct ";

  // When computing the pretty representation for internal purposes,
  // if an anonymous class is named by a typedef, then consider that
  // it has a name, which is the typedef name.
  if (get_is_anonymous())
    return get_class_or_union_flat_representation(this, "",
						  /*one_line=*/true,
						  internal);

  string result = cl;
  if (qualified_name)
    result += get_qualified_name(internal);
  else
    result += get_name();

  return result;
}

decl_base_sptr
class_decl::insert_member_decl(decl_base_sptr d,
			       declarations::iterator before)
{
  if (method_decl_sptr f = dynamic_pointer_cast<method_decl>(d))
    add_member_function(f, public_access,
			/*is_virtual=*/false,
			/*vtable_offset=*/0,
			/*is_static=*/false,
			/*is_ctor=*/false,
			/*is_dtor=*/false,
			/*is_const=*/false);
  else
    d = class_or_union::insert_member_decl(d, before);

  return d;
}

/// The private data structure of class_decl::base_spec.
struct class_decl::base_spec::priv
{
  class_decl_wptr	base_class_;
  long			offset_in_bits_;
  bool			is_virtual_;

  priv(const class_decl_sptr& cl,
       long offset_in_bits,
       bool is_virtual)
    : base_class_(cl),
      offset_in_bits_(offset_in_bits),
      is_virtual_(is_virtual)
  {}
};

/// Constructor for base_spec instances.
///
/// @param base the base class to consider
///
/// @param a the access specifier of the base class.
///
/// @param offset_in_bits if positive or null, represents the offset
/// of the base in the layout of its containing type..  If negative,
/// means that the current base is not laid out in its containing type.
///
/// @param is_virtual if true, means that the current base class is
/// virtual in it's containing type.
class_decl::base_spec::base_spec(const class_decl_sptr& base,
				 access_specifier a,
				 long offset_in_bits,
				 bool is_virtual)
  : type_or_decl_base(base->get_environment(),
		      ABSTRACT_DECL_BASE),
    decl_base(base->get_environment(), base->get_name(), base->get_location(),
	      base->get_linkage_name(), base->get_visibility()),
    member_base(a),
    priv_(new priv(base, offset_in_bits, is_virtual))
{
  runtime_type_instance(this);
}

/// Get the base class referred to by the current base class
/// specifier.
///
/// @return the base class.
class_decl_sptr
class_decl::base_spec::get_base_class() const
{return priv_->base_class_.lock();}

/// Getter of the "is-virtual" proprerty of the base class specifier.
///
/// @return true iff this specifies a virtual base class.
bool
class_decl::base_spec::get_is_virtual() const
{return priv_->is_virtual_;}

/// Getter of the offset of the base.
///
/// @return the offset of the base.
long
class_decl::base_spec::get_offset_in_bits() const
{return priv_->offset_in_bits_;}

/// Calculate the hash value for a class_decl::base_spec.
///
/// @return the hash value.
size_t
class_decl::base_spec::get_hash() const
{
  base_spec::hash h;
  return h(*this);
}

/// Traverses an instance of @ref class_decl::base_spec, visiting all
/// the sub-types and decls that it might contain.
///
/// @param v the visitor that is used to visit every IR sub-node of
/// the current node.
///
/// @return true if either
///  - all the children nodes of the current IR node were traversed
///    and the calling code should keep going with the traversing.
///  - or the current IR node is already being traversed.
/// Otherwise, returning false means that the calling code should not
/// keep traversing the tree.
bool
class_decl::base_spec::traverse(ir_node_visitor& v)
{
  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      get_base_class()->traverse(v);
      visiting(false);
    }

  return v.visit_end(this);
}

/// Constructor for base_spec instances.
///
/// Note that this constructor is for clients that don't support RTTI
/// and that have a base class of type_base, but of dynamic type
/// class_decl.
///
/// @param base the base class to consider.  Must be a pointer to an
/// instance of class_decl
///
/// @param a the access specifier of the base class.
///
/// @param offset_in_bits if positive or null, represents the offset
/// of the base in the layout of its containing type..  If negative,
/// means that the current base is not laid out in its containing type.
///
/// @param is_virtual if true, means that the current base class is
/// virtual in it's containing type.
class_decl::base_spec::base_spec(const type_base_sptr& base,
				 access_specifier a,
				 long offset_in_bits,
				 bool is_virtual)
  : type_or_decl_base(base->get_environment(),
		      ABSTRACT_DECL_BASE),
    decl_base(base->get_environment(), get_type_declaration(base)->get_name(),
	      get_type_declaration(base)->get_location(),
	      get_type_declaration(base)->get_linkage_name(),
	      get_type_declaration(base)->get_visibility()),
    member_base(a),
    priv_(new priv(dynamic_pointer_cast<class_decl>(base),
		   offset_in_bits,
		   is_virtual))
{
  runtime_type_instance(this);
}

/// Compares two instances of @ref class_decl::base_spec.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const class_decl::base_spec& l,
       const class_decl::base_spec& r,
       change_kind* k)
{
  if (!l.member_base::operator==(r))
    {
      if (k)
	*k |= LOCAL_TYPE_CHANGE_KIND;
      return false;
    }

  return (*l.get_base_class() == *r.get_base_class());
}

/// Comparison operator for @ref class_decl::base_spec.
///
/// @param other the instance of @ref class_decl::base_spec to compare
/// against.
///
/// @return true if the current instance of @ref class_decl::base_spec
/// equals @p other.
bool
class_decl::base_spec::operator==(const decl_base& other) const
{
  const class_decl::base_spec* o =
    dynamic_cast<const class_decl::base_spec*>(&other);

  if (!o)
    return false;

  return equals(*this, *o, 0);
}

/// Comparison operator for @ref class_decl::base_spec.
///
/// @param other the instance of @ref class_decl::base_spec to compare
/// against.
///
/// @return true if the current instance of @ref class_decl::base_spec
/// equals @p other.
bool
class_decl::base_spec::operator==(const member_base& other) const
{
  const class_decl::base_spec* o =
    dynamic_cast<const class_decl::base_spec*>(&other);
  if (!o)
    return false;

  return operator==(static_cast<const decl_base&>(*o));
}

mem_fn_context_rel::~mem_fn_context_rel()
{
}

/// A constructor for instances of method_decl.
///
/// @param name the name of the method.
///
/// @param type the type of the method.
///
/// @param declared_inline whether the method was
/// declared inline or not.
///
/// @param locus the source location of the method.
///
/// @param linkage_name the mangled name of the method.
///
/// @param vis the visibility of the method.
///
/// @param bind the binding of the method.
method_decl::method_decl(const string&		name,
			 method_type_sptr	type,
			 bool			declared_inline,
			 const location&	locus,
			 const string&		linkage_name,
			 visibility		vis,
			 binding		bind)
  : type_or_decl_base(type->get_environment(),
		      METHOD_DECL
		      | ABSTRACT_DECL_BASE
		      |FUNCTION_DECL),
    decl_base(type->get_environment(), name, locus, linkage_name, vis),
    function_decl(name, static_pointer_cast<function_type>(type),
		  declared_inline, locus, linkage_name, vis, bind)
{
  runtime_type_instance(this);
  set_context_rel(new mem_fn_context_rel(0));
  set_member_function_is_const(*this, type->get_is_const());
}

/// A constructor for instances of method_decl.
///
/// @param name the name of the method.
///
/// @param type the type of the method.  Must be an instance of
/// method_type.
///
/// @param declared_inline whether the method was
/// declared inline or not.
///
/// @param locus the source location of the method.
///
/// @param linkage_name the mangled name of the method.
///
/// @param vis the visibility of the method.
///
/// @param bind the binding of the method.
method_decl::method_decl(const string&		name,
			 function_type_sptr	type,
			 bool			declared_inline,
			 const location&	locus,
			 const string&		linkage_name,
			 visibility		vis,
			 binding		bind)
  : type_or_decl_base(type->get_environment(),
		      METHOD_DECL
		      | ABSTRACT_DECL_BASE
		      | FUNCTION_DECL),
    decl_base(type->get_environment(), name, locus, linkage_name, vis),
    function_decl(name, static_pointer_cast<function_type>
		  (dynamic_pointer_cast<method_type>(type)),
		  declared_inline, locus, linkage_name, vis, bind)
{
  runtime_type_instance(this);
  set_context_rel(new mem_fn_context_rel(0));
}

/// A constructor for instances of method_decl.
///
/// @param name the name of the method.
///
/// @param type the type of the method.  Must be an instance of
/// method_type.
///
/// @param declared_inline whether the method was
/// declared inline or not.
///
/// @param locus the source location of the method.
///
/// @param linkage_name the mangled name of the method.
///
/// @param vis the visibility of the method.
///
/// @param bind the binding of the method.
method_decl::method_decl(const string&		name,
			 type_base_sptr	type,
			 bool			declared_inline,
			 const location&	locus,
			 const string&		linkage_name,
			 visibility		vis,
			 binding		bind)
  : type_or_decl_base(type->get_environment(),
		      METHOD_DECL
		      | ABSTRACT_DECL_BASE
		      | FUNCTION_DECL),
    decl_base(type->get_environment(), name, locus, linkage_name, vis),
    function_decl(name, static_pointer_cast<function_type>
		  (dynamic_pointer_cast<method_type>(type)),
		  declared_inline, locus, linkage_name, vis, bind)
{
  runtime_type_instance(this);
  set_context_rel(new mem_fn_context_rel(0));
}

/// Set the linkage name of the method.
///
/// @param l the new linkage name of the method.
void
method_decl::set_linkage_name(const string& l)
{
  decl_base::set_linkage_name(l);
  // Update the linkage_name -> member function map of the containing
  // class declaration.
  if (!l.empty())
    {
      method_type_sptr t = get_type();
      class_or_union_sptr cl = t->get_class_type();
      cl->priv_->mem_fns_map_[l] = this;
    }
}

method_decl::~method_decl()
{}

const method_type_sptr
method_decl::get_type() const
{
  method_type_sptr result;
  if (function_decl::get_type())
    result = dynamic_pointer_cast<method_type>(function_decl::get_type());
  return result;
}

/// Set the containing class of a method_decl.
///
/// @param scope the new containing class_decl.
void
method_decl::set_scope(scope_decl* scope)
{
  if (!get_context_rel())
    set_context_rel(new mem_fn_context_rel(scope));
  else
    get_context_rel()->set_scope(scope);
}

/// Equality operator for @ref method_decl_sptr.
///
/// This is a deep equality operator, as it compares the @ref
/// method_decl that is pointed-to by the smart pointer.
///
/// @param l the left-hand side argument of the equality operator.
///
/// @param r the righ-hand side argument of the equality operator.
///
/// @return true iff @p l equals @p r.
bool
operator==(const method_decl_sptr& l, const method_decl_sptr& r)
{
  if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// Inequality operator for @ref method_decl_sptr.
///
/// This is a deep equality operator, as it compares the @ref
/// method_decl that is pointed-to by the smart pointer.
///
/// @param l the left-hand side argument of the equality operator.
///
/// @param r the righ-hand side argument of the equality operator.
///
/// @return true iff @p l differs from @p r.
bool
operator!=(const method_decl_sptr& l, const method_decl_sptr& r)
{return !operator==(l, r);}

/// Test if a function_decl is actually a method_decl.
///
///@param d the @ref function_decl to consider.
///
/// @return the method_decl sub-object of @p d if inherits
/// a method_decl type.
method_decl*
is_method_decl(const type_or_decl_base *d)
{
  return dynamic_cast<method_decl*>
    (const_cast<type_or_decl_base*>(d));
}

/// Test if a function_decl is actually a method_decl.
///
///@param d the @ref function_decl to consider.
///
/// @return the method_decl sub-object of @p d if inherits
/// a method_decl type.
method_decl*
is_method_decl(const type_or_decl_base&d)
{return is_method_decl(&d);}

/// Test if a function_decl is actually a method_decl.
///
///@param d the @ref function_decl to consider.
///
/// @return the method_decl sub-object of @p d if inherits
/// a method_decl type.
method_decl_sptr
is_method_decl(const type_or_decl_base_sptr& d)
{return dynamic_pointer_cast<method_decl>(d);}

/// A "less than" functor to sort a vector of instances of
/// method_decl that are virtual.
struct virtual_member_function_less_than
{
  /// The less than operator.  First, it sorts the methods by their
  /// vtable index.  If they have the same vtable index, it sorts them
  /// by the name of their ELF symbol.  If they don't have elf
  /// symbols, it sorts them by considering their pretty
  /// representation.
  ///
  ///  Note that this method expects virtual methods.
  ///
  /// @param f the first method to consider.
  ///
  /// @param s the second method to consider.
  ///
  /// @return true if method @p is less than method @s.
  bool
  operator()(const method_decl& f,
	     const method_decl& s)
  {
    ABG_ASSERT(get_member_function_is_virtual(f));
    ABG_ASSERT(get_member_function_is_virtual(s));

    ssize_t f_offset = get_member_function_vtable_offset(f);
    ssize_t s_offset = get_member_function_vtable_offset(s);
    if (f_offset != s_offset) return f_offset < s_offset;

    string fn, sn;

    // If the functions have symbols, then compare their symbol-id
    // string.
    elf_symbol_sptr f_sym = f.get_symbol();
    elf_symbol_sptr s_sym = s.get_symbol();
    if ((!f_sym) != (!s_sym)) return !f_sym;
    if (f_sym && s_sym)
      {
	fn = f_sym->get_id_string();
	sn = s_sym->get_id_string();
	if (fn != sn) return fn < sn;
      }

    // Try the linkage names (important for destructors).
    fn = f.get_linkage_name();
    sn = s.get_linkage_name();
    if (fn != sn) return fn < sn;

    // None of the functions have symbols or linkage names that
    // distinguish them, so compare their pretty representation.
    fn = f.get_pretty_representation();
    sn = s.get_pretty_representation();
    if (fn != sn) return fn < sn;

    /// If it's just the file paths that are different then sort them
    /// too.
    string fn_filepath, sn_filepath;
    unsigned line = 0, column = 0;
    location fn_loc = f.get_location(), sn_loc = s.get_location();
    if (fn_loc)
      fn_loc.expand(fn_filepath, line, column);
    if (sn_loc)
      sn_loc.expand(sn_filepath, line, column);
    return fn_filepath < sn_filepath;
  }

  /// The less than operator.  First, it sorts the methods by their
  /// vtable index.  If they have the same vtable index, it sorts them
  /// by the name of their ELF symbol.  If they don't have elf
  /// symbols, it sorts them by considering their pretty
  /// representation.
  ///
  ///  Note that this method expects to take virtual methods.
  ///
  /// @param f the first method to consider.
  ///
  /// @param s the second method to consider.
  bool
  operator()(const method_decl_sptr f,
	     const method_decl_sptr s)
  {return operator()(*f, *s);}
}; // end struct virtual_member_function_less_than

/// Sort a vector of instances of virtual member functions.
///
/// @param mem_fns the vector of member functions to sort.
static void
sort_virtual_member_functions(class_decl::member_functions& mem_fns)
{
  virtual_member_function_less_than lt;
  std::stable_sort(mem_fns.begin(), mem_fns.end(), lt);
}

/// Add a member function to the current instance of @ref class_or_union.
///
/// @param f a method_decl to add to the current class.  This function
/// should not have been already added to a scope.
///
/// @param access the access specifier for the member function to add.
///
/// @param is_virtual if this is true then it means the function @p f
/// is a virtual function.  That also means that the current instance
/// of @ref class_or_union is actually an instance of @ref class_decl.
///
/// @param vtable_offset the offset of the member function in the
/// virtual table.  This parameter is taken into account only if @p
/// is_virtual is true.
///
/// @param is_static whether the member function is static.
///
/// @param is_ctor whether the member function is a constructor.
///
/// @param is_dtor whether the member function is a destructor.
///
/// @param is_const whether the member function is const.
void
class_or_union::add_member_function(method_decl_sptr f,
				    access_specifier a,
				    bool is_virtual,
				    size_t vtable_offset,
				    bool is_static, bool is_ctor,
				    bool is_dtor, bool is_const)
{
  add_member_function(f, a, is_static, is_ctor,
		      is_dtor, is_const);

  if (class_decl* klass = is_class_type(this))
    {
      set_member_function_is_virtual(f, is_virtual);
      if (is_virtual)
	{
	  set_member_function_vtable_offset(f, vtable_offset);
	  sort_virtual_member_functions(klass->priv_->virtual_mem_fns_);
	}
    }
}

/// When a virtual member function has seen its virtualness set by
/// set_member_function_is_virtual(), this function ensures that the
/// member function is added to the specific vectors and maps of
/// virtual member function of its class.
///
/// @param method the method to fixup.
void
fixup_virtual_member_function(method_decl_sptr method)
{
  if (!method || !get_member_function_is_virtual(method))
    return;

  class_decl_sptr klass = is_class_type(method->get_type()->get_class_type());

  class_decl::member_functions::const_iterator m;
  for (m = klass->priv_->virtual_mem_fns_.begin();
       m != klass->priv_->virtual_mem_fns_.end();
       ++m)
    if (m->get() == method.get())
      break;
  if (m == klass->priv_->virtual_mem_fns_.end())
    klass->priv_->virtual_mem_fns_.push_back(method);

  // Build or udpate the map that associates a vtable offset to the
  // number of virtual member functions that "point" to it.
  ssize_t voffset = get_member_function_vtable_offset(method);
  if (voffset == -1)
    return;

  class_decl::virtual_mem_fn_map_type::iterator i =
    klass->priv_->virtual_mem_fns_map_.find(voffset);
  if (i == klass->priv_->virtual_mem_fns_map_.end())
    {
      class_decl::member_functions virtual_mem_fns_at_voffset;
      virtual_mem_fns_at_voffset.push_back(method);
      klass->priv_->virtual_mem_fns_map_[voffset] = virtual_mem_fns_at_voffset;
    }
  else
    {
      for (m = i->second.begin() ; m != i->second.end(); ++m)
	if (m->get() == method.get())
	  break;
      if (m == i->second.end())
	i->second.push_back(method);
    }
}

/// Return true iff the class has no entity in its scope.
bool
class_decl::has_no_base_nor_member() const
{return priv_->bases_.empty() && has_no_member();}

/// Test if the current instance of @ref class_decl has virtual member
/// functions.
///
/// @return true iff the current instance of @ref class_decl has
/// virtual member functions.
bool
class_decl::has_virtual_member_functions() const
{return !get_virtual_mem_fns().empty();}

/// Test if the current instance of @ref class_decl has at least one
/// virtual base.
///
/// @return true iff the current instance of @ref class_decl has a
/// virtual member function.
bool
class_decl::has_virtual_bases() const
{
  for (base_specs::const_iterator b = get_base_specifiers().begin();
       b != get_base_specifiers().end();
       ++b)
    if ((*b)->get_is_virtual()
	|| (*b)->get_base_class()->has_virtual_bases())
      return true;

  return false;
}

/// Test if the current instance has a vtable.
///
/// This is only valid for a C++ program.
///
/// Basically this function checks if the class has either virtual
/// functions, or virtual bases.
bool
class_decl::has_vtable() const
{
  if (has_virtual_member_functions()
      || has_virtual_bases())
    return true;
  return false;
}

/// Get the highest vtable offset of all the virtual methods of the
/// class.
///
/// @return the highest vtable offset of all the virtual methods of
/// the class.
ssize_t
class_decl::get_biggest_vtable_offset() const
{
  ssize_t offset = -1;
  for (class_decl::virtual_mem_fn_map_type::const_iterator e =
	 get_virtual_mem_fns_map().begin();
       e != get_virtual_mem_fns_map().end();
       ++e)
    if (e->first > offset)
      offset = e->first;

  return offset;
}

/// Return the hash value for the current instance.
///
/// @return the hash value.
size_t
class_decl::get_hash() const
{
  class_decl::hash hash_class;
  return hash_class(this);
}

/// Test if two methods are equal without taking their symbol or
/// linkage name into account.
///
/// @param f the first method.
///
/// @param s the second method.
///
/// @return true iff @p f equals @p s without taking their linkage
/// name or symbol into account.
static bool
methods_equal_modulo_elf_symbol(const method_decl_sptr& f,
				const method_decl_sptr& s)
{
  method_decl_sptr first = f, second = s;
  elf_symbol_sptr saved_first_elf_symbol =
    first->get_symbol();
  elf_symbol_sptr saved_second_elf_symbol =
    second->get_symbol();
  interned_string saved_first_linkage_name =
    first->get_linkage_name();
  interned_string saved_second_linkage_name =
    second->get_linkage_name();

  first->set_symbol(elf_symbol_sptr());
  first->set_linkage_name("");
  second->set_symbol(elf_symbol_sptr());
  second->set_linkage_name("");

  bool equal = *first == *second;

  first->set_symbol(saved_first_elf_symbol);
  first->set_linkage_name(saved_first_linkage_name);
  second->set_symbol(saved_second_elf_symbol);
  second->set_linkage_name(saved_second_linkage_name);

  return equal;
}

/// Test if a given method is equivalent to at least of other method
/// that is in a vector of methods.
///
/// Note that "equivalent" here means being equal without taking the
/// linkage name or the symbol of the methods into account.
///
/// This is a sub-routine of the 'equals' function that compares @ref
/// class_decl.
///
/// @param method the method to compare.
///
/// @param fns the vector of functions to compare @p method against.
///
/// @return true iff @p is equivalent to at least one method in @p
/// fns.
static bool
method_matches_at_least_one_in_vector(const method_decl_sptr& method,
				      const class_decl::member_functions& fns)
{
  for (class_decl::member_functions::const_iterator i = fns.begin();
       i != fns.end();
       ++i)
    if (methods_equal_modulo_elf_symbol(*i, method))
      return true;

  return false;
}

/// Compares two instances of @ref class_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const class_decl& l, const class_decl& r, change_kind* k)
{
  // if one of the classes is declaration-only then we take a fast
  // path here.
  if (l.get_is_declaration_only() || r.get_is_declaration_only())
    return equals(static_cast<const class_or_union&>(l),
		  static_cast<const class_or_union&>(r),
		  k);

  if (l.class_or_union::priv_->comparison_started(l)
      || l.class_or_union::priv_->comparison_started(r))
    return true;

  bool result = true;
  if (!equals(static_cast<const class_or_union&>(l),
	      static_cast<const class_or_union&>(r),
	      k))
    {
      result = false;
      if (!k)
	return result;
    }

  l.class_or_union::priv_->mark_as_being_compared(l);
  l.class_or_union::priv_->mark_as_being_compared(r);

#define RETURN(value)						\
  do {								\
    l.class_or_union::priv_->unmark_as_being_compared(l);	\
    l.class_or_union::priv_->unmark_as_being_compared(r);	\
    if (value == true)						\
      maybe_propagate_canonical_type(l, r);			\
    return value;						\
  } while(0)

  // Compare bases.
    if (l.get_base_specifiers().size() != r.get_base_specifiers().size())
      {
	result = false;
	if (k)
	  *k |= LOCAL_TYPE_CHANGE_KIND;
	else
	  RETURN(result);
      }

    for (class_decl::base_specs::const_iterator
	   b0 = l.get_base_specifiers().begin(),
	   b1 = r.get_base_specifiers().begin();
	 (b0 != l.get_base_specifiers().end()
	 && b1 != r.get_base_specifiers().end());
	 ++b0, ++b1)
      if (*b0 != *b1)
	{
	  result = false;
	  if (k)
	    {
	      if (!types_have_similar_structure((*b0)->get_base_class().get(),
						(*b1)->get_base_class().get()))
		*k |= LOCAL_TYPE_CHANGE_KIND;
	      else
		*k |= SUBTYPE_CHANGE_KIND;
	      break;
	    }
	  RETURN(result);
	}

    // Compare virtual member functions

    // We look at the map that associates a given vtable offset to a
    // vector of virtual member functions that point to that offset.
    //
    // This is because there are cases where several functions can
    // point to the same virtual table offset.
    //
    // This is usually the case for virtual destructors.  Even though
    // there can be only one virtual destructor declared in source
    // code, there are actually potentially up to three generated
    // functions for that destructor.  Some of these generated
    // functions can be clones of other functions that are among those
    // generated ones.  In any cases, they all have the same
    // properties, including the vtable offset property.

    // So, there should be the same number of different vtable
    // offsets, the size of two maps must be equals.
    if (l.get_virtual_mem_fns_map().size()
	!= r.get_virtual_mem_fns_map().size())
      {
	result = false;
	if (k)
	  *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	else
	  RETURN(result);
      }

    // Then, each virtual member function of a given vtable offset in
    // the first class type, must match an equivalent virtual member
    // function of a the same vtable offset in the second class type.
    //
    // By "match", I mean that the two virtual member function should
    // be equal if we don't take into account their symbol name or
    // their linkage name.  This is because two destructor functions
    // clones (for instance) might have different linkage name, but
    // are still equivalent if their other properties are the same.
    for (class_decl::virtual_mem_fn_map_type::const_iterator first_v_fn_entry =
	   l.get_virtual_mem_fns_map().begin();
	 first_v_fn_entry != l.get_virtual_mem_fns_map().end();
	 ++first_v_fn_entry)
      {
	unsigned voffset = first_v_fn_entry->first;
	const class_decl::member_functions& first_vfns =
	  first_v_fn_entry->second;

	const class_decl::virtual_mem_fn_map_type::const_iterator
	  second_v_fn_entry = r.get_virtual_mem_fns_map().find(voffset);

	if (second_v_fn_entry == r.get_virtual_mem_fns_map().end())
	  {
	    result = false;
	    if (k)
	      *k |= LOCAL_NON_TYPE_CHANGE_KIND;
	    RETURN(result);
	  }

	const class_decl::member_functions& second_vfns =
	  second_v_fn_entry->second;

	bool matches = false;
	for (class_decl::member_functions::const_iterator i =
	       first_vfns.begin();
	     i != first_vfns.end();
	     ++i)
	  if (method_matches_at_least_one_in_vector(*i, second_vfns))
	    {
	      matches = true;
	      break;
	    }

	if (!matches)
	  {
	    result = false;
	    if (k)
	      *k |= SUBTYPE_CHANGE_KIND;
	    else
	      RETURN(result);
	  }
      }

  RETURN(result);
#undef RETURN
}

/// Copy a method of a class into a new class.
///
/// @param klass the class into which the method is to be copied.
///
/// @param method the method to copy into @p klass.
///
/// @return the resulting newly copied method.
method_decl_sptr
copy_member_function(const class_decl_sptr& clazz, const method_decl_sptr& f)
{return copy_member_function(static_pointer_cast<class_or_union>(clazz), f);}

/// Copy a method of a class into a new class.
///
/// @param klass the class into which the method is to be copied.
///
/// @param method the method to copy into @p klass.
///
/// @return the resulting newly copied method.
method_decl_sptr
copy_member_function(const class_decl_sptr& clazz, const method_decl* f)
{return copy_member_function(static_pointer_cast<class_or_union>(clazz), f);}

/// Comparison operator for @ref class_decl.
///
/// @param other the instance of @ref class_decl to compare against.
///
/// @return true iff the current instance of @ref class_decl equals @p
/// other.
bool
class_decl::operator==(const decl_base& other) const
{
  const class_decl* op = is_class_type(&other);
  if (!op)
    return false;

  // If this is a decl-only type (and thus with no canonical type),
  // use the canonical type of the definition, if any.
  const class_decl *l = 0;
  if (get_is_declaration_only())
    l = dynamic_cast<const class_decl*>(get_naked_definition_of_declaration());
  if (l == 0)
    l = this;

  ABG_ASSERT(l);

  // Likewise for the other type.
  const class_decl *r = 0;
  if (op->get_is_declaration_only())
    r = dynamic_cast<const class_decl*>(op->get_naked_definition_of_declaration());
  if (r == 0)
    r = op;

  ABG_ASSERT(r);

  return try_canonical_compare(l, r);
}

/// Equality operator for class_decl.
///
/// Re-uses the equality operator that takes a decl_base.
///
/// @param other the other class_decl to compare against.
///
/// @return true iff the current instance equals the other one.
bool
class_decl::operator==(const type_base& other) const
{
  const decl_base* o = is_decl(&other);
  if (!o)
    return false;
  return *this == *o;
}

/// Comparison operator for @ref class_decl.
///
/// @param other the instance of @ref class_decl to compare against.
///
/// @return true iff the current instance of @ref class_decl equals @p
/// other.
bool
class_decl::operator==(const class_decl& other) const
{
  const decl_base& o = other;
  return *this == o;
}

/// Turn equality of shared_ptr of class_decl into a deep equality;
/// that is, make it compare the pointed to objects too.
///
/// @param l the shared_ptr of class_decl on left-hand-side of the
/// equality.
///
/// @param r the shared_ptr of class_decl on right-hand-side of the
/// equality.
///
/// @return true if the class_decl pointed to by the shared_ptrs are
/// equal, false otherwise.
bool
operator==(const class_decl_sptr& l, const class_decl_sptr& r)
{
  if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// Turn inequality of shared_ptr of class_decl into a deep equality;
/// that is, make it compare the pointed to objects too.
///
/// @param l the shared_ptr of class_decl on left-hand-side of the
/// equality.
///
/// @param r the shared_ptr of class_decl on right-hand-side of the
/// equality.
///
/// @return true if the class_decl pointed to by the shared_ptrs are
/// different, false otherwise.
bool
operator!=(const class_decl_sptr& l, const class_decl_sptr& r)
{return !operator==(l, r);}

/// Turn equality of shared_ptr of class_or_union into a deep
/// equality; that is, make it compare the pointed to objects too.
///
/// @param l the left-hand-side operand of the operator
///
/// @param r the right-hand-side operand of the operator.
///
/// @return true iff @p l equals @p r.
bool
operator==(const class_or_union_sptr& l, const class_or_union_sptr& r)
{
  if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// Turn inequality of shared_ptr of class_or_union into a deep
/// equality; that is, make it compare the pointed to objects too.
///
/// @param l the left-hand-side operand of the operator
///
/// @param r the right-hand-side operand of the operator.
///
/// @return true iff @p l is different from @p r.
bool
operator!=(const class_or_union_sptr& l, const class_or_union_sptr& r)
{return !operator==(l, r);}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance and on its
/// members.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
class_decl::traverse(ir_node_visitor& v)
{
  if (v.type_node_has_been_visited(this))
    return true;

  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      bool stop = false;

      for (base_specs::const_iterator i = get_base_specifiers().begin();
	   i != get_base_specifiers().end();
	   ++i)
	{
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }
	}

      if (!stop)
	for (data_members::const_iterator i = get_data_members().begin();
	     i != get_data_members().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }

      if (!stop)
	for (member_functions::const_iterator i= get_member_functions().begin();
	     i != get_member_functions().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }

      if (!stop)
	for (member_types::const_iterator i = get_member_types().begin();
	     i != get_member_types().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }

      if (!stop)
	for (member_function_templates::const_iterator i =
	       get_member_function_templates().begin();
	     i != get_member_function_templates().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }

      if (!stop)
	for (member_class_templates::const_iterator i =
	       get_member_class_templates().begin();
	     i != get_member_class_templates().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }
      visiting(false);
    }

  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);
  return result;
}

/// Destructor of the @ref class_decl type.
class_decl::~class_decl()
{delete priv_;}

context_rel::~context_rel()
{}

bool
member_base::operator==(const member_base& o) const
{
  return (get_access_specifier() == o.get_access_specifier()
	  && get_is_static() == o.get_is_static());
}

/// Equality operator for smart pointers to @ref
/// class_decl::base_specs.
///
/// This compares the pointed-to objects.
///
/// @param l the first instance to consider.
///
/// @param r the second instance to consider.
///
/// @return true iff @p l equals @p r.
bool
operator==(const class_decl::base_spec_sptr& l,
	   const class_decl::base_spec_sptr& r)
{
  if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == static_cast<const decl_base&>(*r);
}

/// Inequality operator for smart pointers to @ref
/// class_decl::base_specs.
///
/// This compares the pointed-to objects.
///
/// @param l the first instance to consider.
///
/// @param r the second instance to consider.
///
/// @return true iff @p l is different from @p r.
bool
operator!=(const class_decl::base_spec_sptr& l,
	   const class_decl::base_spec_sptr& r)
{return !operator==(l, r);}

/// Test if an ABI artifact is a class base specifier.
///
/// @param tod the ABI artifact to consider.
///
/// @return a pointer to the @ref class_decl::base_spec sub-object of
/// @p tod iff it's a class base specifier.
class_decl::base_spec*
is_class_base_spec(const type_or_decl_base* tod)
{
  return dynamic_cast<class_decl::base_spec*>
    (const_cast<type_or_decl_base*>(tod));
}

/// Test if an ABI artifact is a class base specifier.
///
/// @param tod the ABI artifact to consider.
///
/// @return a pointer to the @ref class_decl::base_spec sub-object of
/// @p tod iff it's a class base specifier.
class_decl::base_spec_sptr
is_class_base_spec(type_or_decl_base_sptr tod)
{return dynamic_pointer_cast<class_decl::base_spec>(tod);}

bool
member_function_template::operator==(const member_base& other) const
{
  try
    {
      const member_function_template& o =
	dynamic_cast<const member_function_template&>(other);

      if (!(is_constructor() == o.is_constructor()
	    && is_const() == o.is_const()
	    && member_base::operator==(o)))
	return false;

      if (function_tdecl_sptr ftdecl = as_function_tdecl())
	{
	  function_tdecl_sptr other_ftdecl = o.as_function_tdecl();
	  if (other_ftdecl)
	    return ftdecl->function_tdecl::operator==(*other_ftdecl);
	}
    }
  catch(...)
    {}
  return false;
}

/// Equality operator for smart pointers to @ref
/// member_function_template.  This is compares the
/// pointed-to instances.
///
/// @param l the first instance to consider.
///
/// @param r the second instance to consider.
///
/// @return true iff @p l equals @p r.
bool
operator==(const member_function_template_sptr& l,
	   const member_function_template_sptr& r)
{
  if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// Inequality operator for smart pointers to @ref
/// member_function_template.  This is compares the pointed-to
/// instances.
///
/// @param l the first instance to consider.
///
/// @param r the second instance to consider.
///
/// @return true iff @p l equals @p r.
bool
operator!=(const member_function_template_sptr& l,
	   const member_function_template_sptr& r)
{return !operator==(l, r);}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance and on its
/// underlying function template.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
member_function_template::traverse(ir_node_visitor& v)
{
  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (function_tdecl_sptr f = as_function_tdecl())
	f->traverse(v);
      visiting(false);
    }
  return v.visit_end(this);
}

/// Equality operator of the the @ref member_class_template class.
///
/// @param other the other @ref member_class_template to compare against.
///
/// @return true iff the current instance equals @p other.
bool
member_class_template::operator==(const member_base& other) const
{
  try
    {
      const member_class_template& o =
	dynamic_cast<const member_class_template&>(other);

      if (!member_base::operator==(o))
	return false;

      return as_class_tdecl()->class_tdecl::operator==(o);
    }
  catch(...)
    {return false;}
}

/// Comparison operator for the @ref member_class_template
/// type.
///
/// @param other the other instance of @ref
/// member_class_template to compare against.
///
/// @return true iff the two instances are equal.
bool
member_class_template::operator==(const member_class_template& other) const
{
  const decl_base* o = dynamic_cast<const decl_base*>(&other);
  return *this == *o;
}

/// Comparison operator for the @ref member_class_template
/// type.
///
/// @param l the first argument of the operator.
///
/// @param r the second argument of the operator.
///
/// @return true iff the two instances are equal.
bool
operator==(const member_class_template_sptr& l,
	   const member_class_template_sptr& r)
{
  if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// Inequality operator for the @ref member_class_template
/// type.
///
/// @param l the first argument of the operator.
///
/// @param r the second argument of the operator.
///
/// @return true iff the two instances are equal.
bool
operator!=(const member_class_template_sptr& l,
	   const member_class_template_sptr& r)
{return !operator==(l, r);}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance and on the class
/// pattern of the template.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
member_class_template::traverse(ir_node_visitor& v)
{
  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (class_tdecl_sptr t = as_class_tdecl())
	t->traverse(v);
      visiting(false);
    }
  return v.visit_end(this);
}

/// Streaming operator for class_decl::access_specifier.
///
/// @param o the output stream to serialize the access specifier to.
///
/// @param a the access specifier to serialize.
///
/// @return the output stream.
std::ostream&
operator<<(std::ostream& o, access_specifier a)
{
  string r;

  switch (a)
  {
  case no_access:
    r = "none";
    break;
  case private_access:
    r = "private";
    break;
  case protected_access:
    r = "protected";
    break;
  case public_access:
    r= "public";
    break;
  };
  o << r;
  return o;
}

/// Sets the static-ness property of a class member.
///
/// @param d the class member to set the static-ness property for.
/// Note that this must be a class member otherwise the function
/// aborts the current process.
///
/// @param s this must be true if the member is to be static, false
/// otherwise.
void
set_member_is_static(decl_base& d, bool s)
{
  ABG_ASSERT(is_member_decl(d));

  context_rel* c = d.get_context_rel();
  ABG_ASSERT(c);

  c->set_is_static(s);

  scope_decl* scope = d.get_scope();

  if (class_or_union* cl = is_class_or_union_type(scope))
    {
      if (var_decl* v = is_var_decl(&d))
	{
	  if (s)
	    // remove from the non-static data members
	    for (class_decl::data_members::iterator i =
		   cl->priv_->non_static_data_members_.begin();
		 i != cl->priv_->non_static_data_members_.end();
		 ++i)
	      {
		if ((*i)->get_name() == v->get_name())
		  {
		    cl->priv_->non_static_data_members_.erase(i);
		    break;
		  }
	      }
	  else
	    {
	      bool is_already_in_non_static_data_members = false;
	      for (class_or_union::data_members::iterator i =
		     cl->priv_->non_static_data_members_.begin();
		   i != cl->priv_->non_static_data_members_.end();
		   ++i)
	      {
		if ((*i)->get_name() == v->get_name())
		  {
		    is_already_in_non_static_data_members = true;
		    break;
		  }
	      }
	      if (!is_already_in_non_static_data_members)
		{
		  var_decl_sptr var;
		  // add to non-static data members.
		  for (class_or_union::data_members::const_iterator i =
			 cl->priv_->data_members_.begin();
		       i != cl->priv_->data_members_.end();
		       ++i)
		    {
		      if ((*i)->get_name() == v->get_name())
			{
			  var = *i;
			  break;
			}
		    }
		  ABG_ASSERT(var);
		  cl->priv_->non_static_data_members_.push_back(var);
		}
	    }
	}
    }
}

/// Sets the static-ness property of a class member.
///
/// @param d the class member to set the static-ness property for.
/// Note that this must be a class member otherwise the function
/// aborts the current process.
///
/// @param s this must be true if the member is to be static, false
/// otherwise.
void
set_member_is_static(const decl_base_sptr& d, bool s)
{set_member_is_static(*d, s);}

// </class_decl>

// <union_decl>

/// Constructor for the @ref union_decl type.
///
/// @param env the @ref environment we are operating from.
///
/// @param name the name of the union type.
///
/// @param size_in_bits the size of the union, in bits.
///
/// @param locus the location of the type.
///
/// @param vis the visibility of instances of @ref union_decl.
///
/// @param mbr_types the member types of the union.
///
/// @param data_mbrs the data members of the union.
///
/// @param member_fns the member functions of the union.
union_decl::union_decl(const environment* env, const string& name,
		       size_t size_in_bits, const location& locus,
		       visibility vis, member_types& mbr_types,
		       data_members& data_mbrs, member_functions& member_fns)
  : type_or_decl_base(env,
		      UNION_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE),
    decl_base(env, name, locus, name, vis),
    type_base(env, size_in_bits, 0),
    class_or_union(env, name, size_in_bits, 0,
		   locus, vis, mbr_types, data_mbrs, member_fns)
{
  runtime_type_instance(this);
}

/// Constructor for the @ref union_decl type.
///
/// @param env the @ref environment we are operating from.
///
/// @param name the name of the union type.
///
/// @param size_in_bits the size of the union, in bits.
///
/// @param locus the location of the type.
///
/// @param vis the visibility of instances of @ref union_decl.
///
/// @param mbr_types the member types of the union.
///
/// @param data_mbrs the data members of the union.
///
/// @param member_fns the member functions of the union.
///
/// @param is_anonymous whether the newly created instance is
/// anonymous.
union_decl::union_decl(const environment* env, const string& name,
		       size_t size_in_bits, const location& locus,
		       visibility vis, member_types& mbr_types,
		       data_members& data_mbrs, member_functions& member_fns,
		       bool is_anonymous)
  : type_or_decl_base(env,
		      UNION_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE),
    decl_base(env, name, locus,
	      // If the class is anonymous then by default it won't
	      // have a linkage name.  Also, the anonymous class does
	      // have an internal-only unique name that is generally
	      // not taken into account when comparing classes; such a
	      // unique internal-only name, when used as a linkage
	      // name might introduce spurious comparison false
	      // negatives.
	      /*linkage_name=*/is_anonymous ? string() : name,
	      vis),
    type_base(env, size_in_bits, 0),
    class_or_union(env, name, size_in_bits, 0,
		   locus, vis, mbr_types, data_mbrs, member_fns)
{
  runtime_type_instance(this);
  set_is_anonymous(is_anonymous);
}

/// Constructor for the @ref union_decl type.
///
/// @param env the @ref environment we are operating from.
///
/// @param name the name of the union type.
///
/// @param size_in_bits the size of the union, in bits.
///
/// @param locus the location of the type.
///
/// @param vis the visibility of instances of @ref union_decl.
union_decl::union_decl(const environment* env, const string& name,
		       size_t size_in_bits, const location& locus,
		       visibility vis)
  : type_or_decl_base(env,
		      UNION_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_TYPE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, name, locus, name, vis),
    type_base(env, size_in_bits, 0),
    class_or_union(env, name, size_in_bits,
		   0, locus, vis)
{
  runtime_type_instance(this);
}

/// Constructor for the @ref union_decl type.
///
/// @param env the @ref environment we are operating from.
///
/// @param name the name of the union type.
///
/// @param size_in_bits the size of the union, in bits.
///
/// @param locus the location of the type.
///
/// @param vis the visibility of instances of @ref union_decl.
///
/// @param is_anonymous whether the newly created instance is
/// anonymous.
union_decl::union_decl(const environment* env, const string& name,
		       size_t size_in_bits, const location& locus,
		       visibility vis, bool is_anonymous)
  : type_or_decl_base(env,
		      UNION_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_TYPE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, name, locus,
	      // If the class is anonymous then by default it won't
	      // have a linkage name.  Also, the anonymous class does
	      // have an internal-only unique name that is generally
	      // not taken into account when comparing classes; such a
	      // unique internal-only name, when used as a linkage
	      // name might introduce spurious comparison false
	      // negatives.
	      /*linkage_name=*/is_anonymous ? string() : name,
	      vis),
    type_base(env, size_in_bits, 0),
    class_or_union(env, name, size_in_bits,
		   0, locus, vis)
{
  runtime_type_instance(this);
  set_is_anonymous(is_anonymous);
}

/// Constructor for the @ref union_decl type.
///
/// @param env the @ref environment we are operating from.
///
/// @param name the name of the union type.
///
/// @param is_declaration_only a boolean saying whether the instance
/// represents a declaration only, or not.
union_decl::union_decl(const environment* env,
		       const string& name,
		       bool is_declaration_only)
  : type_or_decl_base(env,
		      UNION_TYPE
		      | ABSTRACT_TYPE_BASE
		      | ABSTRACT_DECL_BASE
		      | ABSTRACT_SCOPE_TYPE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, name, location(), name),
    type_base(env, 0, 0),
    class_or_union(env, name, is_declaration_only)
{
  runtime_type_instance(this);
}

/// Getter of the pretty representation of the current instance of
/// @ref union_decl.
///
/// @param internal set to true if the call is intended for an
/// internal use (for technical use inside the library itself), false
/// otherwise.  If you don't know what this is for, then set it to
/// false.
///
/// @param qualified_name if true, names emitted in the pretty
/// representation are fully qualified.
///
/// @return the pretty representaion for a union_decl.
string
union_decl::get_pretty_representation(bool internal,
				      bool qualified_name) const
{
  string repr;
  if (get_is_anonymous())
    repr = get_class_or_union_flat_representation(this, "",
						  /*one_line=*/true,
						  internal);
  else
    {
      repr = "union ";
      if (qualified_name)
	repr += get_qualified_name(internal);
      else
	repr += get_name();
    }

  return repr;
}

/// Comparison operator for @ref union_decl.
///
/// @param other the instance of @ref union_decl to compare against.
///
/// @return true iff the current instance of @ref union_decl equals @p
/// other.
bool
union_decl::operator==(const decl_base& other) const
{
  const union_decl* op = dynamic_cast<const union_decl*>(&other);
  if (!op)
    return false;
  return try_canonical_compare(this, op);
}

/// Equality operator for union_decl.
///
/// Re-uses the equality operator that takes a decl_base.
///
/// @param other the other union_decl to compare against.
///
/// @return true iff the current instance equals the other one.
bool
union_decl::operator==(const type_base& other) const
{
  const decl_base *o = dynamic_cast<const decl_base*>(&other);
  if (!o)
    return false;
  return *this == *o;
}

/// Comparison operator for @ref union_decl.
///
/// @param other the instance of @ref union_decl to compare against.
///
/// @return true iff the current instance of @ref union_decl equals @p
/// other.
bool
union_decl::operator==(const union_decl& other) const
{
  const decl_base& o = other;
  return *this == o;
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance and on its
/// members.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
union_decl::traverse(ir_node_visitor& v)
{
  if (v.type_node_has_been_visited(this))
    return true;

  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      bool stop = false;

      if (!stop)
	for (data_members::const_iterator i = get_data_members().begin();
	     i != get_data_members().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }

      if (!stop)
	for (member_functions::const_iterator i= get_member_functions().begin();
	     i != get_member_functions().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }

      if (!stop)
	for (member_types::const_iterator i = get_member_types().begin();
	     i != get_member_types().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }

      if (!stop)
	for (member_function_templates::const_iterator i =
	       get_member_function_templates().begin();
	     i != get_member_function_templates().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }

      if (!stop)
	for (member_class_templates::const_iterator i =
	       get_member_class_templates().begin();
	     i != get_member_class_templates().end();
	     ++i)
	  if (!(*i)->traverse(v))
	    {
	      stop = true;
	      break;
	    }
      visiting(false);
    }

  bool result = v.visit_end(this);
  v.mark_type_node_as_visited(this);
  return result;
}

/// Destructor of the @ref union_decl type.
union_decl::~union_decl()
{}

/// Compares two instances of @ref union_decl.
///
/// If the two intances are different, set a bitfield to give some
/// insight about the kind of differences there are.
///
/// @param l the first artifact of the comparison.
///
/// @param r the second artifact of the comparison.
///
/// @param k a pointer to a bitfield that gives information about the
/// kind of changes there are between @p l and @p r.  This one is set
/// iff @p k is non-null and the function returns false.
///
/// Please note that setting k to a non-null value does have a
/// negative performance impact because even if @p l and @p r are not
/// equal, the function keeps up the comparison in order to determine
/// the different kinds of ways in which they are different.
///
/// @return true if @p l equals @p r, false otherwise.
bool
equals(const union_decl& l, const union_decl& r, change_kind* k)
{
  bool result = equals(static_cast<const class_or_union&>(l),
		       static_cast<const class_or_union&>(r),
		       k);
  if (result == true)
    maybe_propagate_canonical_type(l, r);
  return result;
}

/// Copy a method of a @ref union_decl into a new @ref
/// union_decl.
///
/// @param t the @ref union_decl into which the method is to be copied.
///
/// @param method the method to copy into @p t.
///
/// @return the resulting newly copied method.
method_decl_sptr
copy_member_function(const union_decl_sptr& union_type,
		     const method_decl_sptr& f)
{return copy_member_function(union_type, f.get());}

/// Copy a method of a @ref union_decl into a new @ref
/// union_decl.
///
/// @param t the @ref union_decl into which the method is to be copied.
///
/// @param method the method to copy into @p t.
///
/// @return the resulting newly copied method.
method_decl_sptr
copy_member_function(const union_decl_sptr& union_type,
		     const method_decl* f)
{
  const class_or_union_sptr t = union_type;
  return copy_member_function(t, f);
}

/// Turn equality of shared_ptr of union_decl into a deep equality;
/// that is, make it compare the pointed to objects too.
///
/// @param l the left-hand-side operand of the operator
///
/// @param r the right-hand-side operand of the operator.
///
/// @return true iff @p l equals @p r.
bool
operator==(const union_decl_sptr& l, const union_decl_sptr& r)
{
  if (l.get() == r.get())
    return true;
  if (!!l != !!r)
    return false;

  return *l == *r;
}

/// Turn inequality of shared_ptr of union_decl into a deep equality;
/// that is, make it compare the pointed to objects too.
///
/// @param l the left-hand-side operand of the operator
///
/// @param r the right-hand-side operand of the operator.
///
/// @return true iff @p l is different from @p r.
bool
operator!=(const union_decl_sptr& l, const union_decl_sptr& r)
{return !operator==(l, r);}
// </union_decl>

// <template_decl stuff>

/// Data type of the private data of the @template_decl type.
class template_decl::priv
{
  friend class template_decl;

  std::list<template_parameter_sptr> parms_;
public:

  priv()
  {}
}; // end class template_decl::priv

/// Add a new template parameter to the current instance of @ref
/// template_decl.
///
/// @param p the new template parameter to add.
void
template_decl::add_template_parameter(const template_parameter_sptr p)
{priv_->parms_.push_back(p);}

/// Get the list of template parameters of the current instance of
/// @ref template_decl.
///
/// @return the list of template parameters.
const std::list<template_parameter_sptr>&
template_decl::get_template_parameters() const
{return priv_->parms_;}

/// Constructor.
///
/// @param env the environment we are operating from.
///
/// @param name the name of the template decl.
///
/// @param locus the source location where the template declaration is
/// defined.
///
/// @param vis the visibility of the template declaration.
template_decl::template_decl(const environment* env,
			     const string& name,
			     const location& locus,
			     visibility vis)
  : type_or_decl_base(env, TEMPLATE_DECL | ABSTRACT_DECL_BASE),
    decl_base(env, name, locus, /*mangled_name=*/"", vis),
    priv_(new priv)
{
  runtime_type_instance(this);
}

/// Destructor.
template_decl::~template_decl()
{}

/// Equality operator.
///
/// @param o the other instance to compare against.
///
/// @return true iff @p equals the current instance.
bool
template_decl::operator==(const template_decl& o) const
{
  try
    {
      list<shared_ptr<template_parameter> >::const_iterator t0, t1;
      for (t0 = get_template_parameters().begin(),
	     t1 = o.get_template_parameters().begin();
	   (t0 != get_template_parameters().end()
	    && t1 != o.get_template_parameters().end());
	   ++t0, ++t1)
	{
	  if (**t0 != **t1)
	    return false;
	}

      if (t0 != get_template_parameters().end()
	  || t1 != o.get_template_parameters().end())
	return false;

      return true;
    }
  catch(...)
    {return false;}
}

// </template_decl stuff>

//<template_parameter>

/// The type of the private data of the @ref template_parameter type.
class template_parameter::priv
{
  friend class template_parameter;

  unsigned index_;
  template_decl_wptr template_decl_;
  mutable bool hashing_started_;
  mutable bool comparison_started_;

  priv();

public:

  priv(unsigned index, template_decl_sptr enclosing_template_decl)
    : index_(index),
      template_decl_(enclosing_template_decl),
      hashing_started_(),
      comparison_started_()
  {}
}; // end class template_parameter::priv

template_parameter::template_parameter(unsigned	 index,
				       template_decl_sptr enclosing_template)
  : priv_(new priv(index, enclosing_template))
  {}

unsigned
template_parameter::get_index() const
{return priv_->index_;}

const template_decl_sptr
template_parameter::get_enclosing_template_decl() const
{return priv_->template_decl_.lock();}

bool
template_parameter::get_hashing_has_started() const
{return priv_->hashing_started_;}

void
template_parameter::set_hashing_has_started(bool f) const
{priv_->hashing_started_ = f;}

bool
template_parameter::operator==(const template_parameter& o) const
{
  if (get_index() != o.get_index())
    return false;

  if (priv_->comparison_started_)
    return true;

  bool result = false;

  // Avoid inifite loops due to the fact that comparison the enclosing
  // template decl might lead to comparing this very same template
  // parameter with another one ...
  priv_->comparison_started_ = true;

  if (!!get_enclosing_template_decl() != !!o.get_enclosing_template_decl())
    ;
  else if (get_enclosing_template_decl()
	   && (*get_enclosing_template_decl()
	       != *o.get_enclosing_template_decl()))
    ;
  else
    result = true;

  priv_->comparison_started_ = false;

  return result;
}

/// Inequality operator.
///
/// @param other the other instance to compare against.
///
/// @return true iff the other instance is different from the current
/// one.
bool
template_parameter::operator!=(const template_parameter& other) const
{return !operator==(other);}

/// Destructor.
template_parameter::~template_parameter()
{}

/// The type of the private data of the @ref type_tparameter type.
class type_tparameter::priv
{
  friend class type_tparameter;
}; // end class type_tparameter::priv

/// Constructor of the @ref type_tparameter type.
///
/// @param index the index the type template parameter.
///
/// @param enclosing_tdecl the enclosing template declaration.
///
/// @param name the name of the template parameter.
///
/// @param locus the location of the declaration of this type template
/// parameter.
type_tparameter::type_tparameter(unsigned		index,
				 template_decl_sptr	enclosing_tdecl,
				 const string&		name,
				 const location&	locus)
  : type_or_decl_base(enclosing_tdecl->get_environment(),
		      ABSTRACT_DECL_BASE
		      | ABSTRACT_TYPE_BASE
		      | BASIC_TYPE),
    decl_base(enclosing_tdecl->get_environment(), name, locus),
    type_base(enclosing_tdecl->get_environment(), 0, 0),
    type_decl(enclosing_tdecl->get_environment(), name, 0, 0, locus),
    template_parameter(index, enclosing_tdecl),
    priv_(new priv)
{
  runtime_type_instance(this);
}

bool
type_tparameter::operator==(const type_base& other) const
{
  if (!type_decl::operator==(other))
    return false;

  try
    {
      const type_tparameter& o = dynamic_cast<const type_tparameter&>(other);
      return template_parameter::operator==(o);
    }
  catch (...)
    {return false;}
}

bool
type_tparameter::operator==(const template_parameter& other) const
{
  try
    {
      const type_base& o = dynamic_cast<const type_base&>(other);
      return *this == o;
    }
  catch(...)
    {return false;}
}

bool
type_tparameter::operator==(const type_tparameter& other) const
{return *this == static_cast<const type_base&>(other);}

type_tparameter::~type_tparameter()
{}

/// The type of the private data of the @ref non_type_tparameter type.
class non_type_tparameter::priv
{
  friend class non_type_tparameter;

  type_base_wptr type_;

  priv();

public:

  priv(type_base_sptr type)
    : type_(type)
  {}
}; // end class non_type_tparameter::priv

/// The constructor for the @ref non_type_tparameter type.
///
/// @param index the index of the template parameter.
///
/// @param enclosing_tdecl the enclosing template declaration that
/// holds this parameter parameter.
///
/// @param name the name of the template parameter.
///
/// @param type the type of the template parameter.
///
/// @param locus the location of the declaration of this template
/// parameter.
non_type_tparameter::non_type_tparameter(unsigned		index,
					 template_decl_sptr	enclosing_tdecl,
					 const string&		name,
					 type_base_sptr	type,
					 const location&	locus)
  : type_or_decl_base(type->get_environment(), ABSTRACT_DECL_BASE),
    decl_base(type->get_environment(), name, locus, ""),
    template_parameter(index, enclosing_tdecl),
    priv_(new priv(type))
{
  runtime_type_instance(this);
}

/// Getter for the type of the template parameter.
///
/// @return the type of the template parameter.
const type_base_sptr
non_type_tparameter::get_type() const
{return priv_->type_.lock();}

/// Get the hash value of the current instance.
///
/// @return the hash value.
size_t
non_type_tparameter::get_hash() const
{
  non_type_tparameter::hash hash_tparm;
  return hash_tparm(this);
}

bool
non_type_tparameter::operator==(const decl_base& other) const
{
  if (!decl_base::operator==(other))
    return false;

  try
    {
      const non_type_tparameter& o =
	dynamic_cast<const non_type_tparameter&>(other);
      return (template_parameter::operator==(o)
	      && get_type() == o.get_type());
    }
  catch(...)
    {return false;}
}

bool
non_type_tparameter::operator==(const template_parameter& other) const
{
  try
    {
      const decl_base& o = dynamic_cast<const decl_base&>(other);
      return *this == o;
    }
  catch(...)
    {return false;}
}

non_type_tparameter::~non_type_tparameter()
{}

// <template_tparameter stuff>

/// Type of the private data of the @ref template_tparameter type.
class template_tparameter::priv
{
}; //end class template_tparameter::priv

/// Constructor for the @ref template_tparameter.
///
/// @param index the index of the template parameter.
///
/// @param enclosing_tdecl the enclosing template declaration.
///
/// @param name the name of the template parameter.
///
/// @param locus the location of the declaration of the template
/// parameter.
template_tparameter::template_tparameter(unsigned		index,
					 template_decl_sptr	enclosing_tdecl,
					 const string&		name,
					 const location&	locus)
  : type_or_decl_base(enclosing_tdecl->get_environment(),
		      ABSTRACT_DECL_BASE
		      | ABSTRACT_TYPE_BASE
		      | BASIC_TYPE),
    decl_base(enclosing_tdecl->get_environment(), name, locus),
    type_base(enclosing_tdecl->get_environment(), 0, 0),
    type_decl(enclosing_tdecl->get_environment(), name,
	      0, 0, locus, name, VISIBILITY_DEFAULT),
    type_tparameter(index, enclosing_tdecl, name, locus),
    template_decl(enclosing_tdecl->get_environment(), name, locus),
    priv_(new priv)
{
  runtime_type_instance(this);
}

bool
template_tparameter::operator==(const type_base& other) const
{
  try
    {
      const template_tparameter& o =
	dynamic_cast<const template_tparameter&>(other);
      return (type_tparameter::operator==(o)
	      && template_decl::operator==(o));
    }
  catch(...)
    {return false;}
}

bool
template_tparameter::operator==(const template_parameter& o) const
{
  try
    {
      const template_tparameter& other =
	dynamic_cast<const template_tparameter&>(o);
      return *this == static_cast<const type_base&>(other);
    }
  catch(...)
    {return false;}
}

bool
template_tparameter::operator==(const template_decl& o) const
{
  try
    {
      const template_tparameter& other =
	dynamic_cast<const template_tparameter&>(o);
      return type_base::operator==(other);
    }
  catch(...)
    {return false;}
}

template_tparameter::~template_tparameter()
{}

// </template_tparameter stuff>

// <type_composition stuff>

/// The type of the private data of the @ref type_composition type.
class type_composition::priv
{
  friend class type_composition;

  type_base_wptr type_;

  // Forbid this.
  priv();

public:

  priv(type_base_wptr type)
    : type_(type)
  {}
}; //end class type_composition::priv

/// Constructor for the @ref type_composition type.
///
/// @param index the index of the template type composition.
///
/// @param tdecl the enclosing template parameter that owns the
/// composition.
///
/// @param t the resulting type.
type_composition::type_composition(unsigned		index,
				   template_decl_sptr	tdecl,
				   type_base_sptr	t)
  : type_or_decl_base(tdecl->get_environment(),
		      ABSTRACT_DECL_BASE),
    decl_base(tdecl->get_environment(), "", location()),
    template_parameter(index, tdecl),
    priv_(new priv(t))
{
  runtime_type_instance(this);
}

/// Getter for the resulting composed type.
///
/// @return the composed type.
const type_base_sptr
type_composition::get_composed_type() const
{return priv_->type_.lock();}

/// Setter for the resulting composed type.
///
/// @param t the composed type.
void
type_composition::set_composed_type(type_base_sptr t)
{priv_->type_ = t;}

/// Get the hash value for the current instance.
///
/// @return the hash value.
size_t
type_composition::get_hash() const
{
  type_composition::hash hash_type_composition;
  return hash_type_composition(this);
}

type_composition::~type_composition()
{}

// </type_composition stuff>

//</template_parameter stuff>

// <function_template>

class function_tdecl::priv
{
  friend class function_tdecl;

  function_decl_sptr pattern_;
  binding binding_;

  priv();

public:

  priv(function_decl_sptr pattern, binding bind)
    : pattern_(pattern), binding_(bind)
  {}

  priv(binding bind)
    : binding_(bind)
  {}
}; // end class function_tdecl::priv

/// Constructor for a function template declaration.
///
/// @param env the environment we are operating from.
///
/// @param locus the location of the declaration.
///
/// @param vis the visibility of the declaration.  This is the
/// visibility the functions instantiated from this template are going
/// to have.
///
/// @param bind the binding of the declaration.  This is the binding
/// the functions instantiated from this template are going to have.
function_tdecl::function_tdecl(const environment*	env,
			       const location&		locus,
			       visibility		vis,
			       binding			bind)
  : type_or_decl_base(env,
		      ABSTRACT_DECL_BASE
		      | TEMPLATE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, "", locus, "", vis),
    template_decl(env, "", locus, vis),
    scope_decl(env, "", locus),
    priv_(new priv(bind))
{
  runtime_type_instance(this);
}

/// Constructor for a function template declaration.
///
/// @param pattern the pattern of the template.
///
/// @param locus the location of the declaration.
///
/// @param vis the visibility of the declaration.  This is the
/// visibility the functions instantiated from this template are going
/// to have.
///
/// @param bind the binding of the declaration.  This is the binding
/// the functions instantiated from this template are going to have.
function_tdecl::function_tdecl(function_decl_sptr	pattern,
			       const location&		locus,
			       visibility		vis,
			       binding			bind)
  : type_or_decl_base(pattern->get_environment(),
		      ABSTRACT_DECL_BASE
		      | TEMPLATE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(pattern->get_environment(), pattern->get_name(), locus,
	      pattern->get_name(), vis),
    template_decl(pattern->get_environment(), pattern->get_name(), locus, vis),
    scope_decl(pattern->get_environment(), pattern->get_name(), locus),
    priv_(new priv(pattern, bind))
{
  runtime_type_instance(this);
}

/// Set a new pattern to the function template.
///
/// @param p the new pattern.
void
function_tdecl::set_pattern(function_decl_sptr p)
{
  priv_->pattern_ = p;
  add_decl_to_scope(p, this);
  set_name(p->get_name());
}

/// Get the pattern of the function template.
///
/// @return the pattern.
function_decl_sptr
function_tdecl::get_pattern() const
{return priv_->pattern_;}

/// Get the binding of the function template.
///
/// @return the binding
decl_base::binding
function_tdecl::get_binding() const
{return priv_->binding_;}

/// Comparison operator for the @ref function_tdecl type.
///
/// @param other the other instance of @ref function_tdecl to compare against.
///
/// @return true iff the two instance are equal.
bool
function_tdecl::operator==(const decl_base& other) const
{
  const function_tdecl* o = dynamic_cast<const function_tdecl*>(&other);
  if (o)
    return *this == *o;
  return false;
}

/// Comparison operator for the @ref function_tdecl type.
///
/// @param other the other instance of @ref function_tdecl to compare against.
///
/// @return true iff the two instance are equal.
bool
function_tdecl::operator==(const template_decl& other) const
{
  const function_tdecl* o = dynamic_cast<const function_tdecl*>(&other);
  if (o)
    return *this == *o;
  return false;
}

/// Comparison operator for the @ref function_tdecl type.
///
/// @param o the other instance of @ref function_tdecl to compare against.
///
/// @return true iff the two instance are equal.
bool
function_tdecl::operator==(const function_tdecl& o) const
{
  if (!(get_binding() == o.get_binding()
	&& template_decl::operator==(o)
	&& scope_decl::operator==(o)
	&& !!get_pattern() == !!o.get_pattern()))
    return false;

  if (get_pattern())
    return (*get_pattern() == *o.get_pattern());

  return true;
}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance and on the
/// function pattern of the template.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
function_tdecl::traverse(ir_node_visitor&v)
{
  if (visiting())
    return true;

  if (!v.visit_begin(this))
    {
      visiting(true);
      if (get_pattern())
	get_pattern()->traverse(v);
      visiting(false);
    }
  return v.visit_end(this);
}

function_tdecl::~function_tdecl()
{}

// </function_template>

// <class template>

/// Type of the private data of the the @ref class_tdecl type.
class class_tdecl::priv
{
  friend class class_tdecl;
  class_decl_sptr pattern_;

public:

  priv()
  {}

  priv(class_decl_sptr pattern)
    : pattern_(pattern)
  {}
}; // end class class_tdecl::priv

/// Constructor for the @ref class_tdecl type.
///
/// @param env the environment we are operating from.
///
/// @param locus the location of the declaration of the class_tdecl
/// type.
///
/// @param vis the visibility of the instance of class instantiated
/// from this template.
class_tdecl::class_tdecl(const environment*	env,
			 const location&	locus,
			 visibility		vis)
  : type_or_decl_base(env,
		      ABSTRACT_DECL_BASE
		      | TEMPLATE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(env, "", locus, "", vis),
    template_decl(env, "", locus, vis),
    scope_decl(env, "", locus),
    priv_(new priv)
{
  runtime_type_instance(this);
}

/// Constructor for the @ref class_tdecl type.
///
/// @param pattern The details of the class template. This must NOT be a
/// null pointer.  If you really this to be null, please use the
/// constructor above instead.
///
/// @param locus the source location of the declaration of the type.
///
/// @param vis the visibility of the instances of class instantiated
/// from this template.
class_tdecl::class_tdecl(class_decl_sptr	pattern,
			 const location&	locus,
			 visibility		vis)
  : type_or_decl_base(pattern->get_environment(),
		      ABSTRACT_DECL_BASE
		      | TEMPLATE_DECL
		      | ABSTRACT_SCOPE_DECL),
    decl_base(pattern->get_environment(), pattern->get_name(),
	      locus, pattern->get_name(), vis),
    template_decl(pattern->get_environment(), pattern->get_name(), locus, vis),
    scope_decl(pattern->get_environment(), pattern->get_name(), locus),
    priv_(new priv(pattern))
{
  runtime_type_instance(this);
}

/// Setter of the pattern of the template.
///
/// @param p the new template.
void
class_tdecl::set_pattern(class_decl_sptr p)
{
  priv_->pattern_ = p;
  add_decl_to_scope(p, this);
  set_name(p->get_name());
}

/// Getter of the pattern of the template.
///
/// @return p the new template.
class_decl_sptr
class_tdecl::get_pattern() const
{return priv_->pattern_;}

bool
class_tdecl::operator==(const decl_base& other) const
{
  try
    {
      const class_tdecl& o = dynamic_cast<const class_tdecl&>(other);

      if (!(template_decl::operator==(o)
	    && scope_decl::operator==(o)
	    && !!get_pattern() == !!o.get_pattern()))
	return false;

      if (!get_pattern() || !o.get_pattern())
	return true;

      return get_pattern()->decl_base::operator==(*o.get_pattern());
    }
  catch(...) {}
  return false;
}

bool
class_tdecl::operator==(const template_decl& other) const
{
  try
    {
      const class_tdecl& o = dynamic_cast<const class_tdecl&>(other);
      return *this == static_cast<const decl_base&>(o);
    }
  catch(...)
    {return false;}
}

bool
class_tdecl::operator==(const class_tdecl& o) const
{return *this == static_cast<const decl_base&>(o);}

/// This implements the ir_traversable_base::traverse pure virtual
/// function.
///
/// @param v the visitor used on the current instance and on the class
/// pattern of the template.
///
/// @return true if the entire IR node tree got traversed, false
/// otherwise.
bool
class_tdecl::traverse(ir_node_visitor&v)
{
  if (visiting())
    return true;

  if (v.visit_begin(this))
    {
      visiting(true);
      if (class_decl_sptr pattern = get_pattern())
	pattern->traverse(v);
      visiting(false);
    }
  return v.visit_end(this);
}

class_tdecl::~class_tdecl()
{}

/// This visitor checks if a given type as non-canonicalized sub
/// types.
class non_canonicalized_subtype_detector : public ir::ir_node_visitor
{
  type_base* type_;
  type_base* has_non_canonical_type_;

private:
  non_canonicalized_subtype_detector();

public:
  non_canonicalized_subtype_detector(type_base* type)
    : type_(type),
      has_non_canonical_type_()
  {}

  /// Return true if the visitor detected that there is a
  /// non-canonicalized sub-type.
  ///
  /// @return true if the visitor detected that there is a
  /// non-canonicalized sub-type.
  type_base*
  has_non_canonical_type() const
  {return has_non_canonical_type_;}

  /// The intent of this visitor handler is to avoid looking into
  /// sub-types of member functions of the type we are traversing.
  bool
  visit_begin(function_decl* f)
  {
    // Do not look at sub-types of non-virtual member functions.
    if (is_member_function(f)
	&& get_member_function_is_virtual(*f))
      return false;
    return true;
  }

  /// When visiting a sub-type, if it's *NOT* been canonicalized, set
  /// the 'has_non_canonical_type' flag.  And in any case, when
  /// visiting a sub-type, do not visit its children nodes.  So this
  /// function only goes to the level below the level of the top-most
  /// type.
  ///
  /// @return true if we are at the same level as the top-most type,
  /// otherwise return false.
  bool
  visit_begin(type_base* t)
  {
    if (t != type_)
      {
	if (!t->get_canonical_type())
	  // We are looking a sub-type of 'type_' which has no
	  // canonical type.  So tada! we found one!  Get out right
	  // now with the trophy.
	  has_non_canonical_type_ = t;

	return false;
      }
    return true;
  }

  /// When we are done visiting a sub-type, if it's been flagged as
  /// been non-canonicalized, then stop the traversing.
  ///
  /// Otherwise, keep going.
  ///
  /// @return false iff the sub-type that has been visited is
  /// non-canonicalized.
  bool
  visit_end(type_base* )
  {
    if (has_non_canonical_type_)
      return false;
    return true;
  }
}; //end class non_canonicalized_subtype_detector

/// Test if a type has sub-types that are non-canonicalized.
///
/// @param t the type which sub-types to consider.
///
/// @return true if a type has sub-types that are non-canonicalized.
type_base*
type_has_non_canonicalized_subtype(type_base_sptr t)
{
  if (!t)
    return 0;

  non_canonicalized_subtype_detector v(t.get());
  t->traverse(v);
  return v.has_non_canonical_type();
}

/// Tests if the change of a given type effectively comes from just
/// its sub-types.  That is, if the type has changed but its type name
/// hasn't changed, then the change of the type mostly likely is a
/// sub-type change.
///
/// @param t_v1 the first version of the type.
///
/// @param t_v2 the second version of the type.
///
/// @return true iff the type changed and the change is about its
/// sub-types.
bool
type_has_sub_type_changes(const type_base_sptr t_v1,
			  const type_base_sptr t_v2)
{
  type_base_sptr t1 = strip_typedef(t_v1);
  type_base_sptr t2 = strip_typedef(t_v2);

  string repr1 = get_pretty_representation(t1),
    repr2 = get_pretty_representation(t2);
  return (t1 != t2 && repr1 == repr2);
}

/// Make sure that the life time of a given (smart pointer to a) type
/// is the same as the life time of the libabigail library.
///
/// @param t the type to consider.
void
keep_type_alive(type_base_sptr t)
{
  environment* env = t->get_environment();
  ABG_ASSERT(env);
  env->priv_->extra_live_types_.push_back(t);
}

/// Hash an ABI artifact that is either a type or a decl.
///
/// This function intends to provides the fastest possible hashing for
/// types and decls, while being completely correct.
///
/// Note that if the artifact is a type and if it has a canonical
/// type, the hash value is going to be the pointer value of the
/// canonical type.  Otherwise, this function computes a hash value
/// for the type by recursively walking the type members.  This last
/// code path is possibly *very* slow and should only be used when
/// only handful of types are going to be hashed.
///
/// If the artifact is a decl, then a combination of the hash of its
/// type and the hash of the other properties of the decl is computed.
///
/// @param tod the type or decl to hash.
///
/// @return the resulting hash value.
size_t
hash_type_or_decl(const type_or_decl_base *tod)
{
  size_t result = 0;

  if (tod == 0)
    ;
  else if (const type_base* t = is_type(tod))
    result = hash_type(t);
  else if (const decl_base* d = is_decl(tod))
    {
      if (var_decl* v = is_var_decl(d))
	{
	  ABG_ASSERT(v->get_type());
	  size_t h = hash_type_or_decl(v->get_type());
	  string repr = v->get_pretty_representation();
	  abg_compat::hash<string> hash_string;
	  h = hashing::combine_hashes(h, hash_string(repr));
	  result = h;
	}
      else if (function_decl* f = is_function_decl(d))
	{
	  ABG_ASSERT(f->get_type());
	  size_t h = hash_type_or_decl(f->get_type());
	  string repr = f->get_pretty_representation();
	  abg_compat::hash<string> hash_string;
	  h = hashing::combine_hashes(h, hash_string(repr));
	  result = h;
	}
      else if (function_decl::parameter* p = is_function_parameter(d))
	{
	  type_base_sptr parm_type = p->get_type();
	  ABG_ASSERT(parm_type);
	  abg_compat::hash<bool> hash_bool;
	  abg_compat::hash<unsigned> hash_unsigned;
	  size_t h = hash_type_or_decl(parm_type);
	  h = hashing::combine_hashes(h, hash_unsigned(p->get_index()));
	  h = hashing::combine_hashes(h, hash_bool(p->get_variadic_marker()));
	  result = h;
	}
      else if (class_decl::base_spec *bs = is_class_base_spec(d))
	{
	  member_base::hash hash_member;
	  abg_compat::hash<size_t> hash_size;
	  abg_compat::hash<bool> hash_bool;
	  type_base_sptr type = bs->get_base_class();
	  size_t h = hash_type_or_decl(type);
	  h = hashing::combine_hashes(h, hash_member(*bs));
	  h = hashing::combine_hashes(h, hash_size(bs->get_offset_in_bits()));
	  h = hashing::combine_hashes(h, hash_bool(bs->get_is_virtual()));
	  result = h;
	}
      else
	// This is a *really* *SLOW* path.  If it shows up in a
	// performance profile, I bet it'd be a good idea to try to
	// avoid it altogether.
	result = d->get_hash();
    }
  else
    // We should never get here.
    abort();
  return result;
}

/// Hash an ABI artifact that is either a type.
///
/// This function intends to provides the fastest possible hashing for
/// types while being completely correct.
///
/// Note that if the type artifact has a canonical type, the hash
/// value is going to be the pointer value of the canonical type.
/// Otherwise, this function computes a hash value for the type by
/// recursively walking the type members.  This last code path is
/// possibly *very* slow and should only be used when only handful of
/// types are going to be hashed.
///
/// @param t the type or decl to hash.
///
/// @return the resulting hash value.
size_t
hash_type(const type_base *t)
{return hash_as_canonical_type_or_constant(t);}

/// Hash an ABI artifact that is either a type of a decl.
///
/// @param tod the ABI artifact to hash.
///
/// @return the hash value of the ABI artifact.
size_t
hash_type_or_decl(const type_or_decl_base_sptr& tod)
{return hash_type_or_decl(tod.get());}

/// Hash a type by either returning the pointer value of its canonical
/// type or by returning a constant if the type doesn't have a
/// canonical type.
///
/// This is a subroutine of hash_type.
///
/// @param t the type to consider.
///
/// @return the hash value.
static size_t
hash_as_canonical_type_or_constant(const type_base *t)
{
  type_base *canonical_type = 0;

  if (t)
    canonical_type = t->get_naked_canonical_type();

  if (!canonical_type)
    {
      // If the type doesn't have a canonical type, maybe it's because
      // it's a declaration-only type?  If that's the case, let's try
      // to get the canonical type of the definition of this
      // declaration.
      decl_base *decl = is_decl(t);
      if (decl
	  && decl->get_is_declaration_only()
	  && decl->get_naked_definition_of_declaration())
	{
	  type_base *definition =
	    is_type(decl->get_naked_definition_of_declaration());
	  ABG_ASSERT(definition);
	  canonical_type = definition->get_naked_canonical_type();
	}
    }

  if (canonical_type)
    return reinterpret_cast<size_t>(canonical_type);

  // If we reached this point, it means we are seeing a
  // non-canonicalized type.  It must be a decl-only class;
  // otherwise it means that for some weird reason, the type hasn't
  // been canonicalized.  It should be!
  ABG_ASSERT(is_declaration_only_class_or_union_type(t));

  return 0xDEADBABE;
}

/// Test if the pretty representation of a given @ref function_decl is
/// lexicographically less then the pretty representation of another
/// @ref function_decl.
///
/// @param f the first @ref function_decl to consider for comparison.
///
/// @param s the second @ref function_decl to consider for comparison.
///
/// @return true iff the pretty representation of @p f is
/// lexicographically less than the pretty representation of @p s.
bool
function_decl_is_less_than(const function_decl &f, const function_decl &s)
{
  string fr = f.get_pretty_representation_of_declarator(),
    sr = s.get_pretty_representation_of_declarator();

  if (fr != sr)
    return fr < sr;

  fr = f.get_pretty_representation(),
    sr = s.get_pretty_representation();

  if (fr != sr)
    return fr < sr;

  if (f.get_symbol())
    fr = f.get_symbol()->get_id_string();
  else if (!f.get_linkage_name().empty())
    fr = f.get_linkage_name();

  if (s.get_symbol())
    sr = s.get_symbol()->get_id_string();
  else if (!s.get_linkage_name().empty())
    sr = s.get_linkage_name();

  return fr < sr;
}

/// Test if two types have similar structures, even though they are
/// (or can be) different.
///
/// const and volatile qualifiers are completely ignored.
///
/// typedef are resolved to their definitions; their names are ignored.
///
/// Two indirect types (pointers or references) have similar structure
/// if their underlying types are of the same kind and have the same
/// name.  In the indirect types case, the size of the underlying type
/// does not matter.
///
/// Two direct types (i.e, non indirect) have a similar structure if
/// they have the same kind, name and size.  Two class types have
/// similar structure if they have the same name, size, and if the
/// types of their data members have similar types.
///
/// @param first the first type to consider.
///
/// @param second the second type to consider.
///
/// @param indirect_type whether to do an indirect comparison
///
/// @return true iff @p first and @p second have similar structures.
bool
types_have_similar_structure(const type_base_sptr& first,
			     const type_base_sptr& second,
			     bool indirect_type)
{return types_have_similar_structure(first.get(), second.get(), indirect_type);}

/// Test if two types have similar structures, even though they are
/// (or can be) different.
///
/// const and volatile qualifiers are completely ignored.
///
/// typedef are resolved to their definitions; their names are ignored.
///
/// Two indirect types (pointers or references) have similar structure
/// if their underlying types are of the same kind and have the same
/// name.  In the indirect types case, the size of the underlying type
/// does not matter.
///
/// Two direct types (i.e, non indirect) have a similar structure if
/// they have the same kind, name and size.  Two class types have
/// similar structure if they have the same name, size, and if the
/// types of their data members have similar types.
///
/// @param first the first type to consider.
///
/// @param second the second type to consider.
///
/// @param indirect_type whether to do an indirect comparison
///
/// @return true iff @p first and @p second have similar structures.
bool
types_have_similar_structure(const type_base* first,
			     const type_base* second,
			     bool indirect_type)
{
  if (!!first != !!second)
    return false;

  if (!first)
    return false;

  // Treat typedefs purely as type aliases and ignore CV-qualifiers.
  first = peel_qualified_or_typedef_type(first);
  second = peel_qualified_or_typedef_type(second);

  // Eliminate all but N of the N^2 comparison cases. This also guarantees the
  // various ty2 below cannot be null.
  if (typeid(*first) != typeid(*second))
    return false;

  // Peel off matching pointers.
  if (const pointer_type_def* ty1 = is_pointer_type(first))
    {
      const pointer_type_def* ty2 = is_pointer_type(second);
      return types_have_similar_structure(ty1->get_pointed_to_type(),
					  ty2->get_pointed_to_type(),
					  true);
    }

  // Peel off matching references.
  if (const reference_type_def* ty1 = is_reference_type(first))
    {
      const reference_type_def* ty2 = is_reference_type(second);
      if (ty1->is_lvalue() != ty2->is_lvalue())
	return false;
      return types_have_similar_structure(ty1->get_pointed_to_type(),
					  ty2->get_pointed_to_type(),
					  true);
    }

  if (const type_decl* ty1 = is_type_decl(first))
    {
      const type_decl* ty2 = is_type_decl(second);
      if (!indirect_type)
	if (ty1->get_size_in_bits() != ty2->get_size_in_bits())
	  return false;

      return ty1->get_name() == ty2->get_name();
    }

  if (const enum_type_decl* ty1 = is_enum_type(first))
    {
      const enum_type_decl* ty2 = is_enum_type(second);
      if (!indirect_type)
	if (ty1->get_size_in_bits() != ty2->get_size_in_bits())
	  return false;

      return (get_name(ty1->get_underlying_type())
	      == get_name(ty2->get_underlying_type()));
    }

  if (const class_decl* ty1 = is_class_type(first))
    {
      const class_decl* ty2 = is_class_type(second);
      if (!ty1->get_is_anonymous() && !ty2->get_is_anonymous()
	  && ty1->get_name() != ty2->get_name())
	return false;

      if (!indirect_type)
	{
	  if ((ty1->get_size_in_bits() != ty2->get_size_in_bits())
	      || (ty1->get_non_static_data_members().size()
		  != ty2->get_non_static_data_members().size()))
	    return false;

	  for (class_or_union::data_members::const_iterator
		 i = ty1->get_non_static_data_members().begin(),
		 j = ty2->get_non_static_data_members().begin();
	       (i != ty1->get_non_static_data_members().end()
		&& j != ty2->get_non_static_data_members().end());
	       ++i, ++j)
	    {
	      var_decl_sptr dm1 = *i;
	      var_decl_sptr dm2 = *j;
	      if (!types_have_similar_structure(dm1->get_type().get(),
						dm2->get_type().get(),
						indirect_type))
		return false;
	    }
	}

      return true;
    }

  if (const union_decl* ty1 = is_union_type(first))
    {
      const union_decl* ty2 = is_union_type(second);
      if (!ty1->get_is_anonymous() && !ty2->get_is_anonymous()
	  && ty1->get_name() != ty2->get_name())
	return false;

      if (!indirect_type)
	return ty1->get_size_in_bits() == ty2->get_size_in_bits();

      return true;
    }

  if (const array_type_def* ty1 = is_array_type(first))
    {
      const array_type_def* ty2 = is_array_type(second);
      // TODO: Handle int[5][2] vs int[2][5] better.
      if (ty1->get_size_in_bits() != ty2->get_size_in_bits()
	  || ty1->get_dimension_count() != ty2->get_dimension_count()
	  || !types_have_similar_structure(ty1->get_element_type(),
					   ty2->get_element_type(),
					   indirect_type))
	return false;

      return true;
    }

  if (const array_type_def::subrange_type *ty1 = is_subrange_type(first))
    {
      const array_type_def::subrange_type *ty2 = is_subrange_type(second);
      if (ty1->get_upper_bound() != ty2->get_upper_bound()
	  || ty1->get_lower_bound() != ty2->get_lower_bound()
	  || ty1->get_language() != ty2->get_language()
	  || !types_have_similar_structure(ty1->get_underlying_type(),
					   ty2->get_underlying_type(),
					   indirect_type))
	return false;

      return true;
    }

  if (const function_type* ty1 = is_function_type(first))
    {
      const function_type* ty2 = is_function_type(second);
      if (!types_have_similar_structure(ty1->get_return_type(),
					ty2->get_return_type(),
					indirect_type))
	return false;

      if (ty1->get_parameters().size() != ty2->get_parameters().size())
	return false;

      for (function_type::parameters::const_iterator
	     i = ty1->get_parameters().begin(),
	     j = ty2->get_parameters().begin();
	   (i != ty1->get_parameters().end()
	    && j != ty2->get_parameters().end());
	   ++i, ++j)
	if (!types_have_similar_structure((*i)->get_type(),
					  (*j)->get_type(),
					  indirect_type))
	  return false;

      return true;
    }

  // All kinds of type should have been handled at this point.
  ABG_ASSERT_NOT_REACHED;

  return false;
}

/// Look for a data member of a given class, struct or union type and
/// return it.
///
/// The data member is designated by its name.
///
/// @param type the class, struct or union type to consider.
///
/// @param dm_name the name of the data member to lookup.
///
/// @return the data member iff it was found in @type or NULL if no
/// data member with that name was found.
const var_decl*
lookup_data_member(const type_base* type,
		   const char* dm_name)

{
  class_or_union *cou = is_class_or_union_type(type);
  if (!cou)
    return 0;

  return cou->find_data_member(dm_name).get();
}

/// Get the function parameter designated by its index.
///
/// Note that the first function parameter has index 0.
///
/// @param fun the function to consider.
///
/// @param parm_index the index of the function parameter to get.
///
/// @return the function parameter designated by its index, of NULL if
/// no function parameter with that index was found.
const function_decl::parameter*
get_function_parameter(const decl_base* fun,
		       unsigned parm_index)
{
  function_decl* fn = is_function_decl(fun);
  if (!fn)
    return 0;

  const function_decl::parameters &parms = fn->get_type()->get_parameters();
  if (parms.size() <= parm_index)
    return 0;

  return parms[parm_index].get();
}

bool
ir_traversable_base::traverse(ir_node_visitor&)
{return true;}

// <ir_node_visitor stuff>

/// The private data structure of the ir_node_visitor type.
struct ir_node_visitor::priv
{
  pointer_set visited_ir_nodes;
  bool allow_visiting_already_visited_type_node;

  priv()
    : allow_visiting_already_visited_type_node(true)
  {}
}; // end struct ir_node_visitory::priv

/// Default Constructor of the ir_node_visitor type.
ir_node_visitor::ir_node_visitor()
  : priv_(new priv)
{}

/// Set if the walker using this visitor is allowed to re-visit a type
/// node that was previously visited or not.
///
/// @param f if true, then the walker using this visitor is allowed to
/// re-visit a type node that was previously visited.
void
ir_node_visitor::allow_visiting_already_visited_type_node(bool f)
{priv_->allow_visiting_already_visited_type_node = f;}

/// Get if the walker using this visitor is allowed to re-visit a type
/// node that was previously visited or not.
///
/// @return true iff the walker using this visitor is allowed to
/// re-visit a type node that was previously visited.
bool
ir_node_visitor::allow_visiting_already_visited_type_node() const
{return priv_->allow_visiting_already_visited_type_node;}

/// Mark a given type node as having been visited.
///
/// Note that for this function to work, the type node must have been
/// canonicalized.  Otherwise the process is aborted.
///
/// @param p the type to mark as having been visited.
void
ir_node_visitor::mark_type_node_as_visited(type_base *p)
{
  if (allow_visiting_already_visited_type_node())
    return;

  if (p == 0 || type_node_has_been_visited(p))
    return;

  type_base* canonical_type = p->get_naked_canonical_type();
  ABG_ASSERT(canonical_type);

  size_t canonical_ptr_value = reinterpret_cast<size_t>(canonical_type);
  priv_->visited_ir_nodes.insert(canonical_ptr_value);
}

/// Un-mark all visited type nodes.
///
/// That is, no type node is going to be considered as having been
/// visited anymore.
///
/// In other words, after invoking this funciton,
/// ir_node_visitor::type_node_has_been_visited() is going to return
/// false on all type nodes.
void
ir_node_visitor::forget_visited_type_nodes()
{priv_->visited_ir_nodes.clear();}

/// Test if a given type node has been marked as visited.
///
/// @param p the type node to consider.
///
/// @return true iff the type node @p p has been marked as visited by
/// the function ir_node_visitor::mark_type_node_as_visited.
bool
ir_node_visitor::type_node_has_been_visited(type_base* p) const
{
  if (allow_visiting_already_visited_type_node())
    return false;

  if (p == 0)
    return false;

  type_base *canonical_type = p->get_naked_canonical_type();
  ABG_ASSERT(canonical_type);

  size_t ptr_value = reinterpret_cast<size_t>(canonical_type);
  pointer_set::iterator it = priv_->visited_ir_nodes.find(ptr_value);
  if (it == priv_->visited_ir_nodes.end())
    return false;

  return true;
}

bool
ir_node_visitor::visit_begin(decl_base*)
{return true;}

bool
ir_node_visitor::visit_end(decl_base*)
{return true;}

bool
ir_node_visitor::visit_begin(scope_decl*)
{return true;}

bool
ir_node_visitor::visit_end(scope_decl*)
{return true;}

bool
ir_node_visitor::visit_begin(type_base*)
{return true;}

bool
ir_node_visitor::visit_end(type_base*)
{return true;}

bool
ir_node_visitor::visit_begin(scope_type_decl* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(scope_type_decl* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(type_decl* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(type_decl* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(namespace_decl* d)
{return visit_begin(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_end(namespace_decl* d)
{return visit_end(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_begin(qualified_type_def* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(qualified_type_def* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(pointer_type_def* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(pointer_type_def* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(reference_type_def* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(reference_type_def* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(array_type_def* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(array_type_def* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(array_type_def::subrange_type* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(array_type_def::subrange_type* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(enum_type_decl* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(enum_type_decl* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(typedef_decl* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(typedef_decl* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(function_type* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(function_type* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(var_decl* d)
{return visit_begin(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_end(var_decl* d)
{return visit_end(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_begin(function_decl* d)
{return visit_begin(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_end(function_decl* d)
{return visit_end(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_begin(function_decl::parameter* d)
{return visit_begin(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_end(function_decl::parameter* d)
{return visit_end(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_begin(function_tdecl* d)
{return visit_begin(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_end(function_tdecl* d)
{return visit_end(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_begin(class_tdecl* d)
{return visit_begin(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_end(class_tdecl* d)
{return visit_end(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_begin(class_or_union* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(class_or_union* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(class_decl* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(class_decl* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(union_decl* t)
{return visit_begin(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_end(union_decl* t)
{return visit_end(static_cast<type_base*>(t));}

bool
ir_node_visitor::visit_begin(class_decl::base_spec* d)
{return visit_begin(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_end(class_decl::base_spec* d)
{return visit_end(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_begin(member_function_template* d)
{return visit_begin(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_end(member_function_template* d)
{return visit_end(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_begin(member_class_template* d)
{return visit_begin(static_cast<decl_base*>(d));}

bool
ir_node_visitor::visit_end(member_class_template* d)
{return visit_end(static_cast<decl_base*>(d));}

// </ir_node_visitor stuff>

// <debugging facilities>

/// Generate a different string at each invocation.
///
/// @return the resulting string.
static string
get_next_string()
{
  static __thread size_t counter;
  ++counter;
  std::ostringstream o;
  o << counter;
  return o.str();
}

/// Convenience typedef for a hash map of pointer to function_decl and
/// string.
typedef unordered_map<const function_decl*, string,
		      function_decl::hash,
		      function_decl::ptr_equal> fns_to_str_map_type;

/// Return a string associated to a given function.  Two functions
/// that compare equal would yield the same string, as far as this
/// routine is concerned.  And two functions that are different would
/// yield different strings.
///
/// This is used to debug core diffing issues on functions.  The
/// sequence of strings can be given to the 'testdiff2' program that
/// is in the tests/ directory of the source tree, to reproduce core
/// diffing issues on string and thus ease the debugging.
///
/// @param fn the function to generate a string for.
///
/// @param m the function_decl* <-> string map to be used by this
/// function to generate strings associated to a function.
///
/// @return the resulting string.
static const string&
fn_to_str(const function_decl* fn,
	  fns_to_str_map_type& m)
{
  fns_to_str_map_type::const_iterator i = m.find(fn);
  if (i != m.end())
    return i->second;
  string s = get_next_string();
  return m[fn]= s;
}

/// Generate a sequence of string that matches a given sequence of
/// function.  In the resulting sequence, each function is "uniquely
/// representated" by a string.  For instance, if the same function "foo"
/// appears at indexes 1 and 3, then the same string 'schmurf' (okay,
/// we don't care about the actual string) would appear at index 1 and 3.
///
/// @param begin the beginning of the sequence of functions to consider.
///
/// @param end the end of the sequence of functions.  This points to
/// one-passed-the-end of the actual sequence.
///
/// @param m the function_decl* <-> string map to be used by this
/// function to generate strings associated to a function.
///
/// @param o the output stream where to emit the generated list of
/// strings to.
static void
fns_to_str(vector<function_decl*>::const_iterator begin,
	   vector<function_decl*>::const_iterator end,
	   fns_to_str_map_type& m,
	   std::ostream& o)
{
  vector<function_decl*>::const_iterator i;
  for (i = begin; i != end; ++i)
    o << "'" << fn_to_str(*i, m) << "' ";
}

/// For each sequence of functions given in argument, generate a
/// sequence of string that matches a given sequence of function.  In
/// the resulting sequence, each function is "uniquely representated"
/// by a string.  For instance, if the same function "foo" appears at
/// indexes 1 and 3, then the same string 'schmurf' (okay, we don't
/// care about the actual string) would appear at index 1 and 3.
///
/// @param a_begin the beginning of the sequence of functions to consider.
///
/// @param a_end the end of the sequence of functions.  This points to
/// one-passed-the-end of the actual sequence.
///
/// @param b_begin the beginning of the second sequence of functions
/// to consider.
///
/// @param b_end the end of the second sequence of functions.
///
/// @param m the function_decl* <-> string map to be used by this
/// function to generate strings associated to a function.
///
/// @param o the output stream where to emit the generated list of
/// strings to.
static void
fns_to_str(vector<function_decl*>::const_iterator a_begin,
	   vector<function_decl*>::const_iterator a_end,
	   vector<function_decl*>::const_iterator b_begin,
	   vector<function_decl*>::const_iterator b_end,
	   fns_to_str_map_type& m,
	   std::ostream& o)
{
  fns_to_str(a_begin, a_end, m, o);
  o << "->|<- ";
  fns_to_str(b_begin, b_end, m, o);
  o << "\n";
}

/// For each sequence of functions given in argument, generate a
/// sequence of string that matches a given sequence of function.  In
/// the resulting sequence, each function is "uniquely representated"
/// by a string.  For instance, if the same function "foo" appears at
/// indexes 1 and 3, then the same string 'schmurf' (okay, we don't
/// care about the actual string) would appear at index 1 and 3.
///
/// @param a_begin the beginning of the sequence of functions to consider.
///
/// @param a_end the end of the sequence of functions.  This points to
/// one-passed-the-end of the actual sequence.
///
/// @param b_begin the beginning of the second sequence of functions
/// to consider.
///
/// @param b_end the end of the second sequence of functions.
///
/// @param o the output stream where to emit the generated list of
/// strings to.
void
fns_to_str(vector<function_decl*>::const_iterator a_begin,
	   vector<function_decl*>::const_iterator a_end,
	   vector<function_decl*>::const_iterator b_begin,
	   vector<function_decl*>::const_iterator b_end,
	   std::ostream& o)
{
  fns_to_str_map_type m;
  fns_to_str(a_begin, a_end, b_begin, b_end, m, o);
}

// </debugging facilities>

// </class template>

}// end namespace ir
}//end namespace abigail

namespace
{

/// Update the qualified parent name, qualified name and scoped name
/// of a tree decl node.
///
/// @return true if the tree walking should continue, false otherwise.
///
/// @param d the tree node to take in account.
bool
qualified_name_setter::do_update(abigail::ir::decl_base* d)
{
  std::string parent_qualified_name;
  abigail::ir::scope_decl* parent = d->get_scope();
  if (parent)
    d->priv_->qualified_parent_name_ = parent->get_qualified_name();
  else
    d->priv_->qualified_parent_name_ = abigail::interned_string();

  abigail::environment* env = d->get_environment();
  ABG_ASSERT(env);
  if (!d->priv_->qualified_parent_name_.empty())
    {
      if (d->get_name().empty())
	d->priv_->qualified_name_ = abigail::interned_string();
      else
	d->priv_->qualified_name_ =
	  env->intern(d->priv_->qualified_parent_name_ + "::" + d->get_name());
    }

  if (d->priv_->scoped_name_.empty())
    {
      if (parent
	  && !parent->get_is_anonymous()
	  && !parent->get_name().empty())
	d->priv_->scoped_name_ =
	  env->intern(parent->get_name() + "::" + d->get_name());
      else
	d->priv_->scoped_name_ =
	  env->intern(d->get_name());
    }

  if (!is_scope_decl(d))
    return false;

  return true;
}

/// This is called when we start visiting a decl node, during the
/// udpate of the qualified name of a given sub-tree.
///
/// @param d the decl node we are visiting.
///
/// @return true iff the traversal should keep going.
bool
qualified_name_setter::visit_begin(abigail::ir::decl_base* d)
{return do_update(d);}

/// This is called when we start visiting a type node, during the
/// udpate of the qualified name of a given sub-tree.
///
/// @param d the decl node we are visiting.
///
/// @return true iff the traversal should keep going.
bool
qualified_name_setter::visit_begin(abigail::ir::type_base* t)
{
  if (abigail::ir::decl_base* d = get_type_declaration(t))
    return do_update(d);
  return false;
}
}// end anonymous namespace.
