// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2020 Red Hat, Inc.
//
// Author: Dodji Seketeli

/// @file
///
/// Types of the main internal representation of libabigail.
///
/// This internal representation abstracts the artifacts that make up
/// an application binary interface.

#ifndef __ABG_IR_H__
#define __ABG_IR_H__

#include <assert.h>
#include <stdint.h>
#include <cstdlib>
#include <functional>
#include <set>
#include <unordered_map>
#include "abg-fwd.h"
#include "abg-hash.h"
#include "abg-traverse.h"
#include "abg-config.h"

/// @file
///
/// This file contains the declarations of the Internal Representation
/// of libabigail.

/// @defgroup Memory Memory management
/// @{
///
/// How objects' lifetime is handled in libabigail.
///
/// For memory management and garbage collection of libabigail's IR
/// artifacts, we use std::shared_ptr and std::weak_ptr.
///
/// When manipulating these IR artifacts, there are a few rules to keep in
/// mind.
///
/// <b>The declaration for a type is owned by only one scope </b>
///
/// This means that for each instance of abigail::type_base (a type) there
/// is an instance of abigail::scope_decl that owns a @ref
/// abigail::decl_base_sptr (a shared pointer to an abigail::decl_base)
/// that points to the declaration of that type.  The
/// abigail::type_base_sptr is added to the scope using the function
/// abigail::add_decl_to_scope().
///
/// There is a kind of type that is usually not syntactically owned by
/// a scope: it's function type.  In libabigail, function types are
/// represented by abigail::function_type and abigail::method_type.
/// These types must be owned by the translation unit they originate
/// from.  Adding them to the translation unit must be done by a call
/// to the method function
/// abigail::translation::bind_function_type_life_time().
///
/// <b> A declaration that has a type does NOT own the type </b>
///
/// This means that, for instance, in an abigail::var_decl (a variable
/// declaration), the type of the declaration is not owned by the
/// declaration.  In other (concrete) words, the variable declaration
/// doesn't have a shared pointer to the type.  Rather, it has a *weak*
/// pointer to its type.  That means that it has a data member of type
/// abigail::type_base_wptr that contains the type of the declaration.
///
/// But then abigail::var_decl::get_type() returns a shared pointer that
/// is constructed from the internal weak pointer to the type.  That way,
/// users of the type of the var can own a temporary reference on it and
/// be assured that the type's life time is long enough for their need.
///
/// Likewise, data members, function and template parameters similarly
/// have weak pointers on their type.
///
/// If, for a reason, you really need to keep a type alive for the
/// entire lifetime of the type system, then you can bind the life
/// time of that type to the life time of the @ref environment that is
/// supposed to outlive the type system.  You do that by passing the
/// type to the function environment::keep_type_alive().
///
/// @}

namespace abigail
{

/// The namespace of the internal representation of ABI artifacts like
/// types and decls.
namespace ir
{

// Inject some std types in here.
using std::unordered_map;

/// A convenience typedef for an unordered set of pointer values
typedef unordered_set<uintptr_t> pointer_set;

/// Functor to hash a canonical type by using its pointer value.
struct canonical_type_hash
{
  size_t operator()(const type_base_sptr& l) const;
  size_t operator()(const type_base *l) const;
}; //end struct canonical_type_hash

/// Helper typedef for an unordered set of type_base_sptr which uses
/// pointer value to tell its members appart, because the members are
/// canonical types.
typedef unordered_set<type_base_sptr,
		      canonical_type_hash> canonical_type_sptr_set_type;

/// Helper typedef for a vector of pointer to type_base.
typedef vector<type_base*> type_base_ptrs_type;

/// Helper typedef for a vector of shared pointer to a type_base.
typedef vector<type_base_sptr> type_base_sptrs_type;

/// This is an abstraction of the set of resources necessary to manage
/// several aspects of the internal representations of the Abigail
/// library.
///
/// An environment can be seen as the boundaries in which all related
/// Abigail artifacts live.  So before doing anything using this
/// library, the first thing to create is, well, you know it now, an
/// environment.
///
/// Note that the lifetime of environment objects must be longer than
/// the lifetime of any other type in the Abigail system.  So a given
/// instance of @ref environment must stay around as long as you are
/// using libabigail.  It's only when you are done using the library
/// that you can de-allocate the environment instance.
class environment
{
public:
  struct priv;
  std::unique_ptr<priv> priv_;

  /// A convenience typedef for a map of canonical types.  The key is
  /// the pretty representation string of a particular type and the
  /// value is the vector of canonical types that have the same pretty
  /// representation string.
  typedef std::unordered_map<string, std::vector<type_base_sptr> >
      canonical_types_map_type;

  environment();

  virtual ~environment();

  canonical_types_map_type&
  get_canonical_types_map();

  const canonical_types_map_type&
  get_canonical_types_map() const;

  const type_base_sptr&
  get_void_type() const;

  const type_base_sptr&
  get_variadic_parameter_type() const;

  bool
  canonicalization_is_done() const;

  void
  canonicalization_is_done(bool);

  bool
  do_on_the_fly_canonicalization() const;

  void
  do_on_the_fly_canonicalization(bool f);

  bool
  decl_only_class_equals_definition() const;

  void
  decl_only_class_equals_definition(bool f) const;

  bool
  use_enum_binary_only_equality() const;

  void
  use_enum_binary_only_equality(bool f) const;

  bool
  is_void_type(const type_base_sptr&) const;

  bool
  is_void_type(const type_base*) const;

  bool
  is_variadic_parameter_type(const type_base*) const;

  bool
  is_variadic_parameter_type(const type_base_sptr&) const;

  interned_string
  intern(const string&) const;

  const config&
  get_config() const;

#ifdef WITH_DEBUG_SELF_COMPARISON
  void
  set_self_comparison_debug_input(const corpus_sptr& corpus);

  void
  get_self_comparison_debug_inputs(corpus_sptr& first_corpus,
				   corpus_sptr& second_corpus);

  void
  self_comparison_debug_is_on(bool);

  bool
  self_comparison_debug_is_on() const;
#endif

  vector<type_base_sptr>* get_canonical_types(const char* name);

  type_base* get_canonical_type(const char* name, unsigned index);

#ifdef WITH_DEBUG_SELF_COMPARISON
  unordered_map<string, uintptr_t>&
  get_type_id_canonical_type_map() const;

  unordered_map<uintptr_t, string>&
  get_pointer_type_id_map();

  string
  get_type_id_from_pointer(uintptr_t ptr);

  uintptr_t
  get_canonical_type_from_type_id(const char*);
#endif

  friend class class_or_union;
  friend class class_decl;
  friend class function_type;

  friend void keep_type_alive(type_base_sptr);
}; // end class environment

class location_manager;
/// @brief The source location of a token.
///
/// This represents the location of a token coming from a given
/// translation unit.  This location is actually an abstraction of
/// cursor in the table of all the locations of all the tokens of the
/// translation unit.  That table is managed by the @ref location_manager
/// type.  To get the file path, line and column numbers associated to
/// a given instance of @ref location, you need to use the
/// location_manager::expand_location method.
class location
{
  unsigned		value_;
  // The location manager to use to decode the value above.  There is
  // one location manager per translation unit, and the location
  // manager's life time is managed by its translation unit.
  location_manager*	loc_manager_;
  // Whether the location is artificial.  Being artificial means that
  // the location wasn't generated by the original emitter of the
  // metadata (i.e, the compiler if the metadata is debug info).  For
  // instance, implicit location derived from the position of XML
  // elements in the abixml file is represented as artificial
  // locations.
  bool			is_artificial_;

  location(unsigned v, location_manager* m)
    : value_(v), loc_manager_(m), is_artificial_(false)
  {}

  /// Get the location manager to use to decode the value of this
  /// location.
  ///
  /// @return the location manager for the current location value.
  location_manager*
  get_location_manager() const
  {return loc_manager_;}

public:

  /// Test if the location is artificial.
  ///
  /// Being artificial means that the location wasn't generated by the
  /// original emitter of the metadata (i.e, the compiler if the
  /// metadata is debug info).  For instance, the implicit location
  /// derived from the position of a given XML element in the abixml
  /// file is represented as artificial locations.  The same XML
  /// element might carry a non-artificial (natural?) location that was
  /// originally emitted by the compiler that generated the original
  /// debug info the abixml file is derived from.
  ///
  /// @return true iff the location is artificial.
  bool
  get_is_artificial() const
  {return is_artificial_;}

  /// Set the artificial-ness of the location.
  ///
  /// Being artificial means that the location wasn't generated by the
  /// original emitter of the metadata (i.e, the compiler if the
  /// metadata is debug info).  For instance, the implicit location
  /// derived from the position of a given XML element in the abixml
  /// file is represented as artificial locations.  The same XML
  /// element might carry a non-artificial (natural?) location that
  /// was originally emitted by the compiler that generated the
  /// original debug info the abixml file is derived from.
  ///
  /// @param f the new artificial-ness state.
  void
  set_is_artificial(bool f)
  {is_artificial_ = f;}

  /// Copy constructor of the location.
  ///
  /// @param l the location to copy from.
  location(const location& l)
    : value_(l.value_),
      loc_manager_(l.loc_manager_),
      is_artificial_(l.is_artificial_)
  {}

  /// Assignment operator of the location.
  ///
  /// @param l the location to assign to the current one.
  location&
  operator=(const location& l)
  {
    value_ = l.value_;
    loc_manager_ = l.loc_manager_;
    is_artificial_ = l.is_artificial_;
    return *this;
  }

  /// Default constructor for the @ref location type.
  location()
    : value_(), loc_manager_(), is_artificial_()
  {}

  /// Get the value of the location.
  unsigned
  get_value() const
  {return value_;}

  /// Convert the location into a boolean.
  ///
  /// @return true iff the value of the location is different from
  /// zero.
  operator bool() const
  {return !!value_;}

  /// Equality operator of the @ref location type.
  ///
  /// @param other the other location to compare against.
  ///
  /// @return true iff both locations are equal.
  bool
  operator==(const location &other) const
  {return value_ == other.value_;}

  /// "Less than" operator of the @ref location type.
  ///
  /// @parm other the other location type to compare against.
  ///
  /// @return true iff the current instance is less than the @p other
  /// one.
  bool
  operator<(const location &other) const
  {return value_ < other.value_;}

  /// Expand the current location into a tripplet file path, line and
  /// column number.
  ///
  /// @param path the output parameter this function sets the expanded
  /// path to.
  ///
  /// @param line the output parameter this function sets the expanded
  /// line number to.
  ///
  /// @param column the output parameter this function sets the
  /// expanded column number to.
  void
  expand(std::string& path, unsigned& line, unsigned& column) const;

  string
  expand(void) const;

  friend class location_manager;
}; // end class location

/// @brief The entry point to manage locations.
///
/// This type keeps a table of all the locations for tokens of a
/// given translation unit.
class location_manager
{
  struct priv;
  std::unique_ptr<priv> priv_;

public:

  location_manager();

  ~location_manager();

  location
  create_new_location(const std::string& fle, size_t lne, size_t col);

  void
  expand_location(const location& location, std::string& path,
		  unsigned& line, unsigned& column) const;
};

/// The base of an entity of the intermediate representation that is
/// to be traversed.
struct ir_traversable_base : public traversable_base
{
  /// Traverse a given IR node and its children, calling an visitor on
  /// each node.
  ///
  /// @param v the visitor to call on each traversed node.
  ///
  /// @return true if the all the IR node tree was traversed.
  virtual bool
  traverse(ir_node_visitor& v);
}; // end class ir_traversable_base

/// The hashing functor for using instances of @ref type_or_decl_base
/// as values in a hash map or set.
struct type_or_decl_hash
{

  /// Function-call Operator to hash the string representation of an
  /// ABI artifact.
  ///
  /// @param artifact the ABI artifact to hash.
  ///
  /// @return the hash value of the string representation of @p
  /// artifact.
  size_t
  operator()(const type_or_decl_base *artifact) const
  {
    string repr =  get_pretty_representation(artifact);
    std::hash<string> do_hash;
    return do_hash(repr);
  }

  /// Function-call Operator to hash the string representation of an
  /// ABI artifact.
  ///
  /// @param artifact the ABI artifact to hash.
  ///
  /// @return the hash value of the string representation of @p
  /// artifact.
  size_t
  operator()(const type_or_decl_base_sptr& artifact) const
  {return operator()(artifact.get());}
}; // end struct type_or_decl_hash

/// The comparison functor for using instances of @ref
/// type_or_decl_base as values in a hash map or set.
struct type_or_decl_equal
{

  /// The function-call operator to compare the string representations
  /// of two ABI artifacts.
  ///
  /// @param l the left hand side ABI artifact operand of the
  /// comparison.
  ///
  /// @param r the right hand side ABI artifact operand of the
  /// comparison.
  ///
  /// @return true iff the string representation of @p l equals the one
  /// of @p r.
  bool
  operator()(const type_or_decl_base *l, const type_or_decl_base *r) const
  {
    string repr1 = get_pretty_representation(l);
    string repr2 = get_pretty_representation(r);

    return repr1 == repr2;
  }

