/* dwarfpp: C++ binding for a useful subset of libdwarf, plus extra goodies.
 * 
 * dies.hpp: classes and methods specific to each DIE tag
 *
 * Copyright (c) 2008--17, Stephen Kell. For licensing information, see the
 * LICENSE file in the root of the libdwarfpp tree.
 */

#ifndef DWARFPP_DIES_HPP_
#define DWARFPP_DIES_HPP_

#include <iostream>
#include <utility>
#include <memory>
#include <boost/icl/interval_map.hpp>
#include <boost/algorithm/string.hpp>
#include <unordered_set>
#include <stack>
#include <set>
#include <boost/optional/optional.hpp> // until we are using C++17
#include <srk31/rotate.hpp>

#include "dwarfpp/root.hpp"
#include "dwarfpp/expr.hpp"
#include "dwarfpp/iter.hpp"

namespace dwarf
{
	using std::string;
	using std::vector;
	using std::stack;
	using std::set;
	using std::unordered_set;
	using std::unordered_map;
	using std::endl;
	using boost::optional;
	using std::shared_ptr;
	
	namespace core
	{
/****************************************************************/
/* begin generated ADT includes								 */
/****************************************************************/
#define forward_decl(t) class t ## _die;
#define declare_base(base) base ## _die
#define initialize_base(fragment) /*fragment ## _die(s, std::move(h))*/
#define constructor(fragment, ...) /* "..." is base inits */ \
		fragment ## _die(spec& s, Die&& h) : basic_die(s, std::move(h)) {}
/* #define declare_bases(first_base, ...) first_base , ##__VA_ARGS__ */
#define begin_class(fragment, base_inits, ...) \
	struct fragment ## _die : virtual __VA_ARGS__ { \
	friend struct dwarf_current_factory_t; \
	protected: /* dummy constructor used only to construct dummy instances */\
		fragment ## _die(spec& s) : basic_die(s) {} \
		/* constructor used only to construct in-memory instances */\
		fragment ## _die(spec& s, root_die& r) : basic_die(s, r) {} \
	public: /* main constructor */ \
		constructor(fragment, base_inits /* NOTE: base_inits is expanded via ',' into varargs list */) \
	protected: /* protected constructor that doesn't touch basic_die */ \
		fragment ## _die() {} \
	public: /* extra decls should be public */
#define base_initializations(...) __VA_ARGS__
#define end_class(fragment) \
	};

#define stored_type_string std::string
#define stored_type_flag bool
#define stored_type_unsigned Dwarf_Unsigned
#define stored_type_signed Dwarf_Signed
#define stored_type_offset Dwarf_Off
#define stored_type_half Dwarf_Half
#define stored_type_ref Dwarf_Off
#define stored_type_tag Dwarf_Half
#define stored_type_loclist dwarf::encap::loclist
#define stored_type_address dwarf::encap::attribute_value::address
#define stored_type_refiter iterator_df<basic_die>
#define stored_type_refiter_is_type iterator_df<type_die>
#define stored_type_rangelist dwarf::encap::rangelist

/* This is libdwarf-specific. This might be okay -- 
 * depends if we want a separate class hierarchy for the 
 * encap-style ones (like in encap::). Yes, we probably do.
 * BUT things like type_die should still be the supertype, right?
 * ARGH. 
 * I think we can use virtuality to deal with this in one go. 
 * Define a new encapsulated_die base class (off of basic_die) 
 * which redefines all the get_() stuff in one go. 
 * and then an encapsulated version of any type_die can use
 * virtual inheritance to wire its getters up to those versions. 
 * ARGH: no, we need another round of these macros to enumerate all the getters. 
 */
#define attr_optional(name, stored_t) \
	opt<stored_type_ ## stored_t> get_ ## name() const \
	{ if (has_attr(DW_AT_ ## name)) \
	  {  /* we have to check the form matches our expectations */ \
		 encap::attribute_value a = attr(DW_AT_ ## name); \
		 if (!a.is_ ## stored_t ()) { \
			debug() << "Warning: attribute " #name " of DIE at 0x" << std::hex << get_offset() << std::dec << " not a " #stored_t << endl; \
			return opt<stored_type_ ## stored_t>(); \
		 } else return a.get_ ## stored_t (); \
	  } \
	  else return opt<stored_type_ ## stored_t>(); } \
	opt<stored_type_ ## stored_t> find_ ## name() const \
	{ encap::attribute_value found = find_attr(DW_AT_ ## name); \
	  if (found.get_form() != encap::attribute_value::NO_ATTR) { \
		 if (!found.is_ ## stored_t ()) { \
			debug() << "Warning: attribute " #name " of DIE at 0x" << std::hex << get_offset() << std::dec << " not a " #stored_t << endl; \
			return opt<stored_type_ ## stored_t>(); \
		 } else return found.get_ ## stored_t (); \
	  } else return opt<stored_type_ ## stored_t>(); }

#define super_attr_optional(name, stored_t) attr_optional(name, stored_t)

#define attr_mandatory(name, stored_t) \
	stored_type_ ## stored_t get_ ## name() const \
	{ assert(has_attr(DW_AT_ ## name)); \
	  return attr(DW_AT_ ## name).get_ ## stored_t (); } \
	stored_type_ ## stored_t find_ ## name() const \
	{ encap::attribute_value found = find_attr(DW_AT_ ## name); \
	  assert(found.get_form() != encap::attribute_value::NO_ATTR); \
	  return found.get_ ## stored_t (); }


#define super_attr_mandatory(name, stored_t) attr_mandatory(name, stored_t)
#define child_tag(arg)

	struct type_die; 
	struct with_static_location_die : public virtual basic_die
	{
		struct sym_binding_t 
		{ 
			Dwarf_Off file_relative_start_addr; 
			Dwarf_Unsigned size;
		};
		typedef std::function<sym_binding_t(const std::string&, void *)> sym_resolver_t;
		virtual encap::loclist get_static_location() const;
		opt<Dwarf_Off> spans_addr(
			Dwarf_Addr file_relative_addr,
			root_die& r,
			sym_resolver_t sym_resolve = sym_resolver_t(),
			void *arg = 0) const;
		virtual boost::icl::interval_map<Dwarf_Addr, Dwarf_Unsigned> 
		file_relative_intervals(
			root_die& r, 
			sym_resolver_t sym_resolve /* = sym_resolver_t() */,
			void *arg /* = 0 */) const;
	};

	struct with_named_children_die : public virtual basic_die
	{
		/* We most most of the resolution stuff into iterator_base, 
		 * but leave this here so that payload implementations can
		 * provide a faster-than-default (i.e. faster than linear search)
		 * way to look up named children (e.g. hash table). */
		virtual 
		iterator_base
		named_child(const std::string& name) const;
	};

/* program_element_die */
begin_class(program_element, base_initializations(basic), declare_base(basic))
		attr_optional(decl_file, unsigned)
		attr_optional(decl_line, unsigned)
		attr_optional(decl_column, unsigned)
		attr_optional(prototyped, flag)
		attr_optional(declaration, flag)
		attr_optional(external, flag)
		attr_optional(visibility, unsigned)
		attr_optional(artificial, flag)
end_class(program_element)
/* type_die */
template <typename BaseType>
struct summary_code_word
{
	opt<BaseType> val;
	typedef summary_code_word<BaseType> self;

	void invalidate() { val = opt<BaseType>(); }
	void zero_check()
	{
		if (val && *val == 0)
		{
			debug() << "Warning: output_word value hit zero again." << endl;
			*val = (BaseType) -1;
		}
	}
	self& operator<<(BaseType arg) 
	{
		if (val)
		{
			*val = rotate_left(*val, 8) ^ arg;
			zero_check();
		}
		return *this;
	}
	self& operator<<(const string& s) 
	{
		if (val)
		{
			for (auto i = s.begin(); i != s.end(); ++i)
			{
				*this << static_cast<BaseType>(*i);
			}
		}
		zero_check();
		return *this;
	}
	self& operator<<(opt<BaseType> o)
	{
		if (!o) 
		{
			val = opt<BaseType>(); // FIXME: do I want this to "lock" at the invalid state? for incomplete types etc.
			return *this;
		}
		else return this->operator<<(*o);
	}
	summary_code_word() : val(0) {}
};
/* To do summary codes efficiently, we need to make them compositional. 
 * This means that given the summary codes for the types we depend on,
 * we should be able to re-use those to compute the code our own type.
 * Unfortunately, type graphs are cyclic, and so the naive approach
 * (which we used to use) of doing a depth-first search and skipping
 * back-edges, is non-compositional: depending on where you start the
 * exploration, different edges are back-edges, and so a depended-on type's
 * code can include/exclude different edges than the depending type does.
 * To avoid this problem, we need to identify the strongly-connected
 * components in the type graph. Then, any type that participates in a
 * cycle can summarised by a pair: its strongly-connected component, and
 * its own identity.
 *
 * Two problems: how do we represent strongly-connected components (SCCs)
 * and how do we identify nodes? To answer the first: an SCC is just a set
 * of edges -- including the back-edges.
 
 * Nodes themselves are tricky. There is no obvious good way to identify
 * nodes in the graph, since we don't want to be sensitive to their DWARF
 * offsets, or even their relative ordering in the DWARF info section.
 * We define an "abstract name". It is definitely not unique -- we are
 * still building up to that, by defining summary codes -- but it should
 * satisfy the property that
 * 
 * - no DIE cycle includes two distinct types with the same abstract name;
 * 
 * - distinct types with the same abstract name will summarise differently,
 *   either because they are not cyclic (and their tree-structures are 
 *   different in the obvious way) or because they participate in cycles
 *   that are distinct *even* after names are abstracted.
 *
 * Whether the last one is true depends a lot on how an SCC, as a set of 
 * edges, gets crumpled down into a summary code.
 */
struct type_edge;
struct type_edge_compare : std::function<bool(const type_edge&, const type_edge&)>
{
	type_edge_compare(); // initializes the std::function with a lambda
};
struct type_scc_t : public set<type_edge, type_edge_compare>
{
	using set::set;
	// FIXME: we could try to maintain this on inserts.
	// if we remove an edge, we have to invalidate it and recompute.
	summary_code_word<uint32_t> edges_summary;
};
bool types_abstractly_equal(iterator_df<type_die> t1, iterator_df<type_die> t2);
std::ostream& print_type_abstract_name(std::ostream& s, iterator_df<type_die> t);
string abstract_name_for_type(iterator_df<type_die> t);
opt<uint32_t> summary_code_for_type(iterator_df<type_die> t);
opt<uint16_t> containment_summary_code_for_type(iterator_df<type_die> t);
opt<uint16_t> traversal_summary_code_for_type(iterator_df<type_die> t);
begin_class(type, base_initializations(initialize_base(program_element)), declare_base(program_element))
		attr_optional(byte_size, unsigned)
		mutable opt<uint32_t> cached_summary_code;
	protected:
		string arbitrary_name() const;
		template <typename BaseType> opt<BaseType> containment_summary_code(
			/* Take an argument for the recursive call. By default it's unset. */
			std::function<opt<BaseType>(iterator_df<type_die>)> recursive_call
			= std::function<opt<BaseType>(iterator_df<type_die>)>()
		) const;
		opt<uint16_t> traversal_summary_code() const;
		friend opt<uint16_t> containment_summary_code_for_type(iterator_df<type_die> t);
		friend opt<uint16_t> traversal_summary_code_for_type(iterator_df<type_die> t);
	public:
		mutable optional<shared_ptr<type_scc_t> > opt_cached_scc; // HACK: should be private, but test-scc needs it
		virtual opt<Dwarf_Unsigned> calculate_byte_size() const;
		// virtual bool is_rep_compatible(iterator_df<type_die> arg) const;
		virtual iterator_df<type_die> get_concrete_type() const;
		virtual iterator_df<type_die> get_unqualified_type() const;
		virtual bool abstractly_equals(core::iterator_df<core::type_die> t) const;
		virtual std::ostream& print_abstract_name(std::ostream& s) const ;
		virtual opt<type_scc_t> get_scc() const;
		virtual opt<uint32_t>		 summary_code() const;
		/* FIXME: temporary side-by-side impls while we compare / bug-fix. */
		template <typename BaseType> 
		opt<BaseType>		 combined_summary_code_using_iterators() const;
		virtual opt<uint32_t>		 summary_code_using_old_method() const;
		virtual opt<uint32_t>		 summary_code_using_walk_type() const;
		virtual bool may_equal(core::iterator_df<core::type_die> t, 
			const std::set< std::pair<core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const;
		bool equal(core::iterator_df<core::type_die> t, 
			const std::set< std::pair<core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const;
		bool operator==(const dwarf::core::type_die& t) const;
end_class(type)
struct type_edge : public pair< pair<iterator_df<type_die>, iterator_df<program_element_die> >, iterator_df<type_die> >
{
	using pair::pair;
	using pair::operator=;
	const iterator_df<type_die>& source() const { return first.first; }
	iterator_df<type_die>& source() { return first.first; }
	const iterator_df<program_element_die>& label() const { return first.second; }
	iterator_df<program_element_die>& label() { return first.second; }
	const iterator_df<program_element_die>& reason() const { return first.second; }
	iterator_df<program_element_die>& reason() { return first.second; }
	bool reason_is_traversal() const;
	const iterator_df<type_die>& target() const { return second; }
	iterator_df<type_die>& target() { return second; }
};
/* "Type iterators" actually walk *edges* in the type DIE graph, 
 * not types per se: the target DIE of an edge is the iterator's
 * "position" if you dereference it, and the DIE which models the
 * edge itself is the "reason". The source edge is implied by the
 * reason, e.g. for a member_die it's the containing structure;
 * for a formal_parameter_die it's the containing subprogram type,
 * etc.. 
 * If you walk a type, you get the edges in the depth-first traversal,
 * without the back-edges. What if you want to know about the back-edges?
 * Bit of a HACK: I have added the "back_edges_here()" method which
 * gives you a vector of (reason, target) pairs. We use this when
 * computing the SCC of a type DIE.
 *
 * Another wart: we have type_iterator_df which avoids repeat visits
 * to any node; and we have type_iterator_edge_df which will visit
 * the same node as many times as there are distinct-labelled incoming
 * edges to it. */
struct type_iterator_df_base : public iterator_base
{
	typedef type_iterator_df_base self;
	friend class boost::iterator_core_access;

	/* The stack records the grey nodes. The back of the stack
	 * may or may not be our current position.  */
	deque< pair<iterator_df<type_die>, iterator_df<program_element_die> > > m_stack;
	struct black_offsets_set_t : std::unordered_set<Dwarf_Off>
	{
		bool contains(const iterator_base& i) const
		{
			return this->find(i.is_end_position() ? (Dwarf_Off)-1 : i.offset_here())
				!= this->end();
		}
		void insert(const iterator_base& i)
		{
			this->unordered_set::insert(i.is_end_position() ? (Dwarf_Off)-1 : i.offset_here());
		}
		using unordered_set::unordered_set;
	} black_offsets;

	iterator_df<program_element_die> m_reason;
	iterator_base& base_reference()
	{ return static_cast<iterator_base&>(*this); }
	const iterator_base& base() const
	{ return static_cast<const iterator_base&>(*this); }

	type_iterator_df_base() : iterator_base() {}
	type_iterator_df_base(const iterator_base& arg)
	 : iterator_base(arg)   {}// this COPIES so avoid
	type_iterator_df_base(iterator_base&& arg)
	 : iterator_base(std::move(arg)) {} // FIXME: use std::move here, and test
	type_iterator_df_base(const self& arg)
	 : iterator_base(arg), m_stack(arg.m_stack),
	   black_offsets(arg.black_offsets), m_reason(arg.m_reason) {}
	type_iterator_df_base(self&& arg)
	 : iterator_base(arg), m_stack(std::move(arg.m_stack)),
	   black_offsets(std::move(arg.black_offsets)), m_reason(std::move(arg.m_reason)) {}

	bool is_grey(const iterator_base& i) const 
	{
		return !black_offsets.contains(i)
		&& std::find_if(m_stack.begin(), m_stack.end(),
			[i](const pair<iterator_df<type_die>, iterator_df<program_element_die> >& pair)
			{ return pair.first == i; }
		) != m_stack.end();
	}
	bool pos_is_grey() const { return is_grey(base()); }
	
	//bool is_latest_grey(const iterator_base& i) const
	//{ return i == m_stack.back().first; }
	
	//bool pos_is_latest_grey() const { return is_latest_grey(base()); }
	
	// for if we moved (for edge-reflecting purposes) to a non-latest-grey position...
	bool is_black(const iterator_base& i) const
	{ return black_offsets.contains(i); }
	bool pos_is_black() const
	{ return is_black(base()); }
	
	bool pos_is_white() const { return !pos_is_black() && !pos_is_grey(); }
	
	enum colour { WHITE, GREY, BLACK };
	colour colour_of(const iterator_base& i) const
	{ return is_black(i) ? BLACK : is_grey(i) ? GREY : WHITE; }
	colour pos_colour() const { return colour_of(base()); }

	self& operator=(const iterator_base& arg) // assign fresh from an iterator; starts white
	{ this->base_reference() = arg; 
	  m_stack.clear();
	  this->black_offsets.clear(); // = std::move(std::set<Dwarf_Off>());
	  this->m_reason = END;
	  return *this; }
	self& operator=(iterator_base&& arg)
	{ this->base_reference() = std::move(arg); 
	  m_stack.clear();
	  black_offsets.clear(); // = std::move(std::set<Dwarf_Off>());
	  this->m_reason = END;
	  return *this; }
	self& operator=(const self& arg)
	{ self tmp(arg); 
	  std::swap(this->base_reference(), tmp.base_reference());
	  std::swap(this->m_stack, tmp.m_stack);
	  std::swap(this->black_offsets, tmp.black_offsets);
	  std::swap(this->m_reason, tmp.m_reason);
	  return *this; }
	self& operator=(self&& arg)
	{ this->base_reference() = std::move(arg);
	  this->m_stack = std::move(arg.m_stack);
	  this->black_offsets = std::move(arg.black_offsets);
	  this->m_reason = std::move(arg.m_reason);
	  return *this; }

	iterator_df<program_element_die> reason() const
	{ return m_reason; }
protected: /* helpers */
	iterator_df<type_die> source_vertex_for(const iterator_df<type_die>&, const iterator_df<program_element_die>& p) const;
	friend class type_die;
public:
	iterator_df<type_die> source_vertex() const
	{ return source_vertex_for(base(), reason()); }
	iterator_df<program_element_die> edge_label() const { return reason(); }
	type_edge as_incoming_edge() const
	{
		return make_pair(
			make_pair(
				source_vertex(),
				edge_label()
			),
			*this
		);
	}
	enum edge_kind { TREE, BACK, CROSS };
	edge_kind incoming_edge_kind() const
	{
		if (pos_is_white()) return TREE;
		if (pos_is_grey()) return BACK;
		assert(pos_is_black()); return CROSS;
		// if we're walking an incoming edge to any node, that node is not white
	}

	/* Since we want to walk "void", which has no representation,
	 * we're only at the end if we're both "no DIE" and "no reason". */
	operator bool() const { return !(!this->base() && !this->reason()); }
	
	/* Primitive operations that our subclasses will use. */
	type_die& dereference() const
	{ return dynamic_cast<type_die&>(this->iterator_base::dereference()); }
	
	/* Nobody implements this. */
	void decrement();

	/* "go deeper" means find a white or black successor of the current node.
	 -- if it's black, we may or may not want to reexplore */
	pair<iterator_df<type_die>, iterator_df<program_element_die> >
	first_outgoing_edge_target() const;
	
	/* "go sideways" means find a white or black successor of the grey 
	 * predecessor of the current node.
	 * -- there is only one grey predecessor, by construction (depth-first)
	 * -- the current node become black when we do this.
	 */
	pair<iterator_df<type_die>, iterator_df<program_element_die> >
	predecessor_node_next_outgoing_edge_target() const;
		/* "backtrack" means find a white or black successor of the next grey predecessor,
	 *  i.e. iterate the go-sideways thing one grey node up the stack. 
	 * The client can open-code this. */
	
	 /* What about a nice interface to back edges and cross edges?
	  * Since the edge iterator will follow these -- it just won't
	  * push them on the stack -- we don't need a separate interface. */
	
	void increment_not_back(bool skip_dependencies = false);
	
	void increment_to_unseen_edge();
	void increment_to_unseen_node();
};

/* The idea of this one is simply to do the same thing as walk_type,
 * for better or worse. */
struct type_iterator_df_walk :  public type_iterator_df_base,
								public boost::iterator_facade<
								   type_iterator_df_walk
								 , type_die
								 , boost::forward_traversal_tag
								 , type_die& /* Reference */
								 , Dwarf_Signed /* difference */
								>
{
	typedef type_iterator_df_walk self;
	friend class boost::iterator_core_access;

	using type_iterator_df_base::type_iterator_df_base;
	
	void increment(bool skip_dependencies = false) { increment_not_back(skip_dependencies); }
	void increment_skipping_dependencies() { increment(true); }
};

struct type_iterator_df_edges : public type_iterator_df_base,
						  public boost::iterator_facade<
							type_iterator_df_edges
						  , type_die
						  , boost::forward_traversal_tag
						  , type_die& /* Reference */
						  , Dwarf_Signed /* difference */
						  >
{
	typedef type_iterator_df_edges self;
	friend class boost::iterator_core_access;
	
	type_iterator_df_edges(const iterator_base& arg)
	{ base_reference() = arg; m_reason = END; }
	type_iterator_df_edges(iterator_base&& arg)
	{ base_reference() = std::move(arg); m_reason = END; }
	type_iterator_df_edges(const self& arg)
	 : type_iterator_df_base(arg) {}
	type_iterator_df_edges(self&& arg) : type_iterator_df_base(std::move(arg)) {}

	void increment();
};

struct type_iterator_outgoing_edges : public type_iterator_df_base,
				  public boost::iterator_facade<
					type_iterator_outgoing_edges
				  , type_die
				  , boost::forward_traversal_tag
				  , type_die& /* Reference */
				  , Dwarf_Signed /* difference */
				  >
{
	typedef type_iterator_outgoing_edges self;
	typedef type_iterator_df_edges super;
private:
	void init()
	{
		m_stack.clear();
		black_offsets.clear();
		// we have nowhere to pop to, so if no outgoing edges, will hit END
		if (this->operator bool()) this->increment_to_unseen_edge();
	}
public:
	type_iterator_outgoing_edges()
	: type_iterator_df_base() {}
	explicit type_iterator_outgoing_edges(const type_iterator_df_base& arg)
	: type_iterator_df_base(arg) { init(); }
	explicit type_iterator_outgoing_edges(type_iterator_df_base&& arg)
	: type_iterator_df_base(std::move(arg)) { init(); }
	type_iterator_outgoing_edges(const self& arg)
	 : type_iterator_df_base(arg) { /* don't init */ }
	type_iterator_outgoing_edges(self&& arg) : type_iterator_df_base(std::move(arg))
	{ /* don't init */ }
	explicit type_iterator_outgoing_edges(
		const deque< pair<iterator_df<type_die>, iterator_df<program_element_die> > >& stack)
	{ /* This constructor means 
	   * "the stack position is black; start from the predecessor's next outgoing edge". */
		this->base_reference() = stack.back().first;
		this->m_reason = stack.back().second;
		this->m_stack.clear();
		// don't need stack for next outgoing edge
		auto found_next = predecessor_node_next_outgoing_edge_target();
		if (found_next.first || found_next.second)
		{
			this->base_reference() = std::move(found_next.first);
			this->m_reason = std::move(found_next.second);
		}
		else
		{
			this->base_reference() = END;
			this->m_reason = END;
		}
	}
	
	// assigning from any DF iterator will walk its children?
	// NO; this would be confusing (changes position)
	// we made the constructors above "explicit" for the same reason
// 	self& operator=(const type_iterator_df_base& arg)
// 	{ static_cast<type_iterator_df_base&>(*this)
// 	  = static_cast<const type_iterator_df_base&>(arg);
// 	  init();
// 	  return *this; }
// 	self& operator=(type_iterator_df_base&& arg)
// 	{ static_cast<type_iterator_df_base&&>(*this)
// 	  = static_cast<type_iterator_df_base&&>(arg);
// 	  init();
// 	  return *this; }
	// assigning from ourselves will just copy-assign -- OK
	self& operator=(const self& arg)
	{ static_cast<type_iterator_df_base&>(*this)
	  = static_cast<const type_iterator_df_base&>(arg);
	  return *this; }
	self& operator=(self&& arg)
	{ static_cast<type_iterator_df_base&&>(*this)
	  = static_cast<type_iterator_df_base&&>(arg);
	  return *this; }


	//type_iterator_outgoing_edges() : type_iterator_df_edges() { init(); }
	//type_iterator_outgoing_edges(const type_iterator_df_base& arg)
	// : type_iterator_df_edges(arg)  { init(); }// this COPIES so avoid
	//type_iterator_outgoing_edges(type_iterator_df_edges&& arg)
	// : type_iterator_df_edges(std::move(arg)) { init(); }
	//type_iterator_outgoing_edges(const self& arg)
	// : type_iterator_df_edges(arg) { /* don't init */ }
	//type_iterator_outgoing_edges(self&& arg)
	// : type_iterator_df_edges(std::move(arg)) { /* don't init */ }

	/* Basic idea: we're an iterator that yields each immediate successor
	 * of the start type_die. Our initial stack consists of that DIE
	 * and the null reason. The first thing we do is increment our position,
	 * moving us to the first outgoing edge's target, if any. */

	void increment()
	{
		auto found_next = predecessor_node_next_outgoing_edge_target();
		this->base_reference() = std::move(found_next.first);
		this->m_reason = std::move(found_next.second);
	}
};

/* type_set and related utilities. */
size_t type_hash_fn(iterator_df<type_die> t);
bool type_eq_fn(iterator_df<type_die> t1, iterator_df<type_die> t2);
struct type_set : public unordered_set< 
	/* Key */   iterator_df<type_die>,
	/* Hash */  std::function<size_t(iterator_df<type_die>)>,
	/* Equal */ std::function<bool(iterator_df<type_die> t1, iterator_df<type_die> t2)>
>
{
	type_set() : unordered_set({}, 0, type_hash_fn, type_eq_fn) {}
};
typedef set< opt<Dwarf_Off> > dieloc_set;
template <typename Value>
struct type_map : public unordered_map< 
	/* Key */   iterator_df<type_die>,
	Value,
	/* Hash */  std::function<size_t(iterator_df<type_die>)>,
	/* Equal */ std::function<bool(iterator_df<type_die> t1, iterator_df<type_die> t2)>
>
{
	typedef unordered_map< 
		/* Key */   iterator_df<type_die>,
		Value,
		/* Hash */  std::function<size_t(iterator_df<type_die>)>,
		/* Equal */ std::function<bool(iterator_df<type_die> t1, iterator_df<type_die> t2)>
	> super;
	type_map() : super({}, 0, type_hash_fn, type_eq_fn) {}
};
void walk_type(core::iterator_df<core::type_die> t, 
	core::iterator_df<core::program_element_die> origin, 
	const std::function<bool(core::iterator_df<core::type_die>, core::iterator_df<core::program_element_die>)>& pre_f,
	const std::function<void(core::iterator_df<core::type_die>, core::iterator_df<core::program_element_die>)>& post_f
	 = std::function<void(core::iterator_df<core::type_die>, core::iterator_df<core::program_element_die>)>(),
	const dieloc_set& currently_walking = dieloc_set());
/* with_type_describing_layout_die */
	struct with_type_describing_layout_die : public virtual program_element_die
	{
		virtual opt<iterator_df<type_die> > get_type() const = 0;
		virtual opt<iterator_df<type_die> > find_type() const = 0;
	};
/* with_dynamic_location_die */
	struct with_dynamic_location_die : public virtual with_type_describing_layout_die
	{
		virtual opt<Dwarf_Off> spans_addr(
				Dwarf_Addr absolute_addr,
				Dwarf_Signed instantiating_instance_addr, 
				root_die& r, 
				Dwarf_Off dieset_relative_ip,
				dwarf::expr::regs *p_regs = 0) const = 0;
		/* We define two variants of the spans_addr logic: 
		 * one suitable for stack-based locations (fp/variable)
		 * and another for object-based locations (member/inheritance)
		 * and each derived class should pick one! */
	protected:
		opt<Dwarf_Off> spans_stack_addr(
				Dwarf_Addr absolute_addr,
				Dwarf_Signed instantiating_instance_addr,
				root_die& r,
				Dwarf_Off dieset_relative_ip,
				dwarf::expr::regs *p_regs = 0) const;
		opt<Dwarf_Off> spans_addr_in_object(
				Dwarf_Addr absolute_addr,
				Dwarf_Signed instantiating_instance_addr,
				root_die& r, 
				Dwarf_Off dieset_relative_ip,
				dwarf::expr::regs *p_regs = 0) const;
	public:
		virtual iterator_df<program_element_die> 
		get_instantiating_definition() const;

		virtual Dwarf_Addr calculate_addr(
			Dwarf_Addr instantiating_instance_location,
			root_die& r,
			Dwarf_Off dieset_relative_ip,
			dwarf::expr::regs *p_regs = 0) const = 0;

		/** This gets a location list describing the location of the thing, 
			assuming that the instantiating_instance_location has been pushed
			onto the operand stack. */
		virtual encap::loclist get_dynamic_location() const = 0;
	protected:
		/* ditto */
		virtual Dwarf_Addr calculate_addr_on_stack(
			Dwarf_Addr instantiating_instance_location,
			root_die& r,
			Dwarf_Off dieset_relative_ip,
			dwarf::expr::regs *p_regs = 0) const;
		virtual Dwarf_Addr calculate_addr_in_object(
			Dwarf_Addr instantiating_instance_location,
			root_die& r, 
			Dwarf_Off dieset_relative_ip,
			dwarf::expr::regs *p_regs = 0) const;
	public:
		virtual bool location_requires_object_base() const = 0; 

		/* virtual Dwarf_Addr calculate_addr(
			Dwarf_Signed frame_base_addr,
			Dwarf_Off dieset_relative_ip,
			dwarf::expr::regs *p_regs = 0) const;*/
	};
#define has_stack_based_location \
	bool location_requires_object_base() const { return false; } \
	opt<Dwarf_Off> spans_addr( \
					Dwarf_Addr aa, \
					Dwarf_Signed fb, \
					root_die& r, \
					Dwarf_Off dr_ip, \
					dwarf::expr::regs *p_regs) const \
					{ return spans_stack_addr(aa, fb, r, dr_ip, p_regs); } \
	Dwarf_Addr calculate_addr( \
				Dwarf_Addr fb, \
				root_die& r, \
				Dwarf_Off dr_ip, \
				dwarf::expr::regs *p_regs = 0) const \
				{ return calculate_addr_on_stack(fb, r, dr_ip, p_regs); } \
	encap::loclist get_dynamic_location() const;
#define has_object_based_location \
	bool location_requires_object_base() const { return true; } \
	opt<Dwarf_Off> spans_addr( \
					Dwarf_Addr aa, \
					Dwarf_Signed io, \
					root_die& r, \
					Dwarf_Off dr_ip, \
					dwarf::expr::regs *p_regs) const \
					{ return spans_addr_in_object(aa, io, r, dr_ip, p_regs); } \
	Dwarf_Addr calculate_addr( \
				Dwarf_Addr io, \
				root_die& r, \
				Dwarf_Off dr_ip, \
				dwarf::expr::regs *p_regs = 0) const \
				{ return calculate_addr_in_object(io, r, dr_ip, p_regs); } \
	encap::loclist get_dynamic_location() const;
/* data_member_die */
begin_class(data_member, base_initializations(initialize_base(with_dynamic_location)), declare_base(with_dynamic_location))
	has_object_based_location
	attr_optional(data_member_location, loclist)
	virtual iterator_df<type_die> find_or_create_type_handling_bitfields() const;
	/** This gets an offset in an enclosing object. */
	virtual opt<Dwarf_Unsigned> byte_offset_in_enclosing_type(bool assume_packed_if_no_location = false) const;
end_class(data_member)
/* type_chain_die */
begin_class(type_chain, base_initializations(initialize_base(type)), declare_base(type))
		attr_optional(type, refiter_is_type)
		opt<Dwarf_Unsigned> calculate_byte_size() const;
		iterator_df<type_die> get_concrete_type() const;
		bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const;
end_class(type_chain)
/* type_describing_subprogram_die */
begin_class(type_describing_subprogram, base_initializations(initialize_base(type)), declare_base(type))
		attr_optional(type, refiter_is_type)
		virtual iterator_df<type_die> get_return_type() const = 0;
		virtual bool is_variadic() const;
		bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const;
		bool abstractly_equals(iterator_df<type_die> t) const;
		std::ostream& print_abstract_name(std::ostream& s) const;
end_class(type_describing_subprogram)
/* address_holding_type_die */
begin_class(address_holding_type, base_initializations(initialize_base(type_chain)), declare_base(type_chain))
		attr_optional(address_class, unsigned)
		iterator_df<type_die> get_concrete_type() const;
		opt<Dwarf_Unsigned> calculate_byte_size() const;
		bool abstractly_equals(iterator_df<type_die> t) const;
		pair<unsigned, iterator_df<type_die> > get_ultimate_reached_type() const;
		pair<unsigned, iterator_df<type_die> > find_ultimate_reached_type() const;
		std::ostream& print_abstract_name(std::ostream& s) const;
end_class(type_chain)
/* qualified_type_die */
begin_class(qualified_type, base_initializations(initialize_base(type_chain)), declare_base(type_chain))
		iterator_df<type_die> get_unqualified_type() const;
end_class(qualified_type)
/* with_data_members_die */
begin_class(with_data_members, base_initializations(initialize_base(type)), declare_base(type))
		child_tag(member)
protected:
		mutable optional< iterator_df<> > maybe_cached_definition; 
		// really use boost::optional, to distinguish "cached END" from "no cache"
public:
		iterator_base find_definition() const; // for turning declarations into defns
		bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const; 
		bool abstractly_equals(iterator_df<type_die> t) const;
		std::ostream& print_abstract_name(std::ostream& s) const;
end_class(with_data_members)

#define extra_decls_subprogram \
		opt< std::pair<Dwarf_Off, iterator_df<with_dynamic_location_die> > > \
		spans_addr_in_frame_locals_or_args( \
					Dwarf_Addr absolute_addr, \
					root_die& r, \
					Dwarf_Off dieset_relative_ip, \
					Dwarf_Signed *out_frame_base, \
					dwarf::expr::regs *p_regs = 0) const; \
		iterator_df<type_die> get_return_type() const;
#define extra_decls_variable \
		bool has_static_storage() const; \
		has_stack_based_location
#define extra_decls_formal_parameter \
		has_stack_based_location
#define extra_decls_array_type \
		opt<Dwarf_Unsigned> element_count() const; \
		vector<opt<Dwarf_Unsigned> > dimension_element_counts() const; \
		opt<Dwarf_Unsigned> calculate_byte_size() const; \
		/* bool is_rep_compatible(iterator_df<type_die> arg) const; */ \
		iterator_df<type_die> ultimate_element_type() const; \
		opt<Dwarf_Unsigned> ultimate_element_count() const; \
		bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const; \
		iterator_df<type_die> get_concrete_type() const; \
		bool abstractly_equals(iterator_df<type_die> t) const; \
		std::ostream& print_abstract_name(std::ostream& s) const;
#define extra_decls_string_type \
		bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const; \
		opt<Dwarf_Unsigned> fixed_length_in_bytes() const; \
		opt<encap::loclist> dynamic_length_in_bytes() const; \
		bool abstractly_equals(iterator_df<type_die> t) const; \
		std::ostream& print_abstract_name(std::ostream& s) const; \
		opt<Dwarf_Unsigned> calculate_byte_size() const;
#define extra_decls_pointer_type \
		/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_reference_type \
		/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_base_type \
		bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const; \
		opt<Dwarf_Unsigned> calculate_byte_size() const; \
		pair<Dwarf_Unsigned, Dwarf_Unsigned> bit_size_and_offset() const; \
		bool is_bitfield_type() const; \
		bool abstractly_equals(iterator_df<type_die> t) const; \
		std::ostream& print_abstract_name(std::ostream& s) const; \
		string get_canonical_name() const; \
		static string canonical_name_for(spec& spec, unsigned encoding, \
			unsigned byte_size, unsigned bit_size, unsigned bit_offset);
		/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_structure_type \
		opt<Dwarf_Unsigned> calculate_byte_size() const; \
		/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_union_type \
		/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_class_type \
		/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_enumeration_type \
		bool abstractly_equals(iterator_df<type_die> t) const; \
		std::ostream& print_abstract_name(std::ostream& s) const; \
		bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const; \
		/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_subrange_type \
		bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const; \
		bool abstractly_equals(iterator_df<type_die> t) const; \
		std::ostream& print_abstract_name(std::ostream& s) const;
#define extra_decls_set_type \
		bool abstractly_equals(iterator_df<type_die> t) const; \
		std::ostream& print_abstract_name(std::ostream& s) const;
#define extra_decls_file_type \
		bool abstractly_equals(iterator_df<type_die> t) const; \
		std::ostream& print_abstract_name(std::ostream& s) const;
#define extra_decls_ptr_to_member_type \
		bool abstractly_equals(iterator_df<type_die> t) const; \
		std::ostream& print_abstract_name(std::ostream& s) const; \
		bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const;
#define extra_decls_member \
		iterator_df<type_die> find_or_create_type_handling_bitfields() const;
#define extra_decls_subroutine_type \
		/* bool is_rep_compatible(iterator_df<type_die> arg) const; */ \
		core::iterator_df<core::type_die> get_return_type() const;
#define extra_decls_unspecified_type \
		std::ostream& print_abstract_name(std::ostream& s) const;

#define extra_decls_compile_unit \
/* We define fields and getters for the per-CU source file info */ \
/* (which is *separate* from decl_file and decl_line attributes). */ \
public: \
inline std::string source_file_name(unsigned o) const; \
opt<std::string> source_file_fq_pathname(unsigned o) const; \
inline unsigned source_file_count() const; \
/* We define fields and getters for the per-CU info (NOT attributes) */ \
/* available from libdwarf. These will be filled in by root_die::make_payload(). */ \
protected: \
Dwarf_Unsigned cu_header_length; \
Dwarf_Half version_stamp; \
Dwarf_Unsigned abbrev_offset; \
Dwarf_Half address_size; \
Dwarf_Half offset_size; \
Dwarf_Half extension_size; \
Dwarf_Unsigned next_cu_header; \
public: \
Dwarf_Unsigned get_cu_header_length() const { return cu_header_length; } \
Dwarf_Half get_version_stamp() const { return version_stamp; } \
Dwarf_Unsigned get_abbrev_offset() const { return abbrev_offset; } \
Dwarf_Half get_address_size() const { return address_size; } \
Dwarf_Half get_offset_size() const { return offset_size; } \
Dwarf_Half get_extension_size() const { return extension_size; } \
Dwarf_Unsigned get_next_cu_header() const { return next_cu_header; } \
opt<Dwarf_Unsigned> implicit_array_base() const; \
mutable iterator_df<type_die> cached_implicit_enum_base_type; \
iterator_df<type_die> implicit_enum_base_type() const; \
mutable iterator_df<type_die> cached_implicit_subrange_base_type; \
iterator_df<type_die> implicit_subrange_base_type() const; \
encap::rangelist normalize_rangelist(const encap::rangelist& rangelist) const; \
bool is_generic_pointee_type(iterator_df<type_die> t) const; \
friend class iterator_base; \
friend class factory; 

#include "dwarf-current-adt.h"

#undef extra_decls_subprogram
#undef extra_decls_compile_unit
#undef extra_decls_array_type
#undef extra_decls_string_type
#undef extra_decls_variable
#undef extra_decls_structure_type
#undef extra_decls_union_type
#undef extra_decls_class_type
#undef extra_decls_enumeration_type
#undef extra_decls_subroutine_type
#undef extra_decls_member
#undef extra_decls_inheritance
#undef extra_decls_pointer_type
#undef extra_decls_reference_type
#undef extra_decls_base_type
#undef extra_decls_formal_parameter
#undef extra_decls_set_type
#undef extra_decls_file_type
#undef extra_decls_ptr_to_member_type
#undef extra_decls_unspecified_type
#undef extra_decls_subrange_type

#undef has_stack_based_location
#undef has_object_based_location

#undef forward_decl
#undef declare_base
#undef declare_bases
#undef base_fragment
#undef initialize_base
#undef constructor
#undef begin_class
#undef base_initializations
#undef declare_base
#undef end_class
#undef stored_type
#undef stored_type_string
#undef stored_type_flag
#undef stored_type_unsigned
#undef stored_type_signed
#undef stored_type_offset
#undef stored_type_half
#undef stored_type_ref
#undef stored_type_tag
#undef stored_type_loclist
#undef stored_type_address
#undef stored_type_refiter
#undef stored_type_refiter_is_type
#undef stored_type_rangelist
#undef attr_optional
#undef attr_mandatory
#undef super_attr_optional
#undef super_attr_mandatory
#undef child_tag

/****************************************************************/
/* end generated ADT includes								   */
/****************************************************************/
	}
}
#endif
