#pragma once
// IWYU pragma: private, include "PassVisitor.h"

#define VISIT_START( node )                     \
	__attribute__((unused))                   \
	guard_value_impl guard( at_cleanup_impl(pass, 0) );       \
	bool visit_children = true;               \
	set_visit_children( visit_children );	\
	call_previsit( node );                    \
	if( visit_children ) {                    \

#define VISIT_END( node )                       \
	}                                         \
	call_postvisit( node );                   \

#define MUTATE_START( node )                    \
	__attribute__((unused))                   \
	guard_value_impl guard( at_cleanup_impl(pass, 0) );       \
	bool visit_children = true;               \
	set_visit_children( visit_children );	\
	call_premutate( node );                   \
	if( visit_children ) {                    \

#define MUTATE_END( type, node )                \
	}                                         \
	return call_postmutate< type * >( node ); \


#define VISIT_BODY( node )        \
	VISIT_START( node );        \
	Visitor::visit( node );     \
	VISIT_END( node );          \


#define MUTATE_BODY( type, node ) \
	MUTATE_START( node );       \
	Mutator::mutate( node );    \
	MUTATE_END( type, node );   \



template<typename T>
static inline bool empty( T * ptr ) {
	return !ptr || ptr->empty();
}

typedef std::list< Statement   * > StmtList_t;
typedef std::list< Declaration * > DeclList_t;

template<typename iterator_t>
static inline void splice( iterator_t it, DeclList_t * decls ) {
	std::transform(
		decls->begin(),
		decls->end(),
		it,
		[](Declaration * decl) -> auto {
			return new DeclStmt( noLabels, decl );
		}
	);
	decls->clear();
}

template< typename pass_type >
static inline void acceptAll( std::list< Declaration* > &decls, PassVisitor< pass_type >& visitor ) {

	DeclList_t* beforeDecls = visitor.get_beforeDecls();
	DeclList_t* afterDecls  = visitor.get_afterDecls();

	for ( std::list< Declaration* >::iterator i = decls.begin(); ; ++i ) {
		// splice in new declarations after previous decl
		if ( !empty( afterDecls ) ) { decls.splice( i, *afterDecls ); }

		if ( i == decls.end() ) break;

		// run mutator on declaration
		maybeAccept( *i, visitor );

		// splice in new declarations before current decl
		if ( !empty( beforeDecls ) ) { decls.splice( i, *beforeDecls ); }
	}
}

template< typename pass_type >
static inline void mutateAll( std::list< Declaration* > &decls, PassVisitor< pass_type >& mutator ) {

	DeclList_t* beforeDecls = mutator.get_beforeDecls();
	DeclList_t* afterDecls  = mutator.get_afterDecls();

	for ( std::list< Declaration* >::iterator i = decls.begin(); ; ++i ) {
		// splice in new declarations after previous decl
		if ( !empty( afterDecls ) ) { decls.splice( i, *afterDecls ); }

		if ( i == decls.end() ) break;

		// run mutator on declaration
		*i = maybeMutate( *i, mutator );

		// splice in new declarations before current decl
		if ( !empty( beforeDecls ) ) { decls.splice( i, *beforeDecls ); }
	}
}

template< typename pass_type >
template< typename func_t >
void PassVisitor< pass_type >::handleStatementList( std::list< Statement * > & statements, func_t func ) {
	SemanticError errors;

	// don't want statements from outer CompoundStmts to be added to this CompoundStmt
	ValueGuardPtr< StmtList_t > oldBeforeStmts( get_beforeStmts() );
	ValueGuardPtr< StmtList_t > oldAfterStmts ( get_afterStmts () );
	ValueGuardPtr< DeclList_t > oldBeforeDecls( get_beforeDecls() );
	ValueGuardPtr< DeclList_t > oldAfterDecls ( get_afterDecls () );

	StmtList_t* beforeStmts = get_beforeStmts();
	StmtList_t* afterStmts  = get_afterStmts();
	DeclList_t* beforeDecls = get_beforeDecls();
	DeclList_t* afterDecls  = get_afterDecls();

	for ( std::list< Statement* >::iterator i = statements.begin(); i != statements.end(); ++i ) {

		if ( !empty( afterDecls ) ) { splice( std::inserter( statements, i ), afterDecls ); }
		if ( !empty( afterStmts ) ) { statements.splice( i, *afterStmts ); }

		try {
			func( *i );
			assert(( empty( beforeStmts ) && empty( afterStmts ))
			    || ( empty( beforeDecls ) && empty( afterDecls )) );

		} catch ( SemanticError &e ) {
			errors.append( e );
		}

		if ( !empty( beforeDecls ) ) { splice( std::inserter( statements, i ), beforeDecls ); }
		if ( !empty( beforeStmts ) ) { statements.splice( i, *beforeStmts ); }
	}

	if ( !empty( afterDecls ) ) { splice( std::back_inserter( statements ), afterDecls); }
	if ( !empty( afterStmts ) ) { statements.splice( statements.end(), *afterStmts ); }
	if ( !errors.isEmpty() ) { throw errors; }
}