  /// The function-call operator to compare the string representations
  /// of two ABI artifacts.
  ///
  /// @param l the left hand side ABI artifact operand of the
  /// comparison.
  ///
  /// @param r the right hand side ABI artifact operand of the
  /// comparison.
  ///
  /// @return true iff the string representation of @p l equals the one
  /// of @p r.
  bool
  operator()(const type_or_decl_base_sptr &l,
	     const type_or_decl_base_sptr &r) const
  {return operator()(l.get(), r.get());}
}; // end type_or_decl_equal

/// A convenience typedef for a hash set of type_or_decl_base_sptr
typedef unordered_set<type_or_decl_base_sptr,
		      type_or_decl_hash,
		      type_or_decl_equal> artifact_sptr_set_type;

/// A convenience typedef for a hash set of const type_or_decl_base*
typedef unordered_set<const type_or_decl_base*,
		      type_or_decl_hash,
		      type_or_decl_equal> artifact_ptr_set_type;

/// A convenience typedef for a map which key is a string and which
/// value is a @ref type_base_wptr.
typedef unordered_map<string, type_base_wptr> string_type_base_wptr_map_type;

/// A convenience typedef for a map which key is an @ref
/// interned_string and which value is a @ref type_base_wptr.
typedef unordered_map<interned_string, type_base_wptr, hash_interned_string>
istring_type_base_wptr_map_type;

/// A convenience typedef for a map which key is an @ref
/// interned_string and which value is a @ref type_base_wptr.
typedef unordered_map<interned_string,
		      type_or_decl_base_sptr,
		      hash_interned_string>
istring_type_or_decl_base_sptr_map_type;

/// This is a type that aggregates maps of all the kinds of types that
/// are supported by libabigail.
///
/// For instance, the type_maps contains a map of string to basic
/// type, a map of string to class type, a map of string to union
/// types, etc.  The key of a map entry is the pretty representation
/// of the type, and the value of the map entry is the type.
class type_maps
{
  struct priv;
  std::unique_ptr<priv> priv_;

public:

  type_maps();

  ~type_maps();

  bool
  empty() const;

  const istring_type_base_wptrs_map_type&
  basic_types() const;

  istring_type_base_wptrs_map_type&
  basic_types();

  const istring_type_base_wptrs_map_type&
  class_types() const;

  istring_type_base_wptrs_map_type&
  class_types();

  istring_type_base_wptrs_map_type&
  union_types();

  const istring_type_base_wptrs_map_type&
  union_types() const;

  istring_type_base_wptrs_map_type&
  enum_types();

  const istring_type_base_wptrs_map_type&
  enum_types() const;

  istring_type_base_wptrs_map_type&
  typedef_types();

  const istring_type_base_wptrs_map_type&
  typedef_types() const;

  istring_type_base_wptrs_map_type&
  qualified_types();

  const istring_type_base_wptrs_map_type&
  qualified_types() const;

  istring_type_base_wptrs_map_type&
  pointer_types();

  const istring_type_base_wptrs_map_type&
  pointer_types() const;

  istring_type_base_wptrs_map_type&
  reference_types();

  const istring_type_base_wptrs_map_type&
  reference_types() const;

  istring_type_base_wptrs_map_type&
  array_types();

  const istring_type_base_wptrs_map_type&
  array_types() const;

  const istring_type_base_wptrs_map_type&
  subrange_types() const;

  istring_type_base_wptrs_map_type&
  subrange_types();

  istring_type_base_wptrs_map_type&
  function_types();

  const istring_type_base_wptrs_map_type&
  function_types() const;

  const vector<type_base_wptr>&
  get_types_sorted_by_name() const;
}; // end class type_maps;

/// This is the abstraction of the set of relevant artefacts (types,
/// variable declarations, functions, templates, etc) bundled together
/// into a translation unit.
class translation_unit : public traversable_base
{
  struct priv;
  std::unique_ptr<priv> priv_;

  // Forbidden
  translation_unit() = delete;

public:
  /// Convenience typedef for a shared pointer on a @ref global_scope.
  typedef shared_ptr<scope_decl> global_scope_sptr;

  /// The language of the translation unit.
  enum language
  {
    LANG_UNKNOWN = 0,
    LANG_Cobol74,
    LANG_Cobol85,
    LANG_C89,
    LANG_C99,
    LANG_C11,
    LANG_C,
    LANG_C_plus_plus_03,
    LANG_C_plus_plus_11,
    LANG_C_plus_plus_14,
    LANG_C_plus_plus,
    LANG_ObjC,
    LANG_ObjC_plus_plus,
    LANG_Fortran77,
    LANG_Fortran90,
    LANG_Fortran95,
    LANG_Ada83,
    LANG_Ada95,
    LANG_Pascal83,
    LANG_Modula2,
    LANG_Java,
    LANG_PL1,
    LANG_UPC,
    LANG_D,
    LANG_Python,
    LANG_Go,
    LANG_Rust,
    LANG_Mips_Assembler
  };

public:
  translation_unit(const ir::environment*	env,
		   const std::string&		path,
		   char			address_size = 0);

  virtual ~translation_unit();

  const environment*
  get_environment() const;

  environment*
  get_environment();

  void
  set_environment(const environment*);

  language
  get_language() const;

  void
  set_language(language l);

  const std::string&
  get_path() const;

  void
  set_path(const string&);

  const std::string&
  get_compilation_dir_path() const;

  void
  set_compilation_dir_path(const std::string&);

  const std::string&
  get_absolute_path() const;

  void
  set_corpus(corpus*);

  const corpus*
  get_corpus() const;

  corpus*
  get_corpus();

  const scope_decl_sptr&
  get_global_scope() const;

  scope_decl_sptr&
  get_global_scope();

  const type_maps&
  get_types() const;

  type_maps&
  get_types();

  const vector<function_type_sptr>&
  get_live_fn_types() const;

  location_manager&
  get_loc_mgr();

  const location_manager&
  get_loc_mgr() const;

  bool
  is_empty() const;

  char
  get_address_size() const;

  void
  set_address_size(char);

  bool
  is_constructed() const;

  void
  set_is_constructed(bool);

  bool
  operator==(const translation_unit&) const;

  bool
  operator!=(const translation_unit&) const;

  void
  bind_function_type_life_time(function_type_sptr) const;

  virtual bool
  traverse(ir_node_visitor& v);

  friend function_type_sptr
  lookup_function_type_in_translation_unit(const function_type& t,
					   const translation_unit& tu);

  friend function_type_sptr
  synthesize_function_type_from_translation_unit(const function_type& fn_type,
						 translation_unit& tu);

  friend type_base_sptr
  synthesize_type_from_translation_unit(const type_base_sptr& type,
					translation_unit& tu);
};//end class translation_unit

/// A comparison functor to compare translation units based on their
/// absolute paths.
struct shared_translation_unit_comp
{
  /// Compare two translations units based on their absolute paths.
  ///
  /// @param lhs the first translation unit to consider for the
  /// comparison.
  ///
  /// @param rhs the second translatin unit to consider for the
  /// comparison.
  bool
  operator()(const translation_unit_sptr& lhs,
	     const translation_unit_sptr& rhs) const
  {return lhs->get_absolute_path() < rhs->get_absolute_path();}
}; // end struct shared_translation_unit_comp

/// Convenience typedef for an ordered set of @ref
/// translation_unit_sptr.
typedef std::set<translation_unit_sptr,
		 shared_translation_unit_comp> translation_units;

string
translation_unit_language_to_string(translation_unit::language);

translation_unit::language
string_to_translation_unit_language(const string&);

bool
is_c_language(translation_unit::language l);

bool
is_cplus_plus_language(translation_unit::language l);

bool
is_java_language(translation_unit::language l);

bool
is_ada_language(translation_unit::language l);

bool
operator==(const translation_unit_sptr&, const translation_unit_sptr&);

bool
operator!=(const translation_unit_sptr&, const translation_unit_sptr&);

/// Access specifier for class members.
enum access_specifier
{
  no_access,
  public_access,
  protected_access,
  private_access,
};

class elf_symbol;
/// A convenience typedef for a shared pointer to elf_symbol.
typedef shared_ptr<elf_symbol> elf_symbol_sptr;

/// A convenience typedef for a weak pointer to elf_symbol.
typedef weak_ptr<elf_symbol> elf_symbol_wptr;

/// Convenience typedef for a map which key is a string and which
/// value if the elf symbol of the same name.
typedef std::unordered_map<string, elf_symbol_sptr>
string_elf_symbol_sptr_map_type;

/// Convenience typedef for a shared pointer to an
/// string_elf_symbol_sptr_map_type.
typedef shared_ptr<string_elf_symbol_sptr_map_type>
string_elf_symbol_sptr_map_sptr;

/// Convenience typedef for a vector of elf_symbol
typedef std::vector<elf_symbol_sptr> elf_symbols;

/// Convenience typedef for a map which key is a string and which
/// value is a vector of elf_symbol.
typedef std::unordered_map<string, elf_symbols>
string_elf_symbols_map_type;

/// Convenience typedef for a shared pointer to
/// string_elf_symbols_map_type.
typedef shared_ptr<string_elf_symbols_map_type> string_elf_symbols_map_sptr;

/// Abstraction of an elf symbol.
///
/// This is useful when a given corpus has been read from an ELF file.
/// In that case, a given decl might be associated to its underlying
/// ELF symbol, if that decl is publicly exported in the ELF file.  In
/// that case, comparing decls might involve comparing their
/// underlying symbols as well.
class elf_symbol
{
public:
  /// The type of a symbol.
  enum type
  {
    NOTYPE_TYPE = 0,
    OBJECT_TYPE,
    FUNC_TYPE,
    SECTION_TYPE,
    FILE_TYPE,
    COMMON_TYPE,
    TLS_TYPE,
    GNU_IFUNC_TYPE
  };

  /// The binding of a symbol.
  enum binding
  {
    LOCAL_BINDING = 0,
    GLOBAL_BINDING,
    WEAK_BINDING,
    GNU_UNIQUE_BINDING
  };

  /// The visibility of the symbol.
  enum visibility
  {
    DEFAULT_VISIBILITY,
    PROTECTED_VISIBILITY,
    HIDDEN_VISIBILITY,
    INTERNAL_VISIBILITY,
  };

  /// Inject the elf_symbol::version here.
  class version;

private:
  struct priv;
  std::unique_ptr<priv> priv_;

  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_in_ksymtab = false,
	     uint64_t		crc = 0,
	     bool		is_suppressed = false);

  elf_symbol(const elf_symbol&);

  elf_symbol&
  operator=(const elf_symbol& s);

public:

  static elf_symbol_sptr
  create();

  static elf_symbol_sptr
  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_in_ksymtab = false,
	 uint64_t	    crc = 0,
	 bool		    is_suppressed = false);

  const environment*
  get_environment() const;

  void
  set_environment(const environment*) const;

  size_t
  get_index() const;

  void
  set_index(size_t);

  const string&
  get_name() const;

  void
  set_name(const string& n);

  type
  get_type() const;

  void
  set_type(type t);

  size_t
  get_size() const;

  void
  set_size(size_t);

  binding
  get_binding() const;

  void
  set_binding(binding b);

  version&
  get_version() const;

  void
  set_version(const version& v);

  void
  set_visibility(visibility v);

  visibility
  get_visibility() const;

  bool
  is_defined() const;

  void
  is_defined(bool d);

  bool
  is_public() const;

  bool
  is_function() const;

  bool
  is_variable() const;

  bool
  is_in_ksymtab() const;

  void
  set_is_in_ksymtab(bool is_in_ksymtab);

  uint64_t
  get_crc() const;

  void
  set_crc(uint64_t crc);

  bool
  is_suppressed() const;

  void
  set_is_suppressed(bool is_suppressed);

  const elf_symbol_sptr
  get_main_symbol() const;

  elf_symbol_sptr
  get_main_symbol();

  bool
  is_main_symbol() const;

  elf_symbol_sptr
  update_main_symbol(const std::string&);

  elf_symbol_sptr
  get_next_alias() const;

  bool
  has_aliases() const;

  int
  get_number_of_aliases() const;

  void
  add_alias(const elf_symbol_sptr&);

  bool
  is_common_symbol() const;

  bool
  has_other_common_instances() const;

  elf_symbol_sptr
  get_next_common_instance() const;

  void
  add_common_instance(const elf_symbol_sptr&);

  const string&
  get_id_string() const;

  elf_symbol_sptr
  get_alias_from_name(const string& name) const;

  elf_symbol_sptr
  get_alias_which_equals(const elf_symbol& other) const;

  string
  get_aliases_id_string(const string_elf_symbols_map_type& symtab,
			bool include_symbol_itself = true) const;

  string
  get_aliases_id_string(bool include_symbol_itself = true) const;

  static bool
  get_name_and_version_from_id(const string& id,
			       string& name,
			       string& ver);

  bool
  operator==(const elf_symbol&) const;

  bool
  does_alias(const elf_symbol&) const;
}; // end class elf_symbol.

std::ostream&
operator<<(std::ostream& o, elf_symbol::type t);

std::ostream&
operator<<(std::ostream& o, elf_symbol::binding t);

std::ostream&
operator<<(std::ostream& o, elf_symbol::visibility t);

bool
string_to_elf_symbol_type(const string&, elf_symbol::type&);

bool
string_to_elf_symbol_binding(const string&, elf_symbol::binding&);

bool
string_to_elf_symbol_visibility(const string&, elf_symbol::visibility&);

bool
elf_symbol_is_function(elf_symbol::type);

bool
elf_symbol_is_variable(elf_symbol::type);

bool
operator==(const elf_symbol_sptr& lhs, const elf_symbol_sptr& rhs);

bool
operator!=(const elf_symbol_sptr& lhs, const elf_symbol_sptr& rhs);

bool
elf_symbols_alias(const elf_symbol& s1, const elf_symbol& s2);

void
compute_aliases_for_elf_symbol(const elf_symbol& symbol,
			       const string_elf_symbols_map_type& symtab,
			       vector<elf_symbol_sptr>& alias_set);

/// The abstraction of the version of an ELF symbol.
class elf_symbol::version
{
  struct priv;
  std::unique_ptr<priv> priv_;

public:
  version();

  version(const string& v,
	  bool is_default);

  version(const version& v);

  ~version();

  operator const string&() const;

  const string&
  str() const;

