Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
+++ src/AST/Convert.cpp	(revision 6380f7877cde845f32dac6bfe1878417869955c0)
@@ -1326,10 +1326,16 @@
 
 	const ast::Attribute * visit( const ast::Attribute * node ) override final {
-		(void)node;
+		auto attr = new Attribute(
+			node->name,
+			get<Expression>().acceptL(node->parameters)
+		);
+		this->node = attr;
 		return nullptr;
 	}
 
 	const ast::TypeSubstitution * visit( const ast::TypeSubstitution * node ) override final {
-		(void)node;
+		// Handled by convertTypeSubstitution helper instead.
+		// TypeSubstitution is not a node in the old model, so the conversion result wouldn't fit in this->node.
+		assert( 0 );
 		return nullptr;
 	}
@@ -2670,14 +2676,18 @@
 
 	virtual void visit( Constant * ) override final {
-
-	}
-
-	virtual void visit( Attribute * ) override final {
-
+		// Handled in visit( ConstantEpxr * ).
+		// In the new tree, Constant fields are inlined into containing ConstantExpression.
+		assert( 0 );
+	}
+
+	virtual void visit( Attribute * old ) override final {
+		this->node = new ast::Attribute(
+			old->name,
+			GET_ACCEPT_V( parameters, Expr )
+		);
 	}
 
 	virtual void visit( AttrExpr * ) override final {
-
-		assert( 0 );
+		assertf( false, "AttrExpr deprecated in new AST." );
 	}
 };
Index: src/AST/Decl.hpp
===================================================================
--- src/AST/Decl.hpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
+++ src/AST/Decl.hpp	(revision 6380f7877cde845f32dac6bfe1878417869955c0)
@@ -232,11 +232,10 @@
 	AggregateDecl* set_body( bool b ) { body = b; return this; }
 
-private:
-	AggregateDecl * clone() const override = 0;
-	MUTATE_FRIEND
-
-protected:
 	/// Produces a name for the kind of aggregate
 	virtual std::string typeString() const = 0;
+
+private:
+	AggregateDecl * clone() const override = 0;
+	MUTATE_FRIEND
 };
 
@@ -256,9 +255,10 @@
 
 	const Decl * accept( Visitor & v ) const override { return v.visit( this ); }
+
+	std::string typeString() const override { return "struct"; }
+
 private:
 	StructDecl * clone() const override { return new StructDecl{ *this }; }
 	MUTATE_FRIEND
-
-	std::string typeString() const override { return "struct"; }
 };
 
@@ -271,9 +271,10 @@
 
 	const Decl * accept( Visitor& v ) const override { return v.visit( this ); }
+
+	std::string typeString() const override { return "union"; }
+
 private:
 	UnionDecl * clone() const override { return new UnionDecl{ *this }; }
 	MUTATE_FRIEND
-
-	std::string typeString() const override { return "union"; }
 };
 
@@ -289,9 +290,10 @@
 
 	const Decl * accept( Visitor & v ) const override { return v.visit( this ); }
+
+	std::string typeString() const override { return "enum"; }
+
 private:
 	EnumDecl * clone() const override { return new EnumDecl{ *this }; }
 	MUTATE_FRIEND
-
-	std::string typeString() const override { return "enum"; }
 
 	/// Map from names to enumerator values; kept private for lazy initialization
@@ -307,9 +309,10 @@
 
 	const Decl * accept( Visitor & v ) const override { return v.visit( this ); }
+
+	std::string typeString() const override { return "trait"; }
+
 private:
 	TraitDecl * clone() const override { return new TraitDecl{ *this }; }
 	MUTATE_FRIEND
-
-	std::string typeString() const override { return "trait"; }
 };
 
Index: src/AST/DeclReplacer.cpp
===================================================================
--- src/AST/DeclReplacer.cpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
+++ src/AST/DeclReplacer.cpp	(revision 6380f7877cde845f32dac6bfe1878417869955c0)
@@ -14,5 +14,82 @@
 //
 
