Index: src/AST/Bitfield.hpp
===================================================================
--- src/AST/Bitfield.hpp	(revision 9a0cd9c19e0576d30e3009540ed2a75d356ffcae)
+++ src/AST/Bitfield.hpp	(revision 15934a6a632325b8563214d12538dafc1749ef28)
@@ -57,19 +57,13 @@
 };
 
-/// Adds default printing operator to a bitfield type.
-/// Include in definition to add print function, requires other bitfield operators.
-/// @param N  Number of bits in bitfield
-#define MakeBitfieldPrint( N ) \
-	static const char* Names[]; \
- \
-	void print( std::ostream & os ) const { \
-		if ( (*this).any() ) { \
-			for ( unsigned int i = 0; i < N; i += 1 ) { \
-				if ( (*this)[i] ) { \
-					os << Names[i] << ' '; \
-				} \
-			} \
-		} \
-	}
+template<typename T>
+inline bool operator== ( const bitfield<T> & a, const bitfield<T> & b ) {
+	return a.val == b.val;
+}
+
+template<typename T>
+inline bool operator!= ( const bitfield<T> & a, const bitfield<T> & b ) {
+	return !(a == b);
+}
 
 // Local Variables: //
Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision 9a0cd9c19e0576d30e3009540ed2a75d356ffcae)
+++ src/AST/Convert.cpp	(revision 15934a6a632325b8563214d12538dafc1749ef28)
@@ -525,15 +525,18 @@
 	}
 
+	Expression * visitBaseExpr_skipResultType(const ast::Expr * src, Expression * tgt) {
+
+		tgt->location  = src->location;
+		tgt->env       = convertTypeSubstitution(src->env);
+		tgt->extension = src->extension;
+
+		convertInferUnion(tgt->inferParams, tgt->resnSlots, src->inferred);
+		return tgt;
+	}
+
 	Expression * visitBaseExpr(const ast::Expr * src, Expression * tgt) {
 
-		tgt->location = src->location;
-
 		tgt->result = get<Type>().accept1(src->result);
-		tgt->env    = convertTypeSubstitution(src->env);
-
-		tgt->extension = src->extension;
-		convertInferUnion(tgt->inferParams, tgt->resnSlots, src->inferred);
-
-		return tgt;
+		return visitBaseExpr_skipResultType(src, tgt);
 	}
 