  void
  str(const string& s);

  bool
  is_default() const;

  void
  is_default(bool f);

  bool
  is_empty() const;

  bool
  operator==(const version& o) const;

  bool
  operator!=(const version& o) const;

  version&
  operator=(const version& o);
};// end class elf_symbol::version

class context_rel;
/// A convenience typedef for shared pointers to @ref context_rel
typedef shared_ptr<context_rel> context_rel_sptr;

/// The abstraction of the relationship between an entity and its
/// containing scope (its context).  That relationship can carry
/// properties like access rights (if the parent is a class_decl),
/// etc.
///
/// But importantly, this relationship carries a pointer to the
/// actualy parent.
class context_rel
{
protected:
  scope_decl*		scope_;
  enum access_specifier access_;
  bool			is_static_;

public:
  context_rel()
    : scope_(0),
      access_(no_access),
      is_static_(false)
  {}

  context_rel(scope_decl* s)
    : scope_(s),
      access_(no_access),
      is_static_(false)
  {}

  context_rel(scope_decl* s,
	      access_specifier a,
	      bool f)
    : scope_(s),
      access_(a),
      is_static_(f)
  {}

  scope_decl*
  get_scope() const
  {return scope_;}

  access_specifier
  get_access_specifier() const
  {return access_;}

  void
  set_access_specifier(access_specifier a)
  {access_ = a;}

  bool
  get_is_static() const
  {return is_static_;}

  void
  set_is_static(bool s)
  {is_static_ = s;}

  void
  set_scope(scope_decl* s)
  {scope_ = s;}

  bool
  operator==(const context_rel& o)const
  {
    return (access_ == o.access_
	    && is_static_ == o.is_static_);
  }

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

  virtual ~context_rel();
};// end class context_rel

/// A bitfield that gives callers of abigail::ir::equals() some
/// insight about how different two internal representation artifacts
/// are.
enum change_kind
{
  NO_CHANGE_KIND = 0,

  /// This means that a given IR artifact has a local type change.
  LOCAL_TYPE_CHANGE_KIND = 1 << 0,

  /// This means that a given IR artifact has a local non-type change.
  /// That is a change that is carried by the artifact itself, not by
  /// its type.
  LOCAL_NON_TYPE_CHANGE_KIND = 1 << 1,

  /// Testing (anding) against this mask means that a given IR artifact has
  /// local differences, with respect to the other artifact it was compared
  /// against. A local change is a change that is carried by the artifact
  /// itself (or its type), rather than by one off its sub-types.
  ALL_LOCAL_CHANGES_MASK = LOCAL_TYPE_CHANGE_KIND | LOCAL_NON_TYPE_CHANGE_KIND,

  /// This means that a given IR artifact has changes in some of its
  /// sub-types, with respect to the other artifact it was compared
  /// against.
  SUBTYPE_CHANGE_KIND = 1 << 2,
};// end enum change_kind

change_kind
operator|(change_kind, change_kind);

change_kind
operator&(change_kind, change_kind);

change_kind&
operator|=(change_kind&, change_kind);

change_kind&
operator&=(change_kind&, change_kind);

bool
maybe_compare_as_member_decls(const decl_base& l,
			      const decl_base& r,
			      change_kind* k);

bool
equals(const decl_base&, const decl_base&, change_kind*);

/// The base class of both types and declarations.
class type_or_decl_base : public ir_traversable_base
{
  struct priv;
  mutable std::unique_ptr<priv> priv_;

  type_or_decl_base();

protected:

  /// This is a bitmap type which instance is meant to contain the
  /// runtime type of a given ABI artifact.  Bits of the identifiers
  /// of the type of a given artifact as well as the types it inherits
  /// from are to be set to 1.
  enum type_or_decl_kind
  {
    ABSTRACT_TYPE_OR_DECL,
    ABSTRACT_DECL_BASE = 1,
    ABSTRACT_SCOPE_DECL = 1 << 1,
    GLOBAL_SCOPE_DECL = 1 << 2,
    NAMESPACE_DECL = 1 << 3,
    VAR_DECL = 1 << 4,
    FUNCTION_DECL = 1 << 5,
    FUNCTION_PARAMETER_DECL = 1 << 6,
    METHOD_DECL = 1 << 7,
    TEMPLATE_DECL = 1 << 8,
    ABSTRACT_TYPE_BASE = 1 << 9,
    ABSTRACT_SCOPE_TYPE_DECL = 1 << 10,
    BASIC_TYPE = 1 << 11,
    QUALIFIED_TYPE = 1 << 12,
    POINTER_TYPE = 1 << 13,
    REFERENCE_TYPE = 1 << 14,
    ARRAY_TYPE = 1 << 15,
    ENUM_TYPE = 1 << 16,
    TYPEDEF_TYPE = 1 << 17,
    CLASS_TYPE = 1 << 18,
    UNION_TYPE = 1 << 19,
    FUNCTION_TYPE = 1 << 20,
    METHOD_TYPE = 1 << 21,
  }; // end enum type_or_decl_kind

  enum type_or_decl_kind
  kind() const;

  void
  kind(enum type_or_decl_kind);

  const void*
  runtime_type_instance() const;

  void*
  runtime_type_instance();

  void
  runtime_type_instance(void*);

  const void*
  type_or_decl_base_pointer() const;

  void*
  type_or_decl_base_pointer();

  bool hashing_started() const;

  void hashing_started(bool) const;

public:

  type_or_decl_base(const environment*,
		    enum type_or_decl_kind k = ABSTRACT_TYPE_OR_DECL);

  type_or_decl_base(const type_or_decl_base&);

  virtual ~type_or_decl_base();

  bool
  get_is_artificial() const;

  void
  set_is_artificial(bool);

  const environment*
  get_environment() const;

  environment*
  get_environment();

  void
  set_environment(const environment*);

  void
  set_artificial_location(const location &);

  location&
  get_artificial_location() const;

  bool
  has_artificial_location() const;

  const corpus*
  get_corpus() const;

  corpus*
  get_corpus();

  void
  set_translation_unit(translation_unit*);

  const translation_unit*
  get_translation_unit() const;

  translation_unit*
  get_translation_unit();

  type_or_decl_base&
  operator=(const type_or_decl_base&);

  virtual bool
  traverse(ir_node_visitor&);

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const = 0;

  friend type_or_decl_base::type_or_decl_kind
  operator|(type_or_decl_base::type_or_decl_kind,
	    type_or_decl_base::type_or_decl_kind);

  friend type_or_decl_base::type_or_decl_kind&
  operator|=(type_or_decl_base::type_or_decl_kind&,
	     type_or_decl_base::type_or_decl_kind);

  friend type_or_decl_base::type_or_decl_kind
  operator&(type_or_decl_base::type_or_decl_kind,
	    type_or_decl_base::type_or_decl_kind);

  friend type_or_decl_base::type_or_decl_kind&
  operator&=(type_or_decl_base::type_or_decl_kind&,
	     type_or_decl_base::type_or_decl_kind);

  friend class_decl*
  is_class_type(const type_or_decl_base*);

  friend pointer_type_def*
  is_pointer_type(type_or_decl_base*);

  friend type_base*
  is_type(const type_or_decl_base*);

  friend decl_base*
  is_decl(const type_or_decl_base* d);
}; // end class type_or_decl_base

type_or_decl_base::type_or_decl_kind
operator|(type_or_decl_base::type_or_decl_kind,
	  type_or_decl_base::type_or_decl_kind);

type_or_decl_base::type_or_decl_kind&
operator|=(type_or_decl_base::type_or_decl_kind&,
	   type_or_decl_base::type_or_decl_kind);

type_or_decl_base::type_or_decl_kind
operator&(type_or_decl_base::type_or_decl_kind,
	  type_or_decl_base::type_or_decl_kind);

type_or_decl_base::type_or_decl_kind&
operator&=(type_or_decl_base::type_or_decl_kind&,
	   type_or_decl_base::type_or_decl_kind);

bool
operator==(const type_or_decl_base&, const type_or_decl_base&);

bool
operator==(const type_or_decl_base_sptr&, const type_or_decl_base_sptr&);

bool
operator!=(const type_or_decl_base_sptr&, const type_or_decl_base_sptr&);

void
set_environment_for_artifact(type_or_decl_base* artifact,
			     const environment* env);

void
set_environment_for_artifact(type_or_decl_base_sptr artifact,
			     const environment* env);

/// The base type of all declarations.
class decl_base : public virtual type_or_decl_base
{
  // Forbidden
  decl_base();

  struct priv;

protected:

  const interned_string&
  peek_qualified_name() const;

  void
  clear_qualified_name();

  void
  set_qualified_name(const interned_string&) const;

  const interned_string&
  peek_temporary_qualified_name() const;

  void
  set_temporary_qualified_name(const interned_string&) const;

public:
  // This is public because some internals of the library need to
  // update it.  But it's opaque to client code anyway, so no big
  // deal.  Also, it's not handled by a shared_ptr because accessing
  // the data members of the priv struct for this decl_base shows up
  // on performance profiles when dealing with big binaries with a lot
  // of types; dereferencing the shared_ptr involves locking of some
  // sort and that is slower than just dereferencing a pointer likere
  // here.  There are other types for which the priv pointer is
  // managed using shared_ptr just fine, because those didn't show up
  // during our performance profiling.
  priv* priv_;

  /// Facility to hash instances of decl_base.
  struct hash;

  /// ELF visibility
  enum visibility
  {
    VISIBILITY_NONE,
    VISIBILITY_DEFAULT,
    VISIBILITY_PROTECTED,
    VISIBILITY_HIDDEN,
    VISIBILITY_INTERNAL
  };

  /// ELF binding
  enum binding
  {
    BINDING_NONE,
    BINDING_LOCAL,
    BINDING_GLOBAL,
    BINDING_WEAK
  };

  virtual void
  set_scope(scope_decl*);

protected:
  const context_rel*
  get_context_rel() const;

  context_rel*
  get_context_rel();

  void
  set_context_rel(context_rel *c);

public:
  decl_base(const environment* e,
	    const string& name,
	    const location& locus,
	    const string& mangled_name = "",
	    visibility vis = VISIBILITY_DEFAULT);

  decl_base(const environment* e,
	    const interned_string& name,
	    const location& locus,
	    const interned_string& mangled_name = interned_string(),
	    visibility vis = VISIBILITY_DEFAULT);

  decl_base(const environment*, const location&);

  decl_base(const decl_base&);

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator!=(const decl_base&) const;

  virtual bool
  traverse(ir_node_visitor& v);

  virtual ~decl_base();

  virtual size_t
  get_hash() const;

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;

  virtual void
  get_qualified_name(interned_string& qualified_name,
		     bool internal = false) const;

  virtual const interned_string&
  get_qualified_name(bool internal = false) const;

  virtual const interned_string&
  get_scoped_name() const;

  bool
  get_is_in_public_symbol_table() const;

  void
  set_is_in_public_symbol_table(bool);

  const location&
  get_location() const;

  void
  set_location(const location& l);

  const interned_string&
  get_name() const;

  const interned_string&
  get_qualified_parent_name() const;

  void
  set_name(const string& n);

  bool
  get_is_anonymous() const;

  void
  set_is_anonymous(bool);

  bool
  get_has_anonymous_parent() const;

  bool
  get_is_anonymous_or_has_anonymous_parent() const;

  typedef_decl_sptr
  get_naming_typedef() const;

  void
  set_naming_typedef(const typedef_decl_sptr&);

  const interned_string&
  get_linkage_name() const;

  virtual void
  set_linkage_name(const string& m);

  scope_decl*
  get_scope() const;

  visibility
  get_visibility() const;

  void
  set_visibility(visibility v);

  const decl_base_sptr
  get_earlier_declaration() const;

  void
  set_earlier_declaration(const decl_base_sptr&);

  const decl_base_sptr
  get_definition_of_declaration() const;

  void
  set_definition_of_declaration(const decl_base_sptr&);

  const decl_base*
  get_naked_definition_of_declaration() const;

  bool
  get_is_declaration_only() const;

  void
  set_is_declaration_only(bool f);

  friend type_base_sptr
  canonicalize(type_base_sptr);

  friend bool
  equals(const decl_base&, const decl_base&, change_kind*);

  friend bool
  equals(const var_decl&, const var_decl&, change_kind*);

  friend bool
  maybe_compare_as_member_decls(const decl_base& l,
				const decl_base& r,
				change_kind* k);

  friend decl_base_sptr
  add_decl_to_scope(decl_base_sptr decl, scope_decl* scpe);

  friend void
  remove_decl_from_scope(decl_base_sptr);

  friend decl_base_sptr
  insert_decl_into_scope(decl_base_sptr,
			 vector<shared_ptr<decl_base> >::iterator,
			 scope_decl*);

  friend enum access_specifier
  get_member_access_specifier(const decl_base& d);

  friend enum access_specifier
  get_member_access_specifier(const decl_base_sptr& d);

  friend void
  set_member_access_specifier(decl_base& d,
			      access_specifier a);

  friend bool
  get_member_is_static(const decl_base& d);

  friend bool
  get_member_is_static(const decl_base_sptr& d);

  friend void
  set_member_is_static(const decl_base_sptr& d, bool s);

  friend void
  set_member_is_static(decl_base& d, bool s);

  friend bool
  get_member_function_is_virtual(const function_decl& f);

  friend void
  set_member_function_is_virtual(function_decl&, bool);

  friend class class_or_union;
  friend class class_decl;
  friend class scope_decl;
};// end class decl_base

bool
operator==(const decl_base_sptr&, const decl_base_sptr&);

bool
operator!=(const decl_base_sptr&, const decl_base_sptr&);

bool
operator==(const type_base_sptr&, const type_base_sptr&);