-#warning unimplemented
+#include "DeclReplacer.hpp"
+#include "Expr.hpp"
+#include "Type.hpp"
+
+#include "Pass.hpp"
+
+namespace ast {
+
+namespace DeclReplacer {
+	namespace {
+		struct DeclReplacer {
+		private:
+			const DeclMap & declMap;
+			const TypeMap & typeMap;
+			bool debug;
+
+		public:
+			DeclReplacer(const DeclMap & declMap, const TypeMap & typeMap, bool debug)
+				: declMap( declMap ), typeMap( typeMap ), debug( debug )
+			{}
+
+			const ast::VariableExpr * previsit( const ast::VariableExpr * );
+			const ast::TypeInstType * previsit( const ast::TypeInstType * );
+		};
+	}
+
+	const ast::Node * replace( const ast::Node * node, const DeclMap & declMap, const TypeMap & typeMap, bool debug ) {
+		if(!node) return nullptr;
+		Pass<DeclReplacer> replacer = { declMap, typeMap, debug };
+		return node->accept( replacer );
+	}
+
+	const ast::Node * replace( const ast::Node * node, const DeclMap & declMap, bool debug ) {
+		TypeMap typeMap;
+		return replace( node, declMap, typeMap, debug );
+	}
+
+	const ast::Node * replace( const ast::Node * node, const TypeMap & typeMap, bool debug ) {
+		DeclMap declMap;
+		return replace( node, declMap, typeMap, debug );
+	}
+
+	namespace {
+		// replace variable with new node from decl map
+		const ast::VariableExpr * DeclReplacer::previsit( const VariableExpr * varExpr ) {
+			// xxx - assertions and parameters aren't accounted for in this... (i.e. they aren't inserted into the map when it's made, only DeclStmts are)
+			if ( !declMap.count( varExpr->var ) ) return varExpr;
+
+			auto replacement = declMap.at( varExpr->var );
+			if ( debug ) {
+				std::cerr << "replacing variable reference: "
+					<< (void*)varExpr->var.get() << " " << varExpr->var
+					<< " with " << (void*)replacement << " " << replacement
+					<< std::endl;
+			}
+			auto nexpr = mutate(varExpr);
+			nexpr->var = replacement;
+			return nexpr;
+		}
+
+		const TypeInstType * DeclReplacer::previsit( const TypeInstType * inst ) {
+			if ( !typeMap.count( inst->base ) ) return inst;
+
+			auto replacement = typeMap.at( inst->base );
+			if ( debug ) {
+				std::cerr << "replacing type reference: "
+					<< (void*)inst->base.get() << " " << inst->base
+					<< " with " << (void*)replacement << " " << replacement
+					<< std::endl;
+			}
+			auto ninst = mutate(inst);
+			ninst->base = replacement;
+			return ninst;
+		}
+	}
+}
+
+}
 
 // Local Variables: //
Index: src/AST/DeclReplacer.hpp
===================================================================
--- src/AST/DeclReplacer.hpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
+++ src/AST/DeclReplacer.hpp	(revision 6380f7877cde845f32dac6bfe1878417869955c0)
@@ -25,10 +25,10 @@
 
 	namespace DeclReplacer {
-		using DeclMap = std::unordered_map< DeclWithType*, DeclWithType* >;
-		using TypeMap = std::unordered_map< TypeDecl*, TypeDecl* >;
+		using DeclMap = std::unordered_map< const DeclWithType *, const DeclWithType * >;
+		using TypeMap = std::unordered_map< const TypeDecl *, const TypeDecl * >;
 
-		void replace( Node* node, const DeclMap& declMap );
-		void replace( Node* node, const TypeMap& typeMap );
-		void replace( Node* node, const DeclMap& declMap, const TypeMap& typeMap );
+		const Node * replace( const Node * node, const DeclMap & declMap, bool debug = false );
+		const Node * replace( const Node * node, const TypeMap & typeMap, bool debug = false );
+		const Node * replace( const Node * node, const DeclMap & declMap, const TypeMap & typeMap, bool debug = false );
 	}
 }
Index: src/AST/Pass.cpp
===================================================================
--- src/AST/Pass.cpp	(revision 6380f7877cde845f32dac6bfe1878417869955c0)
+++ src/AST/Pass.cpp	(revision 6380f7877cde845f32dac6bfe1878417869955c0)
@@ -0,0 +1,23 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Pass.cpp --
+//
+// Author           : Thierry Delisle
+// Created On       : Wed May 22 15:00:33 2019
+// Last Modified By :
+// Last Modified On :
+// Update Count     :
+//
+
+#include "Pass.hpp"
+
+namespace ast {
+
+PassVisitorStats pass_visitor_stats;
+// Stats::Counters::SimpleCounter * BaseSyntaxNode::new_nodes = nullptr;
+
+};
Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
+++ src/AST/Pass.impl.hpp	(revision 6380f7877cde845f32dac6bfe1878417869955c0)
@@ -841,5 +841,5 @@
 		for( const auto & clause : node->clauses ) {
 
-			Expr * func = clause.target.func ? clause.target.func->accept(*this) : nullptr;
+			const Expr * func = clause.target.func ? clause.target.func->accept(*this) : nullptr;
 			if(func != clause.target.func) mutated = true;
 
@@ -852,8 +852,8 @@
 			}
 