template< typename pass_type >
void PassVisitor< pass_type >::visitStatementList( std::list< Statement * > & statements ) {
	handleStatementList( statements, [this]( Statement * stmt) {
		stmt->accept( *this );
	});
}

template< typename pass_type >
void PassVisitor< pass_type >::mutateStatementList( std::list< Statement * > & statements ) {
	handleStatementList( statements, [this]( Statement *& stmt) {
		stmt = stmt->acceptMutator( *this );
	});
}


template< typename pass_type >
template< typename func_t >
Statement * PassVisitor< pass_type >::handleStatement( Statement * stmt, func_t func ) {
	// don't want statements from outer CompoundStmts to be added to this CompoundStmt
	ValueGuardPtr< TypeSubstitution * >  oldEnv        ( get_env_ptr    () );
	ValueGuardPtr< DeclList_t >          oldBeforeDecls( get_beforeDecls() );
	ValueGuardPtr< DeclList_t >          oldAfterDecls ( get_afterDecls () );
	ValueGuardPtr< StmtList_t >          oldBeforeStmts( get_beforeStmts() );
	ValueGuardPtr< StmtList_t >          oldAfterStmts ( get_afterStmts () );

	Statement *newStmt = func( stmt );

	StmtList_t* beforeStmts = get_beforeStmts();
	StmtList_t* afterStmts  = get_afterStmts();
	DeclList_t* beforeDecls = get_beforeDecls();
	DeclList_t* afterDecls  = get_afterDecls();

	if( empty(beforeStmts) && empty(afterStmts) && empty(beforeDecls) && empty(afterDecls) ) { return newStmt; }
	assert(( empty( beforeStmts ) && empty( afterStmts ))
	    || ( empty( beforeDecls ) && empty( afterDecls )) );

	CompoundStmt *compound = new CompoundStmt( noLabels );
	if( !empty(beforeDecls) ) { splice( std::back_inserter( compound->get_kids() ), beforeDecls ); }
	if( !empty(beforeStmts) ) { compound->get_kids().splice( compound->get_kids().end(), *beforeStmts ); }
	compound->get_kids().push_back( newStmt );
	if( !empty(afterDecls) ) { splice( std::back_inserter( compound->get_kids() ), afterDecls ); }
	if( !empty(afterStmts) ) { compound->get_kids().splice( compound->get_kids().end(), *afterStmts ); }
	return compound;
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::visitStatement( Statement * stmt ) {
	return handleStatement( stmt, [this]( Statement * stmt ) {
		maybeAccept( stmt, *this );
		return stmt;
	});
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutateStatement( Statement * stmt ) {
	return handleStatement( stmt, [this]( Statement * stmt ) {
	 	return maybeMutate( stmt, *this );
	});
}

template< typename pass_type >
template< typename func_t >
Expression * PassVisitor< pass_type >::handleExpression( Expression * expr, func_t func ) {
	if( !expr ) return nullptr;

	auto env_ptr = get_env_ptr();
	if ( env_ptr && expr->get_env() ) {
		*env_ptr = expr->get_env();
	}

	// should env be cloned (or moved) onto the result of the mutate?
	return func( expr );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::visitExpression( Expression * expr ) {
	return handleExpression(expr, [this]( Expression * expr ) {
		expr->accept( *this );
		return expr;
	});
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutateExpression( Expression * expr ) {
	return handleExpression(expr, [this]( Expression * expr ) {
		return expr->acceptMutator( *this );
	});
}

//------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template< typename pass_type >
void PassVisitor< pass_type >::visit( ObjectDecl * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( FunctionDecl * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( StructDecl * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( UnionDecl * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( EnumDecl * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( TraitDecl * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( TypeDecl * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( TypedefDecl * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( AsmDecl * node ) {
	VISIT_BODY( node );
}

//--------------------------------------------------------------------------
// CompoundStmt
template< typename pass_type >
void PassVisitor< pass_type >::visit( CompoundStmt * node ) {
	VISIT_START( node );
	call_beginScope();

	visitStatementList( node->get_kids() );

	call_endScope();
	VISIT_END( node );
}

template< typename pass_type >
CompoundStmt * PassVisitor< pass_type >::mutate( CompoundStmt * node ) {
	MUTATE_START( node );
	call_beginScope();

	mutateStatementList( node->get_kids() );

	call_endScope();
	MUTATE_END( CompoundStmt, node );
}

//--------------------------------------------------------------------------
// ExprStmt
template< typename pass_type >
void PassVisitor< pass_type >::visit( ExprStmt * node ) {
	VISIT_START( node );

	visitExpression( node->get_expr() );

	VISIT_END( node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( ExprStmt * node ) {
	MUTATE_START( node );

	node->set_expr( mutateExpression( node->get_expr() ) );

	MUTATE_END( Statement, node );
}

//--------------------------------------------------------------------------
// AsmStmt
template< typename pass_type >
void PassVisitor< pass_type >::visit( AsmStmt * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( AsmStmt * node ) {
	MUTATE_BODY( Statement, node );
}

//--------------------------------------------------------------------------
// IfStmt
template< typename pass_type >
void PassVisitor< pass_type >::visit( IfStmt * node ) {
	VISIT_START( node );

	visitExpression( node->get_condition() );
	node->set_thenPart ( visitStatement( node->get_thenPart() ) );
	node->set_elsePart ( visitStatement( node->get_elsePart() ) );

	VISIT_END( node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( IfStmt * node ) {
	MUTATE_START( node );

	node->set_condition( mutateExpression( node->get_condition() ) );
	node->set_thenPart ( mutateStatement ( node->get_thenPart()  ) );
	node->set_elsePart ( mutateStatement ( node->get_elsePart()  ) );

	MUTATE_END( Statement, node );
}

//--------------------------------------------------------------------------
// WhileStmt
template< typename pass_type >
void PassVisitor< pass_type >::visit( WhileStmt * node ) {
	VISIT_START( node );

	visitExpression( node->get_condition() );
	node->set_body( visitStatement( node->get_body() ) );

	VISIT_END( node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( WhileStmt * node ) {
	MUTATE_START( node );

	node->set_condition( mutateExpression( node->get_condition() ) );
	node->set_body( mutateStatement( node->get_body() ) );

	MUTATE_END( Statement, node );
}

//--------------------------------------------------------------------------
// ForStmt
template< typename pass_type >
void PassVisitor< pass_type >::visit( ForStmt * node ) {
	VISIT_START( node );

	acceptAll( node->get_initialization(), *this );
	visitExpression( node->get_condition() );
	visitExpression( node->get_increment() );
	node->set_body( visitStatement( node->get_body() ) );

	VISIT_END( node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( ForStmt * node ) {
	MUTATE_START( node );

	mutateAll( node->get_initialization(), *this );
	node->set_condition( mutateExpression( node->get_condition() ) );
	node->set_increment( mutateExpression( node->get_increment() ) );
	node->set_body( mutateStatement( node->get_body() ) );

	MUTATE_END( Statement, node );
}

//--------------------------------------------------------------------------
// SwitchStmt
template< typename pass_type >
void PassVisitor< pass_type >::visit( SwitchStmt * node ) {
	VISIT_START( node );

	visitExpression( node->get_condition() );
	visitStatementList( node->get_statements() );

	VISIT_END( node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( SwitchStmt * node ) {
	MUTATE_START( node );

	node->set_condition( mutateExpression( node->get_condition() ) );
	mutateStatementList( node->get_statements() );

	MUTATE_END( Statement, node );
}

//--------------------------------------------------------------------------
// CaseStmt
template< typename pass_type >
void PassVisitor< pass_type >::visit( CaseStmt * node ) {
	VISIT_START( node );

	visitExpression( node->get_condition() );
	visitStatementList( node->get_statements() );

	VISIT_END( node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( CaseStmt * node ) {
	MUTATE_START( node );

	node->set_condition(  mutateExpression( node->get_condition() ) );
	mutateStatementList( node->get_statements() );

	MUTATE_END( Statement, node );
}

//--------------------------------------------------------------------------
// BranchStmt
template< typename pass_type >
void PassVisitor< pass_type >::visit( BranchStmt * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( BranchStmt * node ) {
	MUTATE_BODY( Statement, node );
}

//--------------------------------------------------------------------------
// ReturnStmt
template< typename pass_type >
void PassVisitor< pass_type >::visit( ReturnStmt * node ) {
	VISIT_START( node );

	visitExpression( node->get_expr() );

	VISIT_END( node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( ReturnStmt * node ) {
	MUTATE_START( node );

	node->set_expr( mutateExpression( node->get_expr() ) );

	MUTATE_END( Statement, node );
}

//--------------------------------------------------------------------------
// ThrowStmt

template< typename pass_type >
void PassVisitor< pass_type >::visit( ThrowStmt * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( ThrowStmt * node ) {
	MUTATE_BODY( Statement, node );
}

//--------------------------------------------------------------------------
// TryStmt
template< typename pass_type >
void PassVisitor< pass_type >::visit( TryStmt * node ) {
	VISIT_START( node );

	maybeAccept( node->get_block(), *this );
	acceptAll( node->get_catchers(), *this );
	maybeAccept( node->get_finally(), *this );

	VISIT_END( node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( TryStmt * node ) {
	MUTATE_START( node );

	node->set_block(  maybeMutate( node->get_block(), *this ) );
	mutateAll( node->get_catchers(), *this );
	node->set_finally( maybeMutate( node->get_finally(), *this ) );

	MUTATE_END( Statement, node );
}

//--------------------------------------------------------------------------
// CatchStmt
template< typename pass_type >
void PassVisitor< pass_type >::visit( CatchStmt * node ) {
	VISIT_START( node );

	maybeAccept( node->get_decl(), *this );
	node->set_cond( visitExpression( node->get_cond() ) );
	node->set_body( visitStatement( node->get_body() ) );

	VISIT_END( node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( CatchStmt * node ) {
	MUTATE_START( node );

	node->set_decl( maybeMutate( node->get_decl(), *this ) );
	node->set_cond( mutateExpression( node->get_cond() ) );
	node->set_body( mutateStatement( node->get_body() ) );

	MUTATE_END( Statement, node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( FinallyStmt * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( NullStmt * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( DeclStmt * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( ImplicitCtorDtorStmt * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( ApplicationExpr * node ) {
	VISIT_BODY( node );
}

//--------------------------------------------------------------------------
// UntypedExpr
template< typename pass_type >
void PassVisitor< pass_type >::visit( UntypedExpr * node ) {
	VISIT_START( node );

	// maybeAccept( node->get_env(), *this );
	maybeAccept( node->get_result(), *this );

	for ( auto expr : node->get_args() ) {
		visitExpression( expr );
	}

	VISIT_END( node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( UntypedExpr * node ) {
	MUTATE_START( node );

	node->set_env( maybeMutate( node->get_env(), *this ) );
	node->set_result( maybeMutate( node->get_result(), *this ) );

	for ( auto& expr : node->get_args() ) {
		expr = mutateExpression( expr );
	}

	MUTATE_END( Expression, node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( NameExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( CastExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( VirtualCastExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( AddressExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( LabelAddressExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( UntypedMemberExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( MemberExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( VariableExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( ConstantExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( SizeofExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( AlignofExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( UntypedOffsetofExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( OffsetofExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( OffsetPackExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( AttrExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( LogicalExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( ConditionalExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( CommaExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( TypeExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( AsmExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( ImplicitCopyCtorExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( ConstructorExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( CompoundLiteralExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( RangeExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( UntypedTupleExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( TupleExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( TupleIndexExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( TupleAssignExpr * node ) {
	VISIT_BODY( node );
}

//--------------------------------------------------------------------------
// UntypedExpr
template< typename pass_type >
void PassVisitor< pass_type >::visit( StmtExpr * node ) {
	VISIT_START( node );

	// don't want statements from outer CompoundStmts to be added to this StmtExpr
	ValueGuardPtr< TypeSubstitution * >      oldEnv        ( get_env_ptr() );
	ValueGuardPtr< std::list< Statement* > > oldBeforeStmts( get_beforeStmts() );
	ValueGuardPtr< std::list< Statement* > > oldAfterStmts ( get_afterStmts () );

	Visitor::visit( node );

	VISIT_END( node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( StmtExpr * node ) {
	MUTATE_START( node );

	// don't want statements from outer CompoundStmts to be added to this StmtExpr
	ValueGuardPtr< TypeSubstitution * >      oldEnv        ( get_env_ptr() );
	ValueGuardPtr< std::list< Statement* > > oldBeforeStmts( get_beforeStmts() );
	ValueGuardPtr< std::list< Statement* > > oldAfterStmts ( get_afterStmts () );

	Mutator::mutate( node );

	MUTATE_END( Expression, node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( UniqueExpr * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( VoidType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( BasicType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( PointerType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( ArrayType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( FunctionType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( StructInstType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( UnionInstType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( EnumInstType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( TraitInstType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( TypeInstType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( TupleType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( TypeofType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( AttrType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( VarArgsType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( ZeroType * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( OneType * node ) {
	VISIT_BODY( node );
}

//--------------------------------------------------------------------------
// UntypedExpr
template< typename pass_type >
void PassVisitor< pass_type >::visit( SingleInit * node ) {
	VISIT_START( node );

	visitExpression( node->get_value() );

	VISIT_END( node );
}

template< typename pass_type >
Initializer * PassVisitor< pass_type >::mutate( SingleInit * node ) {
	MUTATE_START( node );

	node->set_value( mutateExpression( node->get_value() ) );

	MUTATE_END( Initializer, node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( ListInit * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( ConstructorInit * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( Subrange * node ) {
	VISIT_BODY( node );
}

template< typename pass_type >
void PassVisitor< pass_type >::visit( Constant * node ) {
	VISIT_BODY( node );
}

//---------------------------------------------------------------------------------------------------------------

template< typename pass_type >
DeclarationWithType * PassVisitor< pass_type >::mutate( ObjectDecl * node ) {
	MUTATE_BODY( DeclarationWithType, node );
}

template< typename pass_type >
DeclarationWithType * PassVisitor< pass_type >::mutate( FunctionDecl * node ) {
	MUTATE_BODY( DeclarationWithType, node );
}

template< typename pass_type >
Declaration * PassVisitor< pass_type >::mutate( StructDecl * node ) {
	MUTATE_BODY( Declaration, node );
}

template< typename pass_type >
Declaration * PassVisitor< pass_type >::mutate( UnionDecl * node ) {
	MUTATE_BODY( Declaration, node );
}

template< typename pass_type >
Declaration * PassVisitor< pass_type >::mutate( EnumDecl * node ) {
	MUTATE_BODY( Declaration, node );
}

template< typename pass_type >
Declaration * PassVisitor< pass_type >::mutate( TraitDecl * node ) {
	MUTATE_BODY( Declaration, node );
}

template< typename pass_type >
TypeDecl * PassVisitor< pass_type >::mutate( TypeDecl * node ) {
	MUTATE_BODY( TypeDecl, node );
}

template< typename pass_type >
Declaration * PassVisitor< pass_type >::mutate( TypedefDecl * node ) {
	MUTATE_BODY( Declaration, node );
}

template< typename pass_type >
AsmDecl * PassVisitor< pass_type >::mutate( AsmDecl * node ) {
	MUTATE_BODY( AsmDecl, node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( FinallyStmt * node ) {
	MUTATE_BODY( Statement, node );
}

template< typename pass_type >
NullStmt * PassVisitor< pass_type >::mutate( NullStmt * node ) {
	MUTATE_BODY( NullStmt, node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( DeclStmt * node ) {
	MUTATE_BODY( Statement, node );
}

template< typename pass_type >
Statement * PassVisitor< pass_type >::mutate( ImplicitCtorDtorStmt * node ) {
	MUTATE_BODY( Statement, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( ApplicationExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( NameExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( AddressExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( LabelAddressExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( CastExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( VirtualCastExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( UntypedMemberExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( MemberExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( VariableExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( ConstantExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( SizeofExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( AlignofExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( UntypedOffsetofExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( OffsetofExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( OffsetPackExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( AttrExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( LogicalExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( ConditionalExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( CommaExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( TypeExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( AsmExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( ImplicitCopyCtorExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( ConstructorExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( CompoundLiteralExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( RangeExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( UntypedTupleExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( TupleExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( TupleIndexExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( TupleAssignExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Expression * PassVisitor< pass_type >::mutate( UniqueExpr * node ) {
	MUTATE_BODY( Expression, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( VoidType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( BasicType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( PointerType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( ArrayType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( FunctionType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( StructInstType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( UnionInstType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( EnumInstType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( TraitInstType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( TypeInstType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( TupleType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( TypeofType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( AttrType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( VarArgsType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( ZeroType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Type * PassVisitor< pass_type >::mutate( OneType * node ) {
	MUTATE_BODY( Type, node );
}

template< typename pass_type >
Initializer * PassVisitor< pass_type >::mutate( ListInit * node ) {
	MUTATE_BODY( Initializer, node );
}

template< typename pass_type >
Initializer * PassVisitor< pass_type >::mutate( ConstructorInit * node ) {
	MUTATE_BODY( Initializer, node );
}

template< typename pass_type >
Subrange * PassVisitor< pass_type >::mutate( Subrange * node  )  {
	MUTATE_BODY( Subrange, node );
}

template< typename pass_type >
Constant * PassVisitor< pass_type >::mutate( Constant * node  )  {
	MUTATE_BODY( Constant, node );
}