bool
operator!=(const type_base_sptr&, const type_base_sptr&);

std::ostream&
operator<<(std::ostream&, decl_base::visibility);

std::ostream&
operator<<(std::ostream&, decl_base::binding);

bool
equals(const scope_decl&, const scope_decl&, change_kind*);

/// A declaration that introduces a scope.
class scope_decl : public virtual decl_base
{
  struct priv;
  std::unique_ptr<priv> priv_;

public:

  /// Convenience typedef for a vector of @ref decl_base_sptr.
  typedef std::vector<decl_base_sptr >	declarations;
  /// Convenience typedef for a vector of @ref function_type_sptr.
  typedef std::vector<function_type_sptr >	function_types;
  /// Convenience typedef for a vector of @ref scope_decl_sptr.
  typedef std::vector<scope_decl_sptr>	scopes;

  scope_decl();

protected:
  virtual decl_base_sptr
  add_member_decl(const decl_base_sptr& member);

  virtual decl_base_sptr
  insert_member_decl(decl_base_sptr member, declarations::iterator before);

  virtual void
  remove_member_decl(decl_base_sptr member);

public:
  struct hash;

  scope_decl(const environment* env,
	     const string& name, const location& locus,
	     visibility	vis = VISIBILITY_DEFAULT);

  scope_decl(const environment* env, location& l);

  virtual size_t
  get_hash() const;

  virtual bool
  operator==(const decl_base&) const;

  const canonical_type_sptr_set_type&
  get_canonical_types() const;

  canonical_type_sptr_set_type&
  get_canonical_types();

  const type_base_sptrs_type&
  get_sorted_canonical_types() const;

  const declarations&
  get_member_decls() const;

  declarations&
  get_member_decls();

  const declarations&
  get_sorted_member_decls() const;

  virtual size_t
  get_num_anonymous_member_classes() const;

  virtual size_t
  get_num_anonymous_member_unions() const;

  virtual size_t
  get_num_anonymous_member_enums() const;

  scopes&
  get_member_scopes();

  const scopes&
  get_member_scopes() const;

  bool
  is_empty() const;

  bool
  find_iterator_for_member(const decl_base*, declarations::iterator&);

  bool
  find_iterator_for_member(const decl_base_sptr, declarations::iterator&);

  virtual bool
  traverse(ir_node_visitor&);

  virtual ~scope_decl();

  friend decl_base_sptr
  add_decl_to_scope(decl_base_sptr decl, scope_decl* scope);

  friend decl_base_sptr
  insert_decl_into_scope(decl_base_sptr decl,
			 scope_decl::declarations::iterator before,
			 scope_decl* scope);

  friend void
  remove_decl_from_scope(decl_base_sptr decl);

  friend type_base_sptr
  canonicalize(type_base_sptr);
};//end class scope_decl

bool
operator==(const scope_decl_sptr&, const scope_decl_sptr&);

bool
operator!=(const scope_decl_sptr&, const scope_decl_sptr&);

/// Hasher for the @ref scope_decl type.
struct scope_decl::hash
{
  size_t
  operator()(const scope_decl& d) const;

  size_t
  operator()(const scope_decl* d) const;
};

/// This abstracts the global scope of a given translation unit.
///
/// Only one instance of this class must be present in a given
/// translation_unit.  That instance is implicitely created the first
/// time translatin_unit::get_global_scope is invoked.
class global_scope : public scope_decl
{
  translation_unit* translation_unit_;

  global_scope(translation_unit *tu);

public:

  friend class translation_unit;

  translation_unit*
  get_translation_unit() const
  {return translation_unit_;}

  virtual ~global_scope();
};

bool
equals(const type_base&, const type_base&, change_kind*);

/// An abstraction helper for type declarations
class type_base : public virtual type_or_decl_base
{
  struct priv;

public:
  // This priv pointer is not handled by a shared_ptr because
  // accessing the data members of the priv struct for this type_base
  // shows up on performance profiles when dealing with big binaries
  // with a lot of types; dereferencing the shared_ptr involves
  // locking of some sort and that is slower than just dereferencing a
  // pointer likere here.  There are other types for which the priv
  // pointer is managed using shared_ptr just fine, because those
  // didn't show up during our performance profiling.
  priv* priv_;

private:
  // Forbid this.
  type_base();

  static type_base_sptr
  get_canonical_type_for(type_base_sptr);

protected:
  virtual void
  on_canonical_type_set();

public:

  /// A hasher for type_base types.
  struct hash;

  /// A hasher for types.  It gets the dynamic type of the current
  /// instance of type and hashes it accordingly.  Note that the hashing
  /// function of this hasher must be updated each time a new kind of
  /// type is added to the IR.
  struct dynamic_hash;

  /// A hasher for shared_ptr<type_base> that will hash it based on the
  /// runtime type of the type pointed to.
  struct shared_ptr_hash;

  type_base(const environment* e, size_t s, size_t a);

  friend type_base_sptr canonicalize(type_base_sptr);

  type_base_sptr
  get_canonical_type() const;

  type_base*
  get_naked_canonical_type() const;

  const interned_string&
  get_cached_pretty_representation(bool internal = false) const;

  virtual bool
  operator==(const type_base&) const;

  virtual bool
  operator!=(const type_base&) const;

  virtual bool
  traverse(ir_node_visitor&);

  virtual ~type_base();

  virtual void
  set_size_in_bits(size_t);

  virtual size_t
  get_size_in_bits() const;

  virtual void
  set_alignment_in_bits(size_t);

  virtual size_t
  get_alignment_in_bits() const;
};//end class type_base

/// Hash functor for instances of @ref type_base.
struct type_base::hash
{
  size_t
  operator()(const type_base& t) const;

  size_t
  operator()(const type_base* t) const;

  size_t
  operator()(const type_base_sptr t) const;
}; // end struct type_base::hash

/// A predicate for deep equality of instances of
/// type_base*
struct type_ptr_equal
{
  bool
  operator()(const type_base* l, const type_base* r) const
  {
    if (!!l != !!r)
      return false;

    if (l == r)
      return true;

    if (l)
      return *l == *r;

    return true;
  }
};

/// A predicate for deep equality of instances of
/// shared_ptr<type_base>
struct type_shared_ptr_equal
{
  bool
  operator()(const type_base_sptr l, const type_base_sptr r) const
  {
    if (!!l != !!r)
      return false;

    if (l.get() == r.get())
      return true;

    if (l)
      return *l == *r;

    return true;
  }
};

bool
equals(const type_decl&, const type_decl&, change_kind*);

/// A basic type declaration that introduces no scope.
class type_decl : public virtual decl_base, public virtual type_base
{
  // Forbidden.
  type_decl();

public:

  /// Facility to hash instance of type_decl
  struct hash;

  type_decl(const environment*	env,
	    const string&	name,
	    size_t		size_in_bits,
	    size_t		alignment_in_bits,
	    const location&	locus,
	    const string&	mangled_name = "",
	    visibility		vis = VISIBILITY_DEFAULT);

  virtual bool
  operator==(const type_base&) const;

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const type_decl&) const;

  bool operator!=(const type_decl&)const;

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;

  virtual bool
  traverse(ir_node_visitor&);

  virtual ~type_decl();
};// end class type_decl.

bool
equals(const scope_type_decl&, const scope_type_decl&, change_kind*);

bool
operator==(const type_decl_sptr&, const type_decl_sptr&);

bool
operator!=(const type_decl_sptr&, const type_decl_sptr&);

/// A type that introduces a scope.
class scope_type_decl : public scope_decl, public virtual type_base
{
  scope_type_decl();

public:

  /// Hasher for instances of scope_type_decl
  struct hash;

  scope_type_decl(const environment* env, const string& name,
		  size_t size_in_bits, size_t alignment_in_bits,
		  const location& locus, visibility vis = VISIBILITY_DEFAULT);

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const type_base&) const;

  virtual bool
  traverse(ir_node_visitor&);

  virtual ~scope_type_decl();
};

/// The abstraction of a namespace declaration
class namespace_decl : public scope_decl
{
public:

  namespace_decl(const environment* env, const string& name,
		 const location& locus, visibility vis = VISIBILITY_DEFAULT);

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  traverse(ir_node_visitor&);

  virtual ~namespace_decl();

  bool is_empty_or_has_empty_sub_namespaces() const;
};// end class namespace_decl

bool
equals(const qualified_type_def&, const qualified_type_def&, change_kind*);

/// The abstraction of a qualified type.
class qualified_type_def : public virtual type_base, public virtual decl_base
{
  class priv;
  std::unique_ptr<priv> priv_;

  // Forbidden.
  qualified_type_def();

protected:
  string build_name(bool, bool internal = false) const;
  virtual void on_canonical_type_set();

public:

  /// A Hasher for instances of qualified_type_def
  struct hash;

  /// Bit field values representing the cv qualifiers of the
  /// underlying type.
  enum CV
  {
    CV_NONE = 0,
    CV_CONST = 1,
    CV_VOLATILE = 1 << 1,
    CV_RESTRICT = 1 << 2
  };

  qualified_type_def(type_base_sptr type, CV quals, const location& locus);

  qualified_type_def(environment* env, CV quals, const location& locus);

  virtual size_t
  get_size_in_bits() const;

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const type_base&) const;

  virtual bool
  operator==(const qualified_type_def&) const;

  CV
  get_cv_quals() const;

  void
  set_cv_quals(CV cv_quals);

  string
  get_cv_quals_string_prefix() const;

  type_base_sptr
  get_underlying_type() const;

  void
  set_underlying_type(const type_base_sptr&);

  virtual void
  get_qualified_name(interned_string& qualified_name,
		     bool internal = false) const;

  virtual const interned_string&
  get_qualified_name(bool internal = false) const;

  virtual bool
  traverse(ir_node_visitor& v);

  virtual ~qualified_type_def();
}; // end class qualified_type_def.

bool
operator==(const qualified_type_def_sptr&, const qualified_type_def_sptr&);

bool
operator!=(const qualified_type_def_sptr&, const qualified_type_def_sptr&);

qualified_type_def::CV
operator|(qualified_type_def::CV, qualified_type_def::CV);

qualified_type_def::CV&
operator|=(qualified_type_def::CV&, qualified_type_def::CV);

qualified_type_def::CV
operator&(qualified_type_def::CV, qualified_type_def::CV);

qualified_type_def::CV
operator~(qualified_type_def::CV);

std::ostream&
operator<<(std::ostream&, qualified_type_def::CV);

string
get_string_representation_of_cv_quals(const qualified_type_def::CV);

interned_string
get_name_of_qualified_type(const type_base_sptr& underlying_type,
			   qualified_type_def::CV quals,
			   bool qualified = true, bool internal = false);

qualified_type_def_sptr
lookup_qualified_type(const type_base_sptr&,
		      qualified_type_def::CV,
		      const translation_unit&);
bool
equals(const pointer_type_def&, const pointer_type_def&, change_kind*);

/// The abstraction of a pointer type.
class pointer_type_def : public virtual type_base, public virtual decl_base
{
  struct priv;
  std::unique_ptr<priv> priv_;

  // Forbidden.
  pointer_type_def();

protected:
  virtual void on_canonical_type_set();

public:

  /// A hasher for instances of pointer_type_def
  struct hash;

  pointer_type_def(const type_base_sptr& pointed_to_type, size_t size_in_bits,
		   size_t alignment_in_bits, const location& locus);

  pointer_type_def(environment* env, size_t size_in_bits,
		   size_t alignment_in_bits, const location& locus);


  void
  set_pointed_to_type(const type_base_sptr&);

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const type_base&) const;

  bool
  operator==(const pointer_type_def&) const;

  const type_base_sptr
  get_pointed_to_type() const;

  type_base*
  get_naked_pointed_to_type() const;

  virtual void
  get_qualified_name(interned_string&, bool internal = false) const;

  virtual const interned_string&
  get_qualified_name(bool internal = false) const;

  virtual bool
  traverse(ir_node_visitor& v);

  virtual ~pointer_type_def();
}; // end class pointer_type_def

bool
operator==(const pointer_type_def_sptr&, const pointer_type_def_sptr&);

bool
operator!=(const pointer_type_def_sptr&, const pointer_type_def_sptr&);

bool
equals(const reference_type_def&, const reference_type_def&, change_kind*);


/// Abstracts a reference type.
class reference_type_def : public virtual type_base, public virtual decl_base
{
  type_base_wptr	pointed_to_type_;
  bool			is_lvalue_;

  // Forbidden.
  reference_type_def();

protected:
  virtual void on_canonical_type_set();

public:

  /// Hasher for intances of reference_type_def.
  struct hash;

  reference_type_def(const type_base_sptr pointed_to_type,
		     bool lvalue, size_t size_in_bits,
		     size_t alignment_in_bits, const location& locus);

  reference_type_def(const environment* env, bool lvalue, size_t size_in_bits,
		     size_t alignment_in_bits, const location& locus);

  void
  set_pointed_to_type(type_base_sptr& pointed_to_type);

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const type_base&) const;

  bool
  operator==(const reference_type_def&) const;

  type_base_sptr
  get_pointed_to_type() const;

  bool
  is_lvalue() const;

  virtual void
  get_qualified_name(interned_string& qualified_name,
		     bool internal = false) const;

  virtual const interned_string&
  get_qualified_name(bool internal = false) const;

  virtual bool
  traverse(ir_node_visitor& v);

  virtual ~reference_type_def();
}; // end class reference_type_def

bool
operator==(const reference_type_def_sptr&, const reference_type_def_sptr&);

bool
operator!=(const reference_type_def_sptr&, const reference_type_def_sptr&);

bool
equals(const array_type_def&, const array_type_def&, change_kind*);

/// The abstraction of an array type.
class array_type_def : public virtual type_base, public virtual decl_base
{
  struct priv;
  std::unique_ptr<priv> priv_;