-			Stmt * stmt = clause.stmt ? clause.stmt->accept(*this) : nullptr;
+			const Stmt * stmt = clause.stmt ? clause.stmt->accept(*this) : nullptr;
 			if(stmt != clause.stmt) mutated = true;
 
-			Expr * cond = clause.cond ? clause.cond->accept(*this) : nullptr;
+			const Expr * cond = clause.cond ? clause.cond->accept(*this) : nullptr;
 			if(cond != clause.cond) mutated = true;
 
@@ -1567,5 +1567,5 @@
 		bool mutated = false;
 		for( const auto & assoc : node->associations ) {
-			Type * type = nullptr;
+			const Type * type = nullptr;
 			if( assoc.type ) {
 				guard_indexer guard { *this };
@@ -1573,5 +1573,5 @@
 				if( type != assoc.type ) mutated = true;
 			}
-			Expr * expr = nullptr;
+			const Expr * expr = nullptr;
 			if( assoc.expr ) {
 				expr = assoc.expr->accept( *this );
@@ -1685,5 +1685,5 @@
 	VISIT_START( node );
 
-	__pass::indexer::addStruct( node->name, 0, pass );
+	__pass::indexer::addStruct( pass, 0, node->name );
 
 	VISIT({
@@ -1702,5 +1702,5 @@
 	VISIT_START( node );
 
-	__pass::indexer::addStruct( node->name, 0, pass );
+	__pass::indexer::addStruct( pass, 0, node->name );
 
 	{
@@ -1885,46 +1885,46 @@
 }
 
-// //--------------------------------------------------------------------------
-// // TypeSubstitution
-// template< typename pass_t >
-// const ast::TypeSubstitution * ast::Pass< pass_t >::visit( const ast::TypeSubstitution * node ) {
-// 	VISIT_START( node );
-
-// 	VISIT(
-// 		{
-// 			bool mutated = false;
-// 			std::unordered_map< std::string, ast::ptr< ast::Type > > new_map;
-// 			for ( const auto & p : node->typeEnv ) {
-// 				guard_indexer guard { *this };
-// 				auto new_node = p.second->accept( *this );
-// 				if (new_node != p.second) mutated = false;
-// 				new_map.insert({ p.first, new_node });
-// 			}
-// 			if (mutated) {
-// 				auto new_node = mutate( node );
-// 				new_node->typeEnv.swap( new_map );
-// 				node = new_node;
-// 			}
-// 		}
-
-// 		{
-// 			bool mutated = false;
-// 			std::unordered_map< std::string, ast::ptr< ast::Expr > > new_map;
-// 			for ( const auto & p : node->varEnv ) {
-// 				guard_indexer guard { *this };
-// 				auto new_node = p.second->accept( *this );
-// 				if (new_node != p.second) mutated = false;
-// 				new_map.insert({ p.first, new_node });
-// 			}
-// 			if (mutated) {
-// 				auto new_node = mutate( node );
-// 				new_node->varEnv.swap( new_map );
-// 				node = new_node;
-// 			}
-// 		}
-// 	)
-
-// 	VISIT_END( TypeSubstitution, node );
-// }
+//--------------------------------------------------------------------------
+// TypeSubstitution
+template< typename pass_t >
+const ast::TypeSubstitution * ast::Pass< pass_t >::visit( const ast::TypeSubstitution * node ) {
+	VISIT_START( node );
+
+	VISIT(
+		{
+			bool mutated = false;
+			std::unordered_map< std::string, ast::ptr< ast::Type > > new_map;
+			for ( const auto & p : node->typeEnv ) {
+				guard_indexer guard { *this };
+				auto new_node = p.second->accept( *this );
+				if (new_node != p.second) mutated = false;
+				new_map.insert({ p.first, new_node });
+			}
+			if (mutated) {
+				auto new_node = mutate( node );
+				new_node->typeEnv.swap( new_map );
+				node = new_node;
+			}
+		}
+
+		{
+			bool mutated = false;
+			std::unordered_map< std::string, ast::ptr< ast::Expr > > new_map;
+			for ( const auto & p : node->varEnv ) {
+				guard_indexer guard { *this };
+				auto new_node = p.second->accept( *this );
+				if (new_node != p.second) mutated = false;
+				new_map.insert({ p.first, new_node });
+			}
+			if (mutated) {
+				auto new_node = mutate( node );
+				new_node->varEnv.swap( new_map );
+				node = new_node;
+			}
+		}
+	)
+
+	VISIT_END( TypeSubstitution, node );
+}
 
 #undef VISIT_START
Index: src/AST/Pass.proto.hpp
===================================================================
--- src/AST/Pass.proto.hpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
+++ src/AST/Pass.proto.hpp	(revision 6380f7877cde845f32dac6bfe1878417869955c0)
@@ -126,4 +126,8 @@
 	template<typename pass_t, typename node_t>
 	static inline auto previsit( pass_t & pass, const node_t * & node, int ) -> decltype( pass.previsit( node ), void() ) {
+		static_assert(
+			std::is_base_of<const node_t, typename std::remove_pointer<decltype( pass.previsit( node ) )>::type >::value,
+			"Previsit may not change the type of the node. Use postvisit instead."
+		);
 		node = pass.previsit( node );
 		assert(node);
Index: src/AST/Stmt.cpp
===================================================================
--- src/AST/Stmt.cpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
+++ src/AST/Stmt.cpp	(revision 6380f7877cde845f32dac6bfe1878417869955c0)
@@ -16,12 +16,41 @@
 #include "Stmt.hpp"
 
+
 #include "DeclReplacer.hpp"
+#include "Type.hpp"
 
 namespace ast {
 
 // --- CompoundStmt
-CompoundStmt::CompoundStmt( const CompoundStmt& o ) : Stmt(o), kids(o.kids) {
-#   warning unimplemented
-	assert(!"implemented");
+CompoundStmt::CompoundStmt( const CompoundStmt& other ) : Stmt(other), kids(other.kids) {
+	// when cloning a compound statement, we may end up cloning declarations which
+	// are referred to by VariableExprs throughout the block. Cloning a VariableExpr
+	// does a shallow copy, so the VariableExpr will end up pointing to the original
+	// declaration. If the original declaration is deleted, e.g. because the original
+	// CompoundStmt is deleted, then we have a dangling pointer. To avoid this case,
+	// find all DeclarationWithType nodes (since a VariableExpr must point to a
+	// DeclarationWithType) in the original CompoundStmt and map them to the cloned
+	// node in the new CompoundStmt ('this'), then replace the Declarations referred to
+	// by each VariableExpr according to the constructed map. Note that only the declarations
+	// in the current level are collected into the map, because child CompoundStmts will
+	// recursively execute this routine. There may be more efficient ways of doing
+	// this.
+	DeclReplacer::DeclMap declMap;
+	auto origit = other.kids.begin();
+	for ( const Stmt * s : kids ) {
+		assert( origit != other.kids.end() );
+		const Stmt * origStmt = *origit++;
+		if ( const DeclStmt * declStmt = dynamic_cast< const DeclStmt * >( s ) ) {
+			const DeclStmt * origDeclStmt = strict_dynamic_cast< const DeclStmt * >( origStmt );
+			if ( const DeclWithType * dwt = dynamic_cast< const DeclWithType * > ( declStmt->decl.get() ) ) {
+				const DeclWithType * origdwt = strict_dynamic_cast< const DeclWithType * > ( origDeclStmt->decl.get() );
+				assert( dwt->name == origdwt->name );
+				declMap[ origdwt ] = dwt;
+			} else assert( ! dynamic_cast< const DeclWithType * > ( origDeclStmt->decl.get() ) );
+		} else assert( ! dynamic_cast< const DeclStmt * > ( s ) );
+	}
+	if ( ! declMap.empty() ) {
+		DeclReplacer::replace( this, declMap );
+	}
 }
 
Index: src/AST/module.mk
===================================================================
--- src/AST/module.mk	(revision d8938622b768abd2937d6a50f68591594aceea55)
+++ src/AST/module.mk	(revision 6380f7877cde845f32dac6bfe1878417869955c0)
@@ -25,4 +25,5 @@
 	AST/LinkageSpec.cpp \
 	AST/Node.cpp \
+	AST/Pass.cpp \
 	AST/Print.cpp \
 	AST/Stmt.cpp \