@@ -628,8 +631,8 @@
 
 	const ast::Expr * visit( const ast::VirtualCastExpr * node ) override final {
-		auto expr = visitBaseExpr( node,
+		auto expr = visitBaseExpr_skipResultType( node,
 			new VirtualCastExpr(
 				get<Expression>().accept1(node->arg),
-				nullptr // cast's "to" type is expr's result type; converted in visitBaseExpr
+				get<Type>().accept1(node->result)
 			)
 		);
@@ -849,85 +852,205 @@
 
 	const ast::Expr * visit( const ast::TypeExpr * node ) override final {
-		(void)node;
+		auto expr = visitBaseExpr( node,
+			new TypeExpr(
+				get<Type>().accept1(node->type)
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::AsmExpr * node ) override final {
-		(void)node;
+		auto expr = visitBaseExpr( node,
+			new AsmExpr(
+				get<Expression>().accept1(node->inout),
+				get<Expression>().accept1(node->constraint),
+				get<Expression>().accept1(node->operand)
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::ImplicitCopyCtorExpr * node ) override final {
-		(void)node;
+		auto rslt = new ImplicitCopyCtorExpr(
+			get<ApplicationExpr>().accept1(node->callExpr)
+		);
+
+		rslt->tempDecls = get<ObjectDecl>().acceptL(node->tempDecls);
+		rslt->returnDecls = get<ObjectDecl>().acceptL(node->returnDecls);
+		rslt->dtors = get<Expression>().acceptL(node->dtors);
+
+		auto expr = visitBaseExpr( node, rslt );
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::ConstructorExpr * node ) override final {
-		(void)node;
+		auto expr = visitBaseExpr( node,
+			new ConstructorExpr(
+				get<Expression>().accept1(node->callExpr)
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::CompoundLiteralExpr * node ) override final {
-		(void)node;
+		auto expr = visitBaseExpr_skipResultType( node,
+			new CompoundLiteralExpr(
+				get<Type>().accept1(node->result),
+				get<Initializer>().accept1(node->init)
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::RangeExpr * node ) override final {
-		(void)node;
+		auto expr = visitBaseExpr( node,
+			new RangeExpr(
+				get<Expression>().accept1(node->low),
+				get<Expression>().accept1(node->high)
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::UntypedTupleExpr * node ) override final {
-		(void)node;
+		auto expr = visitBaseExpr( node,
+			new UntypedTupleExpr(
+				get<Expression>().acceptL(node->exprs)
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::TupleExpr * node ) override final {
-		(void)node;
+		auto expr = visitBaseExpr( node,
+			new UntypedTupleExpr(
+				get<Expression>().acceptL(node->exprs)
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::TupleIndexExpr * node ) override final {
-		(void)node;
+		auto expr = visitBaseExpr( node,
+			new TupleIndexExpr(
+				get<Expression>().accept1(node->tuple),
+				node->index
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::TupleAssignExpr * node ) override final {
-		(void)node;
+		auto expr = visitBaseExpr( node,
+			new TupleAssignExpr(
+				get<StmtExpr>().accept1(node->stmtExpr)
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::StmtExpr * node ) override final {
-		(void)node;
+		auto rslt = new StmtExpr(
+			get<CompoundStmt>().accept1(node->stmts)
+		);
+
+		rslt->returnDecls = get<ObjectDecl>().acceptL(node->returnDecls);
+		rslt->dtors       = get<Expression>().acceptL(node->dtors);
+
+		auto expr = visitBaseExpr( node, rslt );
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::UniqueExpr * node ) override final {
-		(void)node;
+		auto rslt = new UniqueExpr(
+			get<Expression>().accept1(node->expr)
+		);
+
+		rslt->object = get<ObjectDecl>  ().accept1(node->object);
+		rslt->var    = get<VariableExpr>().accept1(node->var);
+
+		auto expr = visitBaseExpr( node, rslt );
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::UntypedInitExpr * node ) override final {
-		(void)node;
+		std::list<InitAlternative> initAlts;
+		for (auto ia : node->initAlts) {
+			initAlts.push_back(InitAlternative(
+				get<Type>       ().accept1(ia.type),
+				get<Designation>().accept1(ia.designation)
+			));
+		}
+		auto expr = visitBaseExpr( node,
+			new UntypedInitExpr(
+				get<Expression>().accept1(node->expr),
+				initAlts
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::InitExpr * node ) override final {
-		(void)node;
+		auto expr = visitBaseExpr( node,
+			new InitExpr(
+				get<Expression>().accept1(node->expr),
+				get<Designation>().accept1(node->designation)
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::DeletedExpr * node ) override final {
-		(void)node;
+		auto expr = visitBaseExpr( node,
+			new DeletedExpr(
+				get<Expression>().accept1(node->expr),
+				inCache(node->deleteStmt) ?
+					this->node :
+					get<BaseSyntaxNode>().accept1(node->deleteStmt)
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::DefaultArgExpr * node ) override final {
-		(void)node;
+		auto expr = visitBaseExpr( node,
+			new DefaultArgExpr(
+				get<Expression>().accept1(node->expr)
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
 
 	const ast::Expr * visit( const ast::GenericExpr * node ) override final {
-		(void)node;
+		std::list<GenericExpr::Association> associations;
+		for (auto association : node->associations) {
+			associations.push_back(GenericExpr::Association(
+				get<Type>      ().accept1(association.type),
+				get<Expression>().accept1(association.expr)
+			));
+		}
+		auto expr = visitBaseExpr( node,
+			new GenericExpr(
+				get<Expression>().accept1(node->control),
+				associations
+			)
+		);
+		this->node = expr;
 		return nullptr;
 	}
@@ -1741,7 +1864,6 @@
 	}
 
-	ast::Expr * visitBaseExpr(Expression * old, ast::Expr * nw) {
-
-		nw->result = GET_ACCEPT_1(result, Type);
+	ast::Expr * visitBaseExpr_SkipResultType(Expression * old, ast::Expr * nw) {
+
 		nw->env    = convertTypeSubstitution(old->env);
 
@@ -1750,4 +1872,10 @@
 
 		return nw;
+	}
+
+	ast::Expr * visitBaseExpr(Expression * old, ast::Expr * nw) {
+
+		nw->result = GET_ACCEPT_1(result, Type);
+		return visitBaseExpr_SkipResultType(old, nw);;
 	}
 
@@ -1817,9 +1945,9 @@
 
 	virtual void visit( VirtualCastExpr * old ) override final {
-		this->node = visitBaseExpr( old,
+		this->node = visitBaseExpr_SkipResultType( old,
 			new ast::VirtualCastExpr(
 				old->location,
 				GET_ACCEPT_1(arg, Expr),
-				nullptr // cast's "to" type is expr's result type; converted in visitBaseExpr
+				GET_ACCEPT_1(result, Type)
 			)
 		);
@@ -2046,70 +2174,189 @@
 	}
 
-	virtual void visit( TypeExpr * ) override final {
-
-	}
-
-	virtual void visit( AsmExpr * ) override final {
-
-	}
-
-	virtual void visit( ImplicitCopyCtorExpr * ) override final {
-
-	}
-
-	virtual void visit( ConstructorExpr *  ) override final {
-
-	}
-
-	virtual void visit( CompoundLiteralExpr * ) override final {
-
-	}
-
-	virtual void visit( RangeExpr * ) override final {
-
-	}
-
-	virtual void visit( UntypedTupleExpr * ) override final {
-
-	}
-
-	virtual void visit( TupleExpr * ) override final {
-
-	}
-
-	virtual void visit( TupleIndexExpr * ) override final {
-
-	}
-
-	virtual void visit( TupleAssignExpr * ) override final {
-
-	}
-
-	virtual void visit( StmtExpr *  ) override final {
-
-	}
-
-	virtual void visit( UniqueExpr *  ) override final {
-
-	}
-
-	virtual void visit( UntypedInitExpr *  ) override final {
-
-	}
-
-	virtual void visit( InitExpr *  ) override final {
-
-	}
-
-	virtual void visit( DeletedExpr * ) override final {
-
-	}
-
-	virtual void visit( DefaultArgExpr * ) override final {
-
-	}
-
-	virtual void visit( GenericExpr * ) override final {
-
+	virtual void visit( TypeExpr * old ) override final {
+		this->node = visitBaseExpr( old,
+			new ast::TypeExpr(
+				old->location,
+				GET_ACCEPT_1(type, Type)
+			)
+		);
+	}
+
+	virtual void visit( AsmExpr * old ) override final {
+		this->node = visitBaseExpr( old,
+			new ast::AsmExpr(
+				old->location,
+				GET_ACCEPT_1(inout, Expr),
+				GET_ACCEPT_1(constraint, Expr),
+				GET_ACCEPT_1(operand, Expr)
+			)
+		);
+	}
+
+	virtual void visit( ImplicitCopyCtorExpr * old ) override final {
+		auto rslt = new ast::ImplicitCopyCtorExpr(
+			old->location,
+			GET_ACCEPT_1(callExpr, ApplicationExpr)
+		);
+
+		rslt->tempDecls = GET_ACCEPT_V(tempDecls, ObjectDecl);
+		rslt->returnDecls = GET_ACCEPT_V(returnDecls, ObjectDecl);
+		rslt->dtors = GET_ACCEPT_V(dtors, Expr);
+
+		this->node = visitBaseExpr( old, rslt );
+	}
+
+	virtual void visit( ConstructorExpr * old ) override final {
+		this->node = visitBaseExpr( old,
+			new ast::ConstructorExpr(
+				old->location,
+				GET_ACCEPT_1(callExpr, Expr)
+			)
+		);
+	}
+
+	virtual void visit( CompoundLiteralExpr * old ) override final {
+		this->node = visitBaseExpr_SkipResultType( old,
+			new ast::CompoundLiteralExpr(
+				old->location,
+				GET_ACCEPT_1(result, Type),
+				GET_ACCEPT_1(initializer, Init)
+			)
+		);
+	}
+
+	virtual void visit( RangeExpr * old ) override final {
+		this->node = visitBaseExpr( old,
+			new ast::RangeExpr(
+				old->location,
+				GET_ACCEPT_1(low, Expr),
+				GET_ACCEPT_1(high, Expr)
+			)
+		);
+	}
+
+	virtual void visit( UntypedTupleExpr * old ) override final {
+		this->node = visitBaseExpr( old,
+			new ast::UntypedTupleExpr(
+				old->location,
+				GET_ACCEPT_V(exprs, Expr)
+			)
+		);
+	}
+
+	virtual void visit( TupleExpr * old ) override final {
+		this->node = visitBaseExpr( old,
+			new ast::TupleExpr(
+				old->location,
+				GET_ACCEPT_V(exprs, Expr)
+			)
+		);
+	}
+
+	virtual void visit( TupleIndexExpr * old ) override final {
+		this->node = visitBaseExpr( old,
+			new ast::TupleIndexExpr(
+				old->location,
+				GET_ACCEPT_1(tuple, Expr),
+				old->index
+			)
+		);
+	}
+
+	virtual void visit( TupleAssignExpr * old ) override final {
+		this->node = visitBaseExpr_SkipResultType( old,
+			new ast::TupleAssignExpr(
+				old->location,
+				GET_ACCEPT_1(result, Type),
+				GET_ACCEPT_1(stmtExpr, StmtExpr)
+			)
+		);
+	}
+
+	virtual void visit( StmtExpr * old ) override final {
+		auto rslt = new ast::StmtExpr(
+			old->location,
+			GET_ACCEPT_1(statements, CompoundStmt)
+		);
+		rslt->returnDecls = GET_ACCEPT_V(returnDecls, ObjectDecl);
+		rslt->dtors       = GET_ACCEPT_V(dtors      , Expr);
+
+		this->node = visitBaseExpr_SkipResultType( old, rslt );
+	}
+
+	virtual void visit( UniqueExpr * old ) override final {
+		auto rslt = new ast::UniqueExpr(
+			old->location,
+			GET_ACCEPT_1(expr, Expr)
+		);
+		rslt->object = GET_ACCEPT_1(object, ObjectDecl);
+		rslt->var    = GET_ACCEPT_1(var   , VariableExpr);
+
+		this->node = visitBaseExpr( old, rslt );
+	}
+
+	virtual void visit( UntypedInitExpr * old ) override final {
+		std::vector<ast::InitAlternative> initAlts;
+		for (auto ia : old->initAlts) {
+			initAlts.push_back(ast::InitAlternative(
+				getAccept1< ast::Type, Type * >( ia.type ),
+				getAccept1< ast::Designation, Designation * >( ia.designation )
+			));
+		}
+		this->node = visitBaseExpr( old,
+			new ast::UntypedInitExpr(
+				old->location,
+				GET_ACCEPT_1(expr, Expr),
+				std::move(initAlts)
+			)
+		);
+	}
+
+	virtual void visit( InitExpr * old ) override final {
+		this->node = visitBaseExpr( old,
+			new ast::InitExpr(
+				old->location,
+				GET_ACCEPT_1(expr, Expr),
+				GET_ACCEPT_1(designation, Designation)
+			)
+		);
+	}
+
+	virtual void visit( DeletedExpr * old ) override final {
+		this->node = visitBaseExpr( old,
+			new ast::DeletedExpr(
+				old->location,
+				GET_ACCEPT_1(expr, Expr),
+				inCache(old->deleteStmt) ?
+					this->node :
+					GET_ACCEPT_1(deleteStmt, Node)
+			)
+		);
+	}
+
+	virtual void visit( DefaultArgExpr * old ) override final {
+		this->node = visitBaseExpr( old,
+			new ast::DefaultArgExpr(
+				old->location,
+				GET_ACCEPT_1(expr, Expr)
+			)
+		);
+	}
+
+	virtual void visit( GenericExpr * old ) override final {
+		std::vector<ast::GenericExpr::Association> associations;
+		for (auto association : old->associations) {
+			associations.push_back(ast::GenericExpr::Association(
+				getAccept1< ast::Type, Type * >( association.type ),
+				getAccept1< ast::Expr, Expression * >( association.expr )
+			));
+		}
+		this->node = visitBaseExpr( old,
+			new ast::GenericExpr(
+				old->location,
+				GET_ACCEPT_1(control, Expr),
+				std::move(associations)
+			)
+		);
 	}
 
Index: src/AST/Expr.cpp
===================================================================
--- src/AST/Expr.cpp	(revision 9a0cd9c19e0576d30e3009540ed2a75d356ffcae)
+++ src/AST/Expr.cpp	(revision 15934a6a632325b8563214d12538dafc1749ef28)
@@ -335,4 +335,10 @@
 }
 
+TupleAssignExpr::TupleAssignExpr( 
+	const CodeLocation & loc, const Type * result, const StmtExpr * s )
+: Expr( loc, result ), stmtExpr() {
+	stmtExpr = s;
+}
+
 // --- StmtExpr
 
Index: src/AST/Expr.hpp
===================================================================
--- src/AST/Expr.hpp	(revision 9a0cd9c19e0576d30e3009540ed2a75d356ffcae)
+++ src/AST/Expr.hpp	(revision 15934a6a632325b8563214d12538dafc1749ef28)
@@ -30,4 +30,6 @@
 #define MUTATE_FRIEND template<typename node_t> friend node_t * mutate(const node_t * node);
 
+class ConverterOldToNew;
+
 namespace ast {
 
@@ -528,5 +530,5 @@
 	std::vector<ptr<ObjectDecl>> tempDecls;
 	std::vector<ptr<ObjectDecl>> returnDecls;
-	std::vector<ptr<ObjectDecl>> dtors;
+	std::vector<ptr<Expr>> dtors;
 
 	ImplicitCopyCtorExpr( const CodeLocation& loc, const ApplicationExpr * call )
@@ -635,6 +637,11 @@
 
 	const Expr * accept( Visitor & v ) const override { return v.visit( this ); }
+
+	friend class ::ConverterOldToNew;
+
 private:
 	TupleAssignExpr * clone() const override { return new TupleAssignExpr{ *this }; }
+    TupleAssignExpr( const CodeLocation & loc, const Type * result, const StmtExpr * s );
+
 	MUTATE_FRIEND
 };
Index: src/AST/Node.cpp
===================================================================
--- src/AST/Node.cpp	(revision 9a0cd9c19e0576d30e3009540ed2a75d356ffcae)
+++ src/AST/Node.cpp	(revision 15934a6a632325b8563214d12538dafc1749ef28)
@@ -26,4 +26,6 @@
 #include "Type.hpp"
 #include "TypeSubstitution.hpp"
+
+#include "Print.hpp"
 
 template< typename node_t, enum ast::Node::ref_type ref_t >
@@ -47,7 +49,5 @@
 
 std::ostream & ast::operator<< ( std::ostream & out, const ast::Node * node ) {
-	(void)node;
-	#warning unimplemented
-	assertf(false, "Unimplemented");
+	print(out, node);
 	return out;
 }
Index: src/AST/Print.cpp
===================================================================
--- src/AST/Print.cpp	(revision 15934a6a632325b8563214d12538dafc1749ef28)
+++ src/AST/Print.cpp	(revision 15934a6a632325b8563214d12538dafc1749ef28)
@@ -0,0 +1,513 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Print.cpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Tue May 21 16:20:15 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#include "Print.hpp"
+
+#include "Decl.hpp"
+#include "Expr.hpp"
+#include "Stmt.hpp"
+#include "Type.hpp"
+#include "TypeSubstitution.hpp"
+
+
+namespace ast {
+
+template <typename C, typename... T>
+constexpr auto make_array(T&&... values) ->
+	std::array<C,sizeof...(T)>
+{
+	return std::array<C,sizeof...(T)>{
+		std::forward<T>(values)...
+	};
+}
+
+class Printer : public Visitor {
+public:
+	std::ostream & os;
+	Indenter indent;
+
+	Printer(std::ostream & os, Indenter indent) : os( os ), indent( indent ) {}
+
+private:
+	template< typename C >
+	void printAll( const C & c ) {
+		for ( const auto & i : c ) {
+			if ( i ) {
+				os << indent;
+				i->accept( *this );
+				// need an endl after each element because it's not
+				// easy to know when each individual item should end
+				os << std::endl;
+			} // if
+		} // for
+	}
+
+
+	static const char* Names[];
+
+	struct Names {
+		static constexpr auto FuncSpecifiers = make_array<const char*>(
+			"inline", "_Noreturn", "fortran"
+		);
+
+		static constexpr auto StorageClasses = make_array<const char*>(
+			"extern", "static", "auto", "register", "_Thread_local"
+		);
+
+		static constexpr auto Qualifiers = make_array<const char*>(
+			"const", "restrict", "volatile", "lvalue", "mutex", "_Atomic"
+		);
+	};
+
+	template<typename storage_t, size_t N>
+	void print(const storage_t & storage, const std::array<const char *, N> & Names ) {
+		if ( storage.any() ) {
+			for ( size_t i = 0; i < Names.size(); i += 1 ) {
+				if ( storage[i] ) {
+					os << Names[i] << ' ';
+				}
+			}
+		}
+	}
+
+	void print( const ast::Function::Specs & specs ) {
+		print(specs, Names::FuncSpecifiers);
+	}
+
+	void print( const ast::Storage::Classes & storage ) {
+		print(storage, Names::StorageClasses);
+	}
+
+	void print( const ast::CV::Qualifiers & qualifiers ) {
+		print(qualifiers, Names::Qualifiers);
+	}
+
+public:
+	virtual const ast::DeclWithType *     visit( const ast::ObjectDecl           * node ) {
+		if ( node->name != "" ) os << node->name << ": ";
+
+		if ( node->linkage != Linkage::Cforall ) {
+			os << Linkage::name( node->linkage ) << " ";
+		} // if
+
+		print( node->storage );
+
+		if ( node->type ) {
+			node->type->accept( *this );
+		} else {
+			os << " untyped entity ";
+		} // if
+
+		if ( node->init ) {
+			os << " with initializer (" << (
+				node->init->maybeConstructed
+					? "maybe constructed"
+					: "not constructed"
+				) << ")" << std::endl << indent+1;
+
+			++indent;
+			node->init->accept( *this );
+			--indent;
+			os << std::endl;
+		} // if
+
+		if ( ! node->attributes.empty() ) {
+			os << std::endl << indent << "... with attributes:" << std::endl;
+			++indent;
+			printAll( node->attributes );
+			--indent;
+		}
+
+		if ( node->bitfieldWidth ) {
+			os << indent << " with bitfield width ";
+			node->bitfieldWidth->accept( *this );
+		} // if
+		return node;
+	}
+
+	virtual const ast::DeclWithType *     visit( const ast::FunctionDecl         * node ) {
+		return node;
+	}
+
+	virtual const ast::Decl *             visit( const ast::StructDecl           * node ) {
+		return node;
+	}
+
+	virtual const ast::Decl *             visit( const ast::UnionDecl            * node ) {
+		return node;
+	}
+
+	virtual const ast::Decl *             visit( const ast::EnumDecl             * node ) {
+		return node;
+	}
+
+	virtual const ast::Decl *             visit( const ast::TraitDecl            * node ) {
+		return node;
+	}
+
+	virtual const ast::Decl *             visit( const ast::TypeDecl             * node ) {
+		return node;
+	}
+
+	virtual const ast::Decl *             visit( const ast::TypedefDecl          * node ) {
+		return node;
+	}
+
+	virtual const ast::AsmDecl *          visit( const ast::AsmDecl              * node ) {
+		return node;
+	}
+
+	virtual const ast::StaticAssertDecl * visit( const ast::StaticAssertDecl     * node ) {
+		return node;
+	}
+
+	virtual const ast::CompoundStmt *     visit( const ast::CompoundStmt         * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::ExprStmt             * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::AsmStmt              * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::DirectiveStmt        * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::IfStmt               * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::WhileStmt            * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::ForStmt              * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::SwitchStmt           * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::CaseStmt             * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::BranchStmt           * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::ReturnStmt           * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::ThrowStmt            * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::TryStmt              * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::CatchStmt            * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::FinallyStmt          * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::WaitForStmt          * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::WithStmt             * node ) {
+		return node;
+	}
+
+	virtual const ast::NullStmt *         visit( const ast::NullStmt             * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::DeclStmt             * node ) {
+		return node;
+	}
+
+	virtual const ast::Stmt *             visit( const ast::ImplicitCtorDtorStmt * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::ApplicationExpr      * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::UntypedExpr          * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::NameExpr             * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::AddressExpr          * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::LabelAddressExpr     * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::CastExpr             * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::KeywordCastExpr      * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::VirtualCastExpr      * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::UntypedMemberExpr    * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::MemberExpr           * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::VariableExpr         * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::ConstantExpr         * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::SizeofExpr           * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::AlignofExpr          * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::UntypedOffsetofExpr  * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::OffsetofExpr         * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::OffsetPackExpr       * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::LogicalExpr          * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::ConditionalExpr      * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::CommaExpr            * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::TypeExpr             * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::AsmExpr              * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::ImplicitCopyCtorExpr * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::ConstructorExpr      * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::CompoundLiteralExpr  * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::RangeExpr            * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::UntypedTupleExpr     * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::TupleExpr            * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::TupleIndexExpr       * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::TupleAssignExpr      * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::StmtExpr             * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::UniqueExpr           * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::UntypedInitExpr      * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::InitExpr             * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::DeletedExpr          * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::DefaultArgExpr       * node ) {
+		return node;
+	}
+
+	virtual const ast::Expr *             visit( const ast::GenericExpr          * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::VoidType             * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::BasicType            * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::PointerType          * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::ArrayType            * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::ReferenceType        * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::QualifiedType        * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::FunctionType         * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::StructInstType       * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::UnionInstType        * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::EnumInstType         * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::TraitInstType        * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::TypeInstType         * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::TupleType            * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::TypeofType           * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::VarArgsType          * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::ZeroType             * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::OneType              * node ) {
+		return node;
+	}
+
+	virtual const ast::Type *             visit( const ast::GlobalScopeType      * node ) {
+		return node;
+	}
+
+	virtual const ast::Designation *      visit( const ast::Designation          * node ) {
+		return node;
+	}
+
+	virtual const ast::Init *             visit( const ast::SingleInit           * node ) {
+		return node;
+	}
+
+	virtual const ast::Init *             visit( const ast::ListInit             * node ) {
+		return node;
+	}
+
+	virtual const ast::Init *             visit( const ast::ConstructorInit      * node ) {
+		return node;
+	}
+
+	virtual const ast::Attribute *        visit( const ast::Attribute            * node ) {
+		return node;
+	}
+
+	virtual const ast::TypeSubstitution * visit( const ast::TypeSubstitution     * node ) {
+		return node;
+	}
+
+};
+
+void print( std::ostream & os, const ast::Node * node, Indenter indent ) {
+	Printer printer { os, indent };
+	node->accept(printer);
+}
+
+// Annoyingly these needed to be defined out of line to avoid undefined references.
+// The size here needs to be explicit but at least the compiler will produce an error
+// if the wrong size is specified
+constexpr std::array<const char*, 3> Printer::Names::FuncSpecifiers;
+constexpr std::array<const char*, 5> Printer::Names::StorageClasses;
+constexpr std::array<const char*, 6> Printer::Names::Qualifiers;
+}
Index: src/AST/Print.hpp
===================================================================
--- src/AST/Print.hpp	(revision 15934a6a632325b8563214d12538dafc1749ef28)
+++ src/AST/Print.hpp	(revision 15934a6a632325b8563214d12538dafc1749ef28)
@@ -0,0 +1,31 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Print.hpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Tue May 21 16:20:15 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#pragma once
+
+#include <iosfwd>
+
+#include "AST/Node.hpp"
+#include "Common/Indenter.h"
+
+namespace ast {
+
+void print( std::ostream & os, const ast::Node * node, Indenter indent = {} );
+
+inline void print( std::ostream & os, const ast::Node * node, unsigned int indent ) {
+    print( os, node, Indenter{ Indenter::tabsize, indent });
+}
+
+}
Index: src/AST/module.mk
===================================================================
--- src/AST/module.mk	(revision 9a0cd9c19e0576d30e3009540ed2a75d356ffcae)
+++ src/AST/module.mk	(revision 15934a6a632325b8563214d12538dafc1749ef28)
@@ -24,4 +24,5 @@
 	AST/LinkageSpec.cpp \
 	AST/Node.cpp \
+	AST/Print.cpp \
 	AST/Stmt.cpp \
 	AST/Type.cpp \