  // Forbidden.
  array_type_def();

  void update_size();

public:

  /// Hasher for intances of array_type_def.
  struct hash;

  class subrange_type;

  /// Convenience typedef for a shared pointer on a @ref
  /// function_decl::subrange
  typedef shared_ptr<subrange_type> subrange_sptr;

  /// Convenience typedef for a vector of @ref subrange_sptr
  typedef std::vector<subrange_sptr> subranges_type;

  /// Abstraction for an array range type, like in Ada, or just for an
  /// array dimension like in C or C++.
  class subrange_type : public virtual type_base,  public virtual decl_base
  {
    struct priv;
    std::unique_ptr<priv> priv_;

    // Forbidden.
    subrange_type();
  public:

    virtual ~subrange_type();
    /// This class is to hold the value of the bound of a subrange.
    /// The value can be either signed or unsigned, at least when it
    /// comes from DWARF.  The class keeps the sign information, but
    /// allows users to access the value as signed or unsigned as they
    /// see fit.
    class bound_value
    {
    public:
      enum signedness
      {
	UNSIGNED_SIGNEDNESS,
	SIGNED_SIGNEDNESS
      };

    private:
      signedness s_;

    public:
      union
      {
	uint64_t unsigned_;
	int64_t signed_;
      } v_;
      bound_value();
      bound_value(uint64_t);
      bound_value(int64_t);
      enum signedness get_signedness() const;
      void set_signedness(enum signedness s);
      int64_t get_signed_value() const;
      uint64_t get_unsigned_value();
      void set_unsigned(uint64_t v);
      void set_signed(int64_t v);
      bool operator==(const bound_value&) const;
    }; //end class bound_value

    /// Hasher for an instance of array::subrange
    struct hash;

    subrange_type(const environment*	env,
		  const string&	name,
		  bound_value		lower_bound,
		  bound_value		upper_bound,
		  const type_base_sptr& underlying_type,
		  const location&	loc,
		  translation_unit::language l = translation_unit::LANG_C11);

    subrange_type(const environment* env,
		  const string& name,
		  bound_value lower_bound,
		  bound_value upper_bound,
		  const location& loc,
		  translation_unit::language l = translation_unit::LANG_C11);

    subrange_type(const environment* env,
		  const string& name,
		  bound_value upper_bound,
		  const location& loc,
		  translation_unit::language l = translation_unit::LANG_C11);

    type_base_sptr
    get_underlying_type() const;

    void
    set_underlying_type(const type_base_sptr &);

    int64_t
    get_upper_bound() const;

    int64_t
    get_lower_bound() const;

    void
    set_upper_bound(int64_t ub);

    void
    set_lower_bound(int64_t lb);

    uint64_t
    get_length() const;

    bool
    is_infinite() const;

    void
    is_infinite(bool);

    translation_unit::language
    get_language() const;

    virtual bool
    operator==(const decl_base&) const;

    virtual bool
    operator==(const type_base&) const;

    bool
    operator==(const subrange_type& o) const;

    bool
    operator!=(const subrange_type& o) const;

    string
    as_string() const;

    static string
    vector_as_string(const vector<subrange_sptr>&);

    virtual string
    get_pretty_representation(bool internal = false,
			      bool qualified_name = true) const;

    virtual bool
    traverse(ir_node_visitor&);
  }; // end class subrange_type

  array_type_def(const type_base_sptr type,
		 const std::vector<subrange_sptr>& subs,
		 const location& locus);

  array_type_def(environment* env,
		 const std::vector<subrange_sptr>& subs,
		 const location& locus);

  translation_unit::language
  get_language() const;

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const type_base&) const;

  virtual void
  get_qualified_name(interned_string& qualified_name,
		     bool internal = false) const;

  virtual const interned_string&
  get_qualified_name(bool internal = false) const;

  const type_base_sptr
  get_element_type() const;

  void
  set_element_type(const type_base_sptr& element_type);

  virtual void
  append_subranges(const std::vector<subrange_sptr>& subs);

  virtual int
  get_dimension_count() const;

  virtual bool
  is_infinite() const;

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;

  virtual string
  get_subrange_representation() const;

  virtual bool
  traverse(ir_node_visitor& v);

  const location&
  get_location() const;

  const std::vector<subrange_sptr>&
  get_subranges() const;

  virtual ~array_type_def();

}; // end class array_type_def

array_type_def::subrange_type*
is_subrange_type(const type_or_decl_base *type);

array_type_def::subrange_sptr
is_subrange_type(const type_or_decl_base_sptr &type);

bool
equals(const enum_type_decl&, const enum_type_decl&, change_kind*);

/// Abstracts a declaration for an enum type.
class enum_type_decl : public virtual type_base, public virtual decl_base
{
  class priv;
  std::unique_ptr<priv> priv_;

  // Forbidden
  enum_type_decl();

public:

  /// A hasher for an enum_type_decl.
  struct hash;

  /// Enumerator Datum.
  class enumerator;

  /// Convenience typedef for a list of @ref enumerator.
  typedef std::vector<enumerator> enumerators;

  /// Constructor of an enum type declaration.
  ///
  /// @param name the name of the enum
  ///
  /// @param locus the locus at which the enum appears in the source
  /// code.
  ///
  /// @param underlying_type the underlying type of the enum
  ///
  /// @param enms a list of enumerators for this enum.
  ///
  /// @param mangled_name the mangled name of the enum type.
  ///
  /// @param vis the visibility of instances of this type.
  enum_type_decl(const string&		name,
		 const location&	locus,
		 type_base_sptr	underlying_type,
		 enumerators&		enms,
		 const string&		mangled_name = "",
		 visibility		vis = VISIBILITY_DEFAULT);

  type_base_sptr
  get_underlying_type() const;

  const enumerators&
  get_enumerators() const;

  enumerators&
  get_enumerators();

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const type_base&) const;

  virtual bool
  traverse(ir_node_visitor& v);

  virtual ~enum_type_decl();

  friend bool
  enum_has_non_name_change(const enum_type_decl& l,
			   const enum_type_decl& r,
			   change_kind* k);
}; // end class enum_type_decl

bool
operator==(const enum_type_decl_sptr& l, const enum_type_decl_sptr& r);

bool
operator!=(const enum_type_decl_sptr& l, const enum_type_decl_sptr& r);

bool
enum_has_non_name_change(const enum_type_decl& l,
			 const enum_type_decl& r,
			 change_kind* k);

/// The abstraction of an enumerator
class enum_type_decl::enumerator
{
  class priv;
  std::unique_ptr<priv> priv_;

public:

  enumerator();

  ~enumerator();

  enumerator(const environment* env, const string& name, int64_t value);

  enumerator(const enumerator&);

  enumerator&
  operator=(const enumerator&);

  bool
  operator==(const enumerator& other) const;

  bool
  operator!=(const enumerator& other) const;

  const environment*
  get_environment() const;

  const interned_string&
  get_name() const;

  const interned_string&
  get_qualified_name(bool internal = false) const;

  void
  set_name(const string& n);

  int64_t
  get_value() const;

  void
  set_value(int64_t v);

  enum_type_decl*
  get_enum_type() const;

  void
  set_enum_type(enum_type_decl*);
}; // end class enum_type_def::enumerator

bool
equals(const typedef_decl&, const typedef_decl&, change_kind*);

/// The abstraction of a typedef declaration.
class typedef_decl : public virtual type_base, public virtual decl_base
{
  struct priv;
  std::unique_ptr<priv> priv_;

  // Forbidden
  typedef_decl();

public:

  /// Hasher for the typedef_decl type.
  struct hash;

  typedef_decl(const string& name,
	       const type_base_sptr underlying_type,
	       const location& locus,
	       const string& mangled_name = "",
	       visibility vis = VISIBILITY_DEFAULT);

  typedef_decl(const string& name,
	       environment* env,
	       const location& locus,
	       const string& mangled_name = "",
	       visibility vis = VISIBILITY_DEFAULT);

  virtual size_t
  get_size_in_bits() const;

  virtual size_t
  get_alignment_in_bits() const;

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const type_base&) const;

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;

  type_base_sptr
  get_underlying_type() const;

  void
  set_underlying_type(const type_base_sptr&);

  virtual bool
  traverse(ir_node_visitor&);

  virtual ~typedef_decl();
};// end class typedef_decl

/// The abstraction for a data member context relationship.  This
/// relates a data member to its parent class.
///
/// The relationship carries properties like the offset of the data
/// member, if applicable.
class dm_context_rel : public context_rel
{
protected:
  struct priv;
  std::unique_ptr<priv> priv_;

public:
  dm_context_rel();

  dm_context_rel(scope_decl* s,
		 bool is_laid_out,
		 size_t offset_in_bits,
		 access_specifier a,
		 bool is_static);

  dm_context_rel(scope_decl* s);

  bool
  get_is_laid_out() const;

  void
  set_is_laid_out(bool f);

  size_t
  get_offset_in_bits() const;

  void
  set_offset_in_bits(size_t o);

  const var_decl*
  get_anonymous_data_member() const;

  void
  set_anonymous_data_member(var_decl *);

  bool
  operator==(const dm_context_rel& o) const;

  bool
  operator!=(const dm_context_rel& o) const;

  virtual ~dm_context_rel();
};// end class class_decl::dm_context_rel

bool
equals(const var_decl&, const var_decl&, change_kind*);

bool
equals_modulo_cv_qualifier(const array_type_def*, const array_type_def*);

/// Abstracts a variable declaration.
class var_decl : public virtual decl_base
{
  struct priv;
  std::unique_ptr<priv> priv_;

  // Forbidden
  var_decl();

  virtual void
  set_scope(scope_decl*);

public:

  /// Hasher for a var_decl type.
  struct hash;

  /// Equality functor to compare pointers to variable_decl.
  struct ptr_equal;

  var_decl(const string&	name,
	   type_base_sptr	type,
	   const location&	locus,
	   const string&	mangled_name,
	   visibility		vis = VISIBILITY_DEFAULT,
	   binding		bind = BINDING_NONE);

  virtual bool
  operator==(const decl_base&) const;

  const type_base_sptr
  get_type() const;

  const type_base*
  get_naked_type() const;

  binding
  get_binding() const;

  void
  set_binding(binding b);

  void
  set_symbol(const elf_symbol_sptr& sym);

  const elf_symbol_sptr&
  get_symbol() const;

  var_decl_sptr
  clone() const;

  interned_string
  get_id() const;

  virtual const interned_string&
  get_qualified_name(bool internal = false) const;

  virtual size_t
  get_hash() const;

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;

  string
  get_anon_dm_reliable_name(bool qualified = true) const;

  virtual bool
  traverse(ir_node_visitor& v);

  virtual ~var_decl();

  friend void
  set_data_member_offset(var_decl_sptr m, uint64_t o);

  friend uint64_t
  get_data_member_offset(const var_decl_sptr m);

  friend uint64_t
  get_data_member_offset(const var_decl& m);

  friend uint64_t
  get_absolute_data_member_offset(const var_decl& m);

  friend uint64_t
  get_absolute_data_member_offset(const var_decl_sptr& m);

  friend void
  set_data_member_is_laid_out(var_decl_sptr m, bool l);

  friend bool
  get_data_member_is_laid_out(const var_decl& m);

  friend bool
  get_data_member_is_laid_out(const var_decl_sptr m);
}; // end class var_decl

bool
equals(const function_decl&, const function_decl&, change_kind*);

/// Abstraction for a function declaration.
class function_decl : public virtual decl_base
{
  struct priv;
  // This priv pointer is not handled by a shared_ptr because
  // accessing the data members of the priv struct for this
  // function_decl shows up on performance profiles when dealing with
  // big binaries with a lot of types; dereferencing the shared_ptr
  // involves locking of some sort and that is slower than just
  // dereferencing a pointer likere here.  There are other types for
  // which the priv pointer is managed using shared_ptr just fine,
  // because those didn't show up during our performance profiling.
  priv* priv_;

public:
  /// Hasher for function_decl
  struct hash;

  /// Equality functor to compare pointers to function_decl
  struct ptr_equal;

  /// Abstraction for the parameter of a function.
  class parameter;

  /// Convenience typedef for a shared pointer on a @ref
  /// function_decl::parameter
  typedef shared_ptr<parameter> parameter_sptr;

  /// Convenience typedef for a vector of @ref parameter_sptr
  typedef std::vector<parameter_sptr> parameters;

  function_decl(const string& name,
		function_type_sptr function_type,
		bool declared_inline,
		const location& locus,
		const string& mangled_name,
		visibility vis,
		binding bind);

  function_decl(const string& name,
		type_base_sptr fn_type,
		bool declared_inline,
		const location& locus,
		const string& mangled_name = "",
		visibility vis = VISIBILITY_DEFAULT,
		binding bind = BINDING_GLOBAL);

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;

  string
  get_pretty_representation_of_declarator (bool internal = false) const;

  const std::vector<parameter_sptr >&
  get_parameters() const;

  void
  append_parameter(parameter_sptr parm);

  void
  append_parameters(std::vector<parameter_sptr >& parms);

  parameters::const_iterator
  get_first_non_implicit_parm() const;

  const function_type_sptr
  get_type() const;

  const function_type*
  get_naked_type() const;

  const type_base_sptr
  get_return_type() const;

  void
  set_type(const function_type_sptr& fn_type);

  void
  set_symbol(const elf_symbol_sptr& sym);

  const elf_symbol_sptr&
  get_symbol() const;

  bool
  is_declared_inline() const;

  binding
  get_binding() const;

  function_decl_sptr
  clone() const;

  virtual bool
  operator==(const decl_base& o) const;

  /// Return true iff the function takes a variable number of
  /// parameters.
  ///
  /// @return true if the function taks a variable number
  /// of parameters.
  bool
  is_variadic() const;

  virtual size_t
  get_hash() const;

  interned_string
  get_id() const;

  virtual bool
  traverse(ir_node_visitor&);

  virtual ~function_decl();
}; // end class function_decl

bool
operator==(const function_decl_sptr& l, const function_decl_sptr& r);

bool
operator!=(const function_decl_sptr& l, const function_decl_sptr& r);

bool
function_decls_alias(const function_decl& f1, const function_decl& f2);

bool
equals(const function_decl::parameter&,
       const function_decl::parameter&,
       change_kind*);

/// A comparison functor to compare pointer to instances of @ref
/// type_or_decl_base.
struct type_or_decl_base_comp
{
  /// Comparison operator for ABI artifacts.
  ///
  /// @param f the first ABI artifact to consider for the comparison.
  ///
  /// @param s the second  ABI artifact to consider for the comparison.
  ///
  /// @return true iff @p f is lexicographically less than than @p s.
  bool
  operator()(const type_or_decl_base *f,
	     const type_or_decl_base *s)
  {
    function_decl *f_fn = is_function_decl(f), *s_fn = is_function_decl(s);
    if (f_fn && s_fn)
      return function_decl_is_less_than(*f_fn, *s_fn);

    var_decl *f_var = is_var_decl(f), *s_var = is_var_decl(s);
    if (f_var && s_var)
      return get_name(f_var) < get_name(s_var);

    string l_repr = get_pretty_representation(f),
      r_repr = get_pretty_representation(s);

    return l_repr < r_repr;
  }

  /// Comparison operator for ABI artifacts.
  ///
  /// @param f the first ABI artifact to consider for the comparison.
  ///
  /// @param s the second  ABI artifact to consider for the comparison.
  ///
  /// @return true iff @p f is lexicographically less than than @p s.
  bool
  operator()(const type_or_decl_base_sptr& f,
	     const type_or_decl_base_sptr& s)
  {return operator()(f.get(), s.get());}
}; // end struct type_or_decl_base_comp

/// Abstraction of a function parameter.
class function_decl::parameter : public decl_base
{
  struct priv;
  std::unique_ptr<priv> priv_;

public:

  /// Hasher for an instance of function::parameter
  struct hash;

  parameter(const type_base_sptr	type,
	    unsigned			index,
	    const string&		name,
	    const location&		loc,
	    bool			variadic_marker = false);

  parameter(const type_base_sptr	type,
	    unsigned			index,
	    const string&		name,
	    const location&		loc,
	    bool			variadic_marker,
	    bool			is_artificial);

  parameter(const type_base_sptr	type,
	    const string&		name,
	    const location&		loc,
	    bool			variadic_marker = false,
	    bool			is_artificial	= false);

  parameter(const type_base_sptr	type,
	    unsigned			index = 0,
	    bool			variadic_marker = false);

  virtual ~parameter();

  const type_base_sptr
  get_type()const;

  interned_string
  get_type_name() const;

  const string
  get_type_pretty_representation() const;

  interned_string
  get_name_id() const;

  unsigned
  get_index() const;

  void
  set_index(unsigned i);

  bool
  get_variadic_marker() const;

  bool
  operator==(const parameter& o) const;

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  traverse(ir_node_visitor& v);

  virtual size_t
  get_hash() const;

  virtual void
  get_qualified_name(interned_string& qualified_name,
		     bool internal = false) const;

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;
}; // end class function_decl::parameter

bool
operator==(const function_decl::parameter_sptr&,
	   const function_decl::parameter_sptr&);

/// A hashing functor for a function_decl::parameter.
struct function_decl::parameter::hash
{
  size_t
  operator()(const function_decl::parameter&) const;

  size_t
  operator()(const function_decl::parameter*) const;

  size_t
  operator()(const function_decl::parameter_sptr) const;
}; // end struct function_decl::parameter::hash

function_decl::parameter*
is_function_parameter(const type_or_decl_base*);

function_decl::parameter_sptr
is_function_parameter(const type_or_decl_base_sptr tod);

bool
equals(const function_type&, const function_type&, change_kind*);

/// Abstraction of a function type.
class function_type : public virtual type_base
{
protected:
  virtual void on_canonical_type_set();

public:
  /// Hasher for an instance of function_type
  struct hash;

  /// Convenience typedef for a shared pointer on a @ref
  /// function_decl::parameter
  typedef shared_ptr<function_decl::parameter>	parameter_sptr;
  /// Convenience typedef for a vector of @ref parameter_sptr
  typedef std::vector<parameter_sptr>		parameters;

  struct priv;
  std::unique_ptr<priv> priv_;

private:
  function_type();

public:

  function_type(type_base_sptr		return_type,
		const parameters&	parms,
		size_t			size_in_bits,
		size_t			alignment_in_bits);

  function_type(type_base_sptr	return_type,
		size_t		size_in_bits,
		size_t		alignment_in_bits);

  function_type(const environment*	env,
		size_t		size_in_bits,
		size_t		alignment_in_bits);

  type_base_sptr
  get_return_type() const;

  void
  set_return_type(type_base_sptr t);

  const parameters&
  get_parameters() const;

  const parameter_sptr
  get_parm_at_index_from_first_non_implicit_parm(size_t) const;

  void
  set_parameters(const parameters &p);

  void
  append_parameter(parameter_sptr parm);

  bool
  is_variadic() const;

  parameters::const_iterator
  get_first_non_implicit_parm() const;

  parameters::const_iterator
  get_first_parm() const;

  const interned_string&
  get_cached_name(bool internal = false) const;

  virtual bool
  operator==(const type_base&) const;

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;

  virtual bool
  traverse(ir_node_visitor&);

  virtual ~function_type();

  friend bool
  equals(const function_type&, const function_type&, change_kind*);
};//end class function_type

/// The hashing functor for @ref function_type.
struct function_type::hash
{
  size_t
  operator()(const function_type& t) const;

  size_t
  operator()(const function_type* t) const;

  size_t
  operator()(const function_type_sptr t) const;
};// end struct function_type::hash

/// Abstracts the type of a class member function.
class method_type : public function_type
{
  struct priv;
  std::unique_ptr<priv> priv_;

  method_type();

public:

  /// Hasher for intances of method_type
  struct hash;

  method_type(type_base_sptr return_type,
	      class_or_union_sptr class_type,
	      const std::vector<function_decl::parameter_sptr>& parms,
	      bool is_const,
	      size_t size_in_bits,
	      size_t alignment_in_bits);

  method_type(type_base_sptr return_type,
	      type_base_sptr class_type,
	      const std::vector<function_decl::parameter_sptr>& parms,
	      bool is_const,
	      size_t size_in_bits,
	      size_t alignment_in_bits);

  method_type(class_or_union_sptr class_type,
	      bool is_const,
	      size_t size_in_bits,
	      size_t alignment_in_bits);

  method_type(const environment* env,
	      size_t size_in_bits,
	      size_t alignment_in_bits);

  class_or_union_sptr
  get_class_type() const;

  void
  set_class_type(const class_or_union_sptr& t);

  void set_is_const(bool);

  bool get_is_const() const;

  virtual ~method_type();

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;

  friend interned_string
  get_method_type_name(const method_type& fn_type, bool internal);
};// end class method_type.

/// The base class of templates.
class template_decl : public virtual decl_base
{
  class priv;
  std::unique_ptr<priv> priv_;

  template_decl();

public:

  /// Hasher.
  struct hash;

  template_decl(const environment*	env,
		const string&		name,
		const location&	locus,
		visibility		vis = VISIBILITY_DEFAULT);

  void
  add_template_parameter(const template_parameter_sptr p);

  const std::list<template_parameter_sptr>&
  get_template_parameters() const;

  virtual bool
  operator==(const template_decl& o) const;

  virtual ~template_decl();
};//end class template_decl

/// Base class for a template parameter.  Client code should use the
/// more specialized type_template_parameter,
/// non_type_template_parameter and template_template_parameter below.
class template_parameter
{
  class priv;
  std::unique_ptr<priv> priv_;

  // Forbidden
  template_parameter();

 public:

  /// Hashers.
  struct hash;
  struct dynamic_hash;
  struct shared_ptr_hash;

  template_parameter(unsigned			index,
		     template_decl_sptr	enclosing_tdecl);

  virtual bool
  operator==(const template_parameter&) const;

  bool
  operator!=(const template_parameter&) const;

  unsigned
  get_index() const;

  const template_decl_sptr
  get_enclosing_template_decl() const;

  bool
  get_hashing_has_started() const;

  void
  set_hashing_has_started(bool f) const;

  virtual ~template_parameter();
};//end class template_parameter

struct template_decl::hash
{
    size_t
    operator()(const template_decl& t) const;
};// end struct template_decl::hash

/// Abstracts a type template parameter.
class type_tparameter : public template_parameter, public virtual type_decl
{
  class priv;
  std::unique_ptr<priv> priv_;

  // Forbidden
  type_tparameter();

public:

  /// Hasher.
  struct hash;

  type_tparameter(unsigned		index,
		  template_decl_sptr	enclosing_tdecl,
		  const string&	name,
		  const location&	locus);

  virtual bool
  operator==(const type_base&) const;

  virtual bool
  operator==(const template_parameter&) const;

  virtual bool
  operator==(const type_tparameter&) const;

  virtual ~type_tparameter();
};// end class type_tparameter.

/// Abstracts non type template parameters.
class non_type_tparameter : public template_parameter, public virtual decl_base
{
  class priv;
  std::unique_ptr<priv> priv_;

  type_base_wptr type_;

  // Forbidden
  non_type_tparameter();

public:
  /// Hasher.
  struct hash;

  non_type_tparameter(unsigned			index,
		      template_decl_sptr	enclosing_tdecl,
		      const string&		name,
		      type_base_sptr		type,
		      const location&		locus);
  virtual size_t
  get_hash() const;

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const template_parameter&) const;

  const type_base_sptr
  get_type() const;

  virtual ~non_type_tparameter();
};// end class non_type_tparameter

/// Hasher for the @ref non_type_tparameter type.
struct non_type_tparameter::hash
{
  size_t
  operator()(const non_type_tparameter& t) const;

  size_t
  operator()(const non_type_tparameter* t) const;
};

class template_tparameter;

/// Abstracts a template template parameter.
class template_tparameter : public type_tparameter, public template_decl
{
  class priv;
  std::unique_ptr<priv> priv_;

  // Forbidden
  template_tparameter();

public:

  /// A hasher for instances of template_tparameter
  struct hash;

  template_tparameter(unsigned			index,
		      template_decl_sptr	enclosing_tdecl,
		      const string&		name,
		      const location&		locus);

  virtual bool
  operator==(const type_base&) const;

  virtual bool
  operator==(const template_parameter&) const;

  virtual bool
  operator==(const template_decl&) const;

  virtual ~template_tparameter();
};

/// This abstracts a composition of types based on template type
/// parameters.  The result of the composition is a type that can be
/// referred to by a template non-type parameter.  Instances of this
/// type can appear at the same level as template parameters, in the
/// scope of a template_decl.
class type_composition : public template_parameter, public virtual decl_base
{
  class priv;
  std::unique_ptr<priv> priv_;

  type_composition();

public:
  struct hash;

  type_composition(unsigned		index,
		   template_decl_sptr	tdecl,
		   type_base_sptr	composed_type);

  const type_base_sptr
  get_composed_type() const;

  void
  set_composed_type(type_base_sptr t);

  virtual size_t
  get_hash() const;

  virtual ~type_composition();
};

/// Hasher for the @ref type_composition type.
struct type_composition::hash
{
  size_t
  operator()(const type_composition& t) const;

  size_t
  operator()(const type_composition* t) const;

}; //struct type_composition::hash

/// Abstract a function template declaration.
class function_tdecl : public template_decl, public scope_decl
{
  class priv;
  std::unique_ptr<priv> priv_;

  // Forbidden
  function_tdecl();

public:

  /// Hash functor for function templates.
  struct hash;
  struct shared_ptr_hash;

  function_tdecl(const environment*	env,
		 const location&	locus,
		 visibility		vis = VISIBILITY_DEFAULT,
		 binding		bind = BINDING_NONE);

  function_tdecl(function_decl_sptr	pattern,
		 const location&	locus,
		 visibility		vis = VISIBILITY_DEFAULT,
		 binding		bind = BINDING_NONE);

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const template_decl&) const;

  virtual bool
  operator==(const function_tdecl&) const;

  void
  set_pattern(shared_ptr<function_decl> p);

  shared_ptr<function_decl>
  get_pattern() const;

  binding
  get_binding() const;

  virtual bool
  traverse(ir_node_visitor& v);

  virtual ~function_tdecl();
}; // end class function_tdecl.

/// Abstract a class template.
class class_tdecl : public template_decl, public scope_decl
{
  class priv;
  std::unique_ptr<priv> priv_;

  // Forbidden
  class_tdecl();

public:

  /// Hashers.
  struct hash;
  struct shared_ptr_hash;

  class_tdecl(const environment* env, const location& locus,
	      visibility vis = VISIBILITY_DEFAULT);

  class_tdecl(class_decl_sptr	pattern,
	      const location&	locus,
	      visibility	vis = VISIBILITY_DEFAULT);

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const template_decl&) const;

  virtual bool
  operator==(const class_tdecl&) const;

  void
  set_pattern(class_decl_sptr p);

  shared_ptr<class_decl>
  get_pattern() const;

  virtual bool
  traverse(ir_node_visitor& v);

  virtual ~class_tdecl();
};// end class class_tdecl

/// The base class for member types, data members and member
/// functions.  Its purpose is mainly to carry the access specifier
/// (and possibly other properties that might be shared by all class
/// members) for the member.
class member_base
{
protected:
  enum access_specifier access_;
  bool			is_static_;

private:
  // Forbidden
  member_base();

public:
  /// Hasher.
  struct hash;

  member_base(access_specifier a, bool is_static = false)
    : access_(a), is_static_(is_static)
  {}

  /// Getter for the access specifier of this member.
  ///
  /// @return the access specifier for this member.
  access_specifier
  get_access_specifier() const
  {return access_;}

  /// Setter for the access specifier of this member.
  ///
  /// @param a the new access specifier.
  void
  set_access_specifier(access_specifier a)
  {access_ = a;}

  /// @return true if the member is static, false otherwise.
  bool
  get_is_static() const
  {return is_static_;}

  /// Set a flag saying if the parameter is static or not.
  ///
  /// @param f set to true if the member is static, false otherwise.
  void
  set_is_static(bool f)
  {is_static_ = f;}

  virtual bool
  operator==(const member_base& o) const;
};// end class member_base

/// Abstraction of the declaration of a method.
class method_decl : public function_decl
{
  method_decl();

  virtual void
  set_scope(scope_decl*);

public:

  method_decl(const string& name, method_type_sptr type,
	      bool declared_inline, const location& locus,
	      const string& mangled_name = "",
	      visibility vis = VISIBILITY_DEFAULT,
	      binding	bind = BINDING_GLOBAL);

  method_decl(const string& name,
	      function_type_sptr type,
	      bool declared_inline,
	      const location& locus,
	      const string& mangled_name = "",
	      visibility vis  = VISIBILITY_DEFAULT,
	      binding	bind = BINDING_GLOBAL);

  method_decl(const string& name, type_base_sptr type,
	      bool declared_inline, const location& locus,
	      const string& mangled_name = "",
	      visibility vis = VISIBILITY_DEFAULT,
	      binding bind = BINDING_GLOBAL);

  virtual void
  set_linkage_name(const string&);

  /// @return the type of the current instance of the
  /// method_decl.
  const method_type_sptr
  get_type() const;

  void
  set_type(const method_type_sptr fn_type)
  {function_decl::set_type(fn_type);}

  friend bool
  get_member_function_is_ctor(const function_decl&);

  friend void
  set_member_function_is_ctor(function_decl&, bool);

  friend void
  set_member_function_is_ctor(const function_decl_sptr&, bool);

  friend bool
  get_member_function_is_dtor(const function_decl&);

  friend void
  set_member_function_is_dtor(function_decl&, bool);

  friend void
  set_member_function_is_dtor(const function_decl_sptr&, bool);

  friend bool
  get_member_function_is_static(const function_decl&);

  friend void
  set_member_function_is_static(const function_decl&, bool);

  friend bool
  get_member_function_is_const(const function_decl&);

  friend void
  set_member_function_is_const(function_decl&, bool);

  friend void
  set_member_function_is_const(const function_decl_sptr&, bool);

  friend bool
  member_function_has_vtable_offset(const function_decl&);

  friend ssize_t
  get_member_function_vtable_offset(const function_decl&);

  friend void
  set_member_function_vtable_offset(function_decl&, ssize_t);

  friend void
  set_member_function_vtable_offset(const function_decl_sptr&, ssize_t);

  friend bool
  get_member_function_is_virtual(const function_decl&);

  friend void
  set_member_function_is_virtual(function_decl&, bool);

  virtual ~method_decl();
};// end class method_decl

bool
operator==(const method_decl_sptr& l, const method_decl_sptr& r);

bool
operator!=(const method_decl_sptr& l, const method_decl_sptr& r);

/// The base type of @ref class_decl and @ref union_decl
class class_or_union : public scope_type_decl
{
public:
  struct priv;
  priv *priv_;

private:
  // Forbidden
  class_or_union();

protected:

  virtual decl_base_sptr
  add_member_decl(const decl_base_sptr&);

  virtual decl_base_sptr
  insert_member_decl(decl_base_sptr member, declarations::iterator before);

  virtual void
  remove_member_decl(decl_base_sptr);

  void
  maybe_fixup_members_of_anon_data_member(var_decl_sptr& anon_dm);

public:
  /// Hasher.
  struct hash;

  /// Convenience typedef
  /// @{
  typedef vector<type_base_sptr>		member_types;
  typedef vector<var_decl_sptr>		data_members;
  typedef vector<method_decl_sptr>		member_functions;
  typedef unordered_map<ssize_t, member_functions> virtual_mem_fn_map_type;
  typedef unordered_map<string, method_decl*> string_mem_fn_ptr_map_type;
  typedef unordered_map<string, method_decl_sptr> string_mem_fn_sptr_map_type;
  /// @}

  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& mbrs, data_members& data_mbrs,
		 member_functions& member_fns);

  class_or_union(const environment* env, const string& name,
		 size_t size_in_bits, size_t align_in_bits,
		 const location& locus, visibility vis);

  class_or_union(const environment* env, const string& name,
		 bool is_declaration_only = true);

  virtual void
  set_size_in_bits(size_t);

  virtual size_t
  get_size_in_bits() const;

  virtual size_t
  get_alignment_in_bits() const;

  virtual void
  set_alignment_in_bits(size_t);

 void
  insert_member_type(type_base_sptr t,
		     declarations::iterator before);

  void
  add_member_type(type_base_sptr t);

  type_base_sptr
  add_member_type(type_base_sptr t, access_specifier a);

  void
  remove_member_type(type_base_sptr t);

  const member_types&
  get_member_types() const;

  virtual size_t
  get_num_anonymous_member_classes() const;

  virtual size_t
  get_num_anonymous_member_unions() const;

  virtual size_t
  get_num_anonymous_member_enums() const;

  type_base_sptr
  find_member_type(const string& name) const;

  void
  add_data_member(var_decl_sptr v, access_specifier a,
		  bool is_laid_out, bool is_static,
		  size_t offset_in_bits);

  const data_members&
  get_data_members() const;

  const var_decl_sptr
  find_data_member(const string&) const;

  const var_decl_sptr
  find_data_member(const var_decl_sptr&) const;

  const var_decl_sptr
  find_anonymous_data_member(const var_decl_sptr&) const;

  const data_members&
  get_non_static_data_members() const;

  void
  add_member_function(method_decl_sptr f,
		      access_specifier a,
		      bool is_static, bool is_ctor,
		      bool is_dtor, bool is_const);

  void
  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);

  const member_functions&
  get_member_functions() const;

  const method_decl*
  find_member_function(const string& mangled_name) const;

  method_decl*
  find_member_function(const string& mangled_name);

  method_decl_sptr
  find_member_function_sptr(const string& mangled_name);

  const method_decl*
  find_member_function_from_signature(const string& s) const;

  method_decl*
  find_member_function_from_signature(const string& s);

  void
  add_member_function_template(member_function_template_sptr);

  const member_function_templates&
  get_member_function_templates() const;

  void
  add_member_class_template(member_class_template_sptr m);

  const member_class_templates&
  get_member_class_templates() const;

  bool
  has_no_member() const;

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const type_base&) const;

  virtual bool
  operator==(const class_or_union&) const;

  virtual bool
  traverse(ir_node_visitor& v);

  virtual ~class_or_union();

  friend method_decl_sptr
  copy_member_function(class_or_union_sptr& t,
		       const method_decl*m);

  friend method_decl_sptr
  copy_member_function(class_or_union_sptr& t,
		       const method_decl_sptr& m);

  friend void
  fixup_virtual_member_function(method_decl_sptr method);

  friend void
  set_member_is_static(decl_base& d, bool s);

  friend bool
  equals(const class_or_union&, const class_or_union&, change_kind*);

  friend bool
  equals(const class_decl&, const class_decl&, change_kind*);

  friend class method_decl;
  friend class class_decl;
}; // end class class_or_union

method_decl_sptr
copy_member_function(const class_or_union_sptr& clazz,
		     const method_decl_sptr& f);

method_decl_sptr
copy_member_function(const class_or_union_sptr& clazz,
		     const method_decl* f);

bool
operator==(const class_or_union_sptr& l, const class_or_union_sptr& r);

bool
operator!=(const class_or_union_sptr& l, const class_or_union_sptr& r);

/// Hasher for the @ref class_or_union type
struct class_or_union::hash
{
  size_t
  operator()(const class_or_union& t) const;

  size_t
  operator()(const class_or_union* t) const;
}; // end struct class_decl::hash

/// Abstracts a class declaration.
class class_decl : public class_or_union
{
  // Forbidden
  class_decl();

protected:

  virtual decl_base_sptr
  insert_member_decl(decl_base_sptr member, declarations::iterator before);

public:
  /// Hasher.
  struct hash;

  /// Forward declarations.
  class base_spec;

  /// Convenience typedef
  /// @{
  typedef shared_ptr<base_spec>			base_spec_sptr;
  typedef vector<base_spec_sptr>			base_specs;

  /// @}

protected:
  virtual void
  on_canonical_type_set();

private:
  struct priv;
  // This priv it's not handled by a shared_ptr because accessing the
  // data members of the priv struct for this class_decl shows up on
  // performance profiles when dealing with big binaries with a lot of
  // types; dereferencing the shared_ptr involves locking of some sort
  // and that is slower than just dereferencing a pointer likere here.
  // There are other types for which the priv pointer is managed using
  // shared_ptr just fine, because those didn't show up during our
  // performance profiling.
  priv * priv_;

public:

  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& mbrs, data_members& data_mbrs,
	     member_functions& member_fns);

  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& mbrs, data_members& data_mbrs,
	     member_functions& member_fns, bool is_anonymous);

  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);

  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);

  class_decl(const environment* env, const string& name, bool is_struct,
	     bool is_declaration_only = true);

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;

  void
  is_struct(bool f);

  bool
  is_struct() const;

  void
  add_base_specifier(shared_ptr<base_spec> b);

  const base_specs&
  get_base_specifiers() const;

  class_decl_sptr
  find_base_class(const string&) const;

  const member_functions&
  get_virtual_mem_fns() const;

  const virtual_mem_fn_map_type&
  get_virtual_mem_fns_map() const;

  void
  sort_virtual_mem_fns();

  bool
  has_no_base_nor_member() const;

  bool
  has_virtual_member_functions() const;

  bool
  has_virtual_bases() const;

  bool
  has_vtable() const;

  ssize_t
  get_biggest_vtable_offset() const;

  virtual size_t
  get_hash() const;

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const type_base&) const;

  virtual bool
  operator==(const class_decl&) const;

  virtual bool
  traverse(ir_node_visitor& v);

  virtual ~class_decl();

  friend void
  fixup_virtual_member_function(method_decl_sptr method);

  friend void
  set_member_is_static(decl_base& d, bool s);

  friend bool
  equals(const class_decl&, const class_decl&, change_kind*);

  friend class method_decl;
  friend class class_or_union;
};// end class class_decl

bool
equals(const class_decl&, const class_decl&, change_kind*);

method_decl_sptr
copy_member_function(const class_decl_sptr& clazz,
		     const method_decl_sptr& f);

method_decl_sptr
copy_member_function(const class_decl_sptr& clazz,
		     const method_decl* f);
void
fixup_virtual_member_function(method_decl_sptr method);

/// Hasher for the @ref class_decl type
struct class_decl::hash
{
  size_t
  operator()(const class_decl& t) const;

  size_t
  operator()(const class_decl* t) const;
}; // end struct class_decl::hash

enum access_specifier
get_member_access_specifier(const decl_base&);

enum access_specifier
get_member_access_specifier(const decl_base_sptr&);

void
set_member_access_specifier(decl_base&,
			    access_specifier);

void
set_member_access_specifier(const decl_base_sptr&,
			    access_specifier);

std::ostream&
operator<<(std::ostream&, access_specifier);

bool
operator==(const class_decl_sptr& l, const class_decl_sptr& r);

bool
operator!=(const class_decl_sptr& l, const class_decl_sptr& r);

bool
equals(const class_decl::base_spec&,
       const class_decl::base_spec&,
       change_kind*);

/// Abstraction of a base specifier in a class declaration.
class class_decl::base_spec : public member_base,
			      public virtual decl_base
{
  struct priv;
  std::unique_ptr<priv> priv_;

  // Forbidden
  base_spec();

public:

  /// Hasher.
  struct hash;

  base_spec(const class_decl_sptr& base, access_specifier a,
	    long offset_in_bits = -1, bool is_virtual = false);

  base_spec(const type_base_sptr& base, access_specifier a,
	    long offset_in_bits = -1, bool is_virtual = false);

  virtual ~base_spec();

  class_decl_sptr
  get_base_class() const;

  bool
  get_is_virtual() const;

  long
  get_offset_in_bits() const;

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const member_base&) const;

  virtual size_t
  get_hash() const;

  virtual bool
  traverse(ir_node_visitor&);
};// end class class_decl::base_spec

bool
operator==(const class_decl::base_spec_sptr& l,
	   const class_decl::base_spec_sptr& r);

bool
operator!=(const class_decl::base_spec_sptr& l,
	   const class_decl::base_spec_sptr& r);

class_decl::base_spec*
is_class_base_spec(type_or_decl_base*);

class_decl::base_spec_sptr
is_class_base_spec(type_or_decl_base_sptr);

/// Abstracts a union type declaration.
class union_decl : public class_or_union
{
  // Forbid
  union_decl();

public:

  union_decl(const environment* env, const string& name,
	     size_t size_in_bits, const location& locus,
	     visibility vis, member_types& mbrs,
	     data_members& data_mbrs, member_functions& member_fns);

  union_decl(const environment* env, const string& name,
	     size_t size_in_bits, const location& locus,
	     visibility vis, member_types& mbrs,
	     data_members& data_mbrs, member_functions& member_fns,
	     bool is_anonymous);

  union_decl(const environment* env, const string& name,
	     size_t size_in_bits, const location& locus,
	     visibility vis);

  union_decl(const environment* env, const string& name,
	     size_t size_in_bits, const location& locus,
	     visibility vis, bool is_anonymous);

  union_decl(const environment* env, const string& name,
	     bool is_declaration_only = true);

  virtual string
  get_pretty_representation(bool internal = false,
			    bool qualified_name = true) const;

  virtual bool
  operator==(const decl_base&) const;

  virtual bool
  operator==(const type_base&) const;

  virtual bool
  operator==(const union_decl&) const;

  virtual bool
  traverse(ir_node_visitor& v);

  virtual ~union_decl();
}; // union_decl

bool
equals(const union_decl&, const union_decl&, change_kind*);

method_decl_sptr
copy_member_function(const union_decl_sptr& union_type,
		     const method_decl_sptr& f);

method_decl_sptr
copy_member_function(const union_decl_sptr& union_type,
		     const method_decl* f);

bool
operator==(const union_decl_sptr& l, const union_decl_sptr& r);

bool
operator!=(const union_decl_sptr& l, const union_decl_sptr& r);

/// Abstraction of a member function context relationship.  This
/// relates a member function to its parent class.
class mem_fn_context_rel : public context_rel
{
protected:
  bool		is_virtual_;
  ssize_t	vtable_offset_in_bits_;
  bool		is_constructor_;
  bool		is_destructor_;
  bool		is_const_;

public:
  mem_fn_context_rel()
    : context_rel(),
      is_virtual_(false),
      vtable_offset_in_bits_(-1),
      is_constructor_(false),
      is_destructor_(false),
      is_const_(false)
  {}

  mem_fn_context_rel(scope_decl* s)
    : context_rel(s),
      is_virtual_(false),
      vtable_offset_in_bits_(-1),
      is_constructor_(false),
      is_destructor_(false),
      is_const_(false)
  {}

  mem_fn_context_rel(scope_decl* s,
		     bool is_constructor,
		     bool is_destructor,
		     bool is_const,
		     bool is_virtual,
		     size_t vtable_offset_in_bits,
		     access_specifier access,
		     bool is_static)
    : context_rel(s, access, is_static),
      is_virtual_(is_virtual),
      vtable_offset_in_bits_(vtable_offset_in_bits),
      is_constructor_(is_constructor),
      is_destructor_(is_destructor),
      is_const_(is_const)
  {}

  bool
  is_virtual() const
  {return is_virtual_;}

  void
  is_virtual(bool is_virtual)
  {is_virtual_ = is_virtual;}

  /// Getter for the vtable offset property.
  ///
  /// This is the vtable offset of the member function of this
  /// relation.
  ///
  /// @return the vtable offset property of the relation.
  size_t
  vtable_offset() const
  {return vtable_offset_in_bits_;}

  /// Setter for the vtable offset property.
  ///
  /// This is the vtable offset of the member function of this
  /// relation.
  ///
  /// @partam s the new vtable offset.
  void
  vtable_offset(size_t s)
  {vtable_offset_in_bits_ = s;}

  /// Getter for the 'is-constructor' property.
  ///
  /// This tells if the member function of this relation is a
  /// constructor.
  ///
  /// @return the is-constructor property of the relation.
  bool
  is_constructor() const
  {return is_constructor_;}

  /// Setter for the 'is-constructor' property.
  ///
  /// @param f the new value of the the property.  Is true if this is
  /// for a constructor, false otherwise.
  void
  is_constructor(bool f)
  {is_constructor_ = f;}

  /// Getter for the 'is-destructor' property.
  ///
  /// Tells if the member function of this relation is a destructor.
  ///
  /// @return the is-destructor property of the relation;
  bool
  is_destructor() const
  {return is_destructor_;}

  /// Setter for the 'is-destructor' property.
  ///
  /// @param f the new value of the property.  Is true if this is for
  /// a destructor, false otherwise.
  void
  is_destructor(bool f)
  {is_destructor_ = f;}

  /// Getter for the 'is-const' property.
  ///
  /// Tells if the member function of this relation is a const member
  /// function.
  ///
  /// @return the 'is-const' property of the relation.
  bool
  is_const() const
  {return is_const_;}

  /// Setter for the 'is-const' property.
  ///
  /// @param f the new value of the property.  Is true if this is for
  /// a const entity, false otherwise.
  void
  is_const(bool f)
  {is_const_ = f;}

  virtual ~mem_fn_context_rel();
}; // end class mem_fn_context_rel

method_decl*
is_method_decl(const type_or_decl_base*);

method_decl*
is_method_decl(const type_or_decl_base&);

method_decl_sptr
is_method_decl(const type_or_decl_base_sptr&);

const var_decl*
lookup_data_member(const type_base* type,
		   const char* dm_name);

const function_decl::parameter*
get_function_parameter(const decl_base* fun,
		       unsigned parm_num);

/// Abstract a member function template.
class member_function_template : public member_base, public virtual decl_base
{
  bool is_constructor_;
  bool is_const_;
  shared_ptr<function_tdecl> fn_tmpl_;

  // Forbiden
  member_function_template();

public:
  /// Hasher.
  struct hash;

  member_function_template(function_tdecl_sptr f,
			   access_specifier access, bool is_static,
			   bool is_constructor, bool is_const)
    : type_or_decl_base(f->get_environment()),
      decl_base(f->get_environment(), f->get_name(), location()),
      member_base(access, is_static), is_constructor_(is_constructor),
      is_const_(is_const), fn_tmpl_(f)
  {}

  bool
  is_constructor() const
  {return is_constructor_;}

  bool
  is_const() const
  {return is_const_;}

  operator const function_tdecl& () const
  {return *fn_tmpl_;}

  function_tdecl_sptr
  as_function_tdecl() const
  {return fn_tmpl_;}

  virtual bool
  operator==(const member_base& o) const;

  virtual bool
  traverse(ir_node_visitor&);
};// end class member_function_template

bool
operator==(const member_function_template_sptr& l,
	   const member_function_template_sptr& r);

bool
operator!=(const member_function_template_sptr& l,
	   const member_function_template_sptr& r);

/// Abstracts a member class template template
class member_class_template
  : public member_base,
    public virtual decl_base
{
  shared_ptr<class_tdecl> class_tmpl_;

  // Forbidden
  member_class_template();

public:

  /// Hasher.
  struct hash;

  member_class_template(class_tdecl_sptr c,
			access_specifier access, bool is_static)
    : type_or_decl_base(c->get_environment()),
      decl_base(c->get_environment(), c->get_name(), location()),
      member_base(access, is_static),
      class_tmpl_(c)
  {}

  operator const class_tdecl& () const
  { return *class_tmpl_; }

  class_tdecl_sptr
  as_class_tdecl() const
  {return class_tmpl_;}

  virtual bool
  operator==(const member_base& o) const;

  virtual bool
  operator==(const member_class_template&) const;

  virtual bool
  traverse(ir_node_visitor& v);
};// end class member_class_template

bool
operator==(const member_class_template_sptr& l,
	   const member_class_template_sptr& r);

bool
operator!=(const member_class_template_sptr& l,
	   const member_class_template_sptr& r);

// Forward declarations for select nested hashers.
struct type_base::shared_ptr_hash
{
  size_t
  operator()(const shared_ptr<type_base> t) const;
};

struct type_base::dynamic_hash
{
  size_t
  operator()(const type_base* t) const;
};

/// A hashing functor for instances and pointers of @ref var_decl.
struct var_decl::hash
{
  size_t
  operator()(const var_decl& t) const;

  size_t
  operator()(const var_decl* t) const;
}; //end struct var_decl::hash

/// A comparison functor for pointers to @ref var_decl.
struct var_decl::ptr_equal
{
  /// Return true if the two instances of @ref var_decl are equal.
  ///
  /// @param l the first variable to compare.
  ///
  /// @param r the second variable to compare.
  ///
  /// @return true if @p l equals @p r.
  bool
  operator()(const var_decl* l, const var_decl* r) const
  {
    if (l == r)
      return true;
    if (!!l != !!r)
      return false;
    return (*l == *r);
  }
};// end struct var_decl::ptr_equal

/// A hashing functor fo instances and pointers of @ref function_decl.
struct function_decl::hash
{
  size_t
  operator()(const function_decl& t) const;

  size_t
  operator()(const function_decl* t) const;
};//end struct function_decl::hash

/// Equality functor for instances of @ref function_decl
struct function_decl::ptr_equal
{
  /// Tests if two pointers to @ref function_decl are equal.
  ///
  /// @param l the first pointer to @ref function_decl to consider in
  /// the comparison.
  ///
  /// @param r the second pointer to @ref function_decl to consider in
  /// the comparison.
  ///
  /// @return true if the two functions @p l and @p r are equal, false
  /// otherwise.
  bool
  operator()(const function_decl* l, const function_decl* r) const
  {
    if (l == r)
      return true;
    if (!!l != !!r)
      return false;
    return (*l == *r);
  }
};// function_decl::ptr_equal

/// The hashing functor for class_decl::base_spec.
struct class_decl::base_spec::hash
{
  size_t
  operator()(const base_spec& t) const;
};

/// The hashing functor for member_base.
struct member_base::hash
{
  size_t
  operator()(const member_base& m) const;
};

/// The hashing functor for member_function_template.
struct member_function_template::hash
{
  size_t
  operator()(const member_function_template& t) const;
};

/// The hashing functor for member_class_template
struct member_class_template::hash
{
  size_t
  operator()(const member_class_template& t) const;
};

struct function_tdecl::hash
{
  size_t
  operator()(const function_tdecl& t) const;
};

struct function_tdecl::shared_ptr_hash
{
  size_t
  operator()(const shared_ptr<function_tdecl> f) const;
};

struct class_tdecl::hash
{
  size_t
  operator()(const class_tdecl& t) const;
};

struct class_tdecl::shared_ptr_hash
{
  size_t
  operator()(const shared_ptr<class_tdecl> t) const;
};

/// The base class for the visitor type hierarchy used for traversing
/// a translation unit.
///
/// Client code willing to get notified for a certain kind of node
/// during the IR traversal might want to define a visitor class that
/// inherit ir_node_visitor, overload the ir_node_visitor::visit_begin()
/// or ir_node_visitor::visit_end() method of its choice, and provide
/// and implementation for it.  If either
/// ir_node_visitor::visit_begin() or ir_node_visitor::visit_end()
/// return false, it means the traversal has to stop immediately after
/// the methods' return.  If the methods return true, it means the
/// traversal keeps going.
///
/// That new visitor class would then be passed to e.g,
/// translation_unit::traverse or to the traverse method of any type
/// where the traversal is supposed to start from.
class ir_node_visitor : public node_visitor_base
{
  struct priv;
  std::unique_ptr<priv> priv_;

public:

  ir_node_visitor();

  virtual ~ir_node_visitor();

  void allow_visiting_already_visited_type_node(bool);
  bool allow_visiting_already_visited_type_node() const;
  void mark_type_node_as_visited(type_base *);
  void forget_visited_type_nodes();
  bool type_node_has_been_visited(type_base*) const;

  virtual bool visit_begin(decl_base*);
  virtual bool visit_end(decl_base*);

  virtual bool visit_begin(scope_decl*);
  virtual bool visit_end(scope_decl*);

  virtual bool visit_begin(type_base*);
  virtual bool visit_end(type_base*);

  virtual bool visit_begin(scope_type_decl*);
  virtual bool visit_end(scope_type_decl*);

  virtual bool visit_begin(type_decl*);
  virtual bool visit_end(type_decl*);

  virtual bool visit_begin(namespace_decl*);
  virtual bool visit_end(namespace_decl*);

  virtual bool visit_begin(qualified_type_def*);
  virtual bool visit_end(qualified_type_def*);

  virtual bool visit_begin(pointer_type_def*);
  virtual bool visit_end(pointer_type_def*);

  virtual bool visit_begin(reference_type_def*);
  virtual bool visit_end(reference_type_def*);

  virtual bool visit_begin(array_type_def*);
  virtual bool visit_end(array_type_def*);

  virtual bool visit_begin(array_type_def::subrange_type*);
  virtual bool visit_end(array_type_def::subrange_type*);

  virtual bool visit_begin(enum_type_decl*);
  virtual bool visit_end(enum_type_decl*);

  virtual bool visit_begin(typedef_decl*);
  virtual bool visit_end(typedef_decl*);

  virtual bool visit_begin(function_type*);
  virtual bool visit_end(function_type*);

  virtual bool visit_begin(var_decl*);
  virtual bool visit_end(var_decl*);

  virtual bool visit_begin(function_decl*);
  virtual bool visit_end(function_decl*);

  virtual bool visit_begin(function_decl::parameter*);
  virtual bool visit_end(function_decl::parameter*);

  virtual bool visit_begin(function_tdecl*);
  virtual bool visit_end(function_tdecl*);

  virtual bool visit_begin(class_tdecl*);
  virtual bool visit_end(class_tdecl*);

  virtual bool visit_begin(class_or_union *);
  virtual bool visit_end(class_or_union *);

  virtual bool visit_begin(class_decl*);
  virtual bool visit_end(class_decl*);

  virtual bool visit_begin(union_decl*);
  virtual bool visit_end(union_decl*);

  virtual bool visit_begin(class_decl::base_spec*);
  virtual bool visit_end(class_decl::base_spec*);

  virtual bool visit_begin(member_function_template*);
  virtual bool visit_end(member_function_template*);

  virtual bool visit_begin(member_class_template*);
  virtual bool visit_end(member_class_template*);
}; // end struct ir_node_visitor

// Debugging facility
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);

}// end namespace ir
} // end namespace abigail
#endif // __ABG_IR_H__
