Index: src/AST/Expr.cpp
===================================================================
--- src/AST/Expr.cpp	(revision 5902625312b0f730ef5d4cca6254a8283c7cff36)
+++ src/AST/Expr.cpp	(revision f23de79d3598c65a1d98ae2a637ac911add4fd81)
@@ -20,6 +20,8 @@
 #include <vector>
 
+#include "GenericSubstitution.hpp"
 #include "Stmt.hpp"
 #include "Type.hpp"
+#include "TypeSubstitution.hpp"
 #include "Common/utility.h"
 #include "Common/SemanticError.h"
@@ -157,5 +159,13 @@
 	assert( aggregate->result );
 
-//	assert(!"unimplemented; need TypeSubstitution, genericSubstitution");
+	#warning Needs GenericSubsitution.cpp building to work correctly
+	result.set_and_mutate( mem->get_type() )->qualifiers |= aggregate->result->qualifiers | CV::Lvalue;  // FIXME temporary patch
+
+	// // take ownership of member type
+	// result = mem->get_type();
+	// // substitute aggregate generic parameters into member type
+	// genericSubsitution( aggregate->result ).apply( result );
+	// // ensure lvalue and appropriate restrictions from aggregate type
+	// result.get_and_mutate()->qualifiers |= aggregate->result->qualifiers | CV::Lvalue;
 }
 
Index: src/AST/GenericSubstitution.cpp
===================================================================
--- src/AST/GenericSubstitution.cpp	(revision f23de79d3598c65a1d98ae2a637ac911add4fd81)
+++ src/AST/GenericSubstitution.cpp	(revision f23de79d3598c65a1d98ae2a637ac911add4fd81)
@@ -0,0 +1,63 @@
+//
+// 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.
+//
+// GenericSubstitution.cpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Wed May 22 14:15:00 2019
+// Last Modified By : Aaron B. Moss
+// Created On       : Wed May 22 14:15:00 2019
+// Update Count     : 1
+//
+
+#include "GenericSubstitution.hpp"
+
+#include <cassert>
+#include <utility>               // for move
+
+#include "Decl.hpp"
+#include "Node.hpp"              // for maybe_accept
+#include "Pass.hpp"
+#include "Type.hpp"
+#include "TypeSubstitution.cpp"
+
+namespace ast {
+
+namespace {
+	struct GenericSubstitutionBuilder : public WithShortCircuiting {
+		TypeSubstitution sub;
+
+		void previsit( const Type * ty ) {
+			assertf( false, "Attempted generic substitution for non-aggregate type: %s", 
+				toString( ty ).c_str() );
+		}
+
+		void previsit( const ReferenceType * ty ) {
+			// do nothing; allows substitution from base type
+		}
+
+		void previsit( const ReferenceToType * ty ) {
+			visit_children = false;
+			// build substitution from base parameters
+			const AggregateDecl * aggr = ty->aggr();
+			sub = TypeSubstitution{ aggr->params.begin(), aggr->params.end(), ty->params.begin() };
+		}
+	};
+}
+
+TypeSubstitution genericSubsitution( const Type * ty ) {
+	Pass<GenericSubstitutionBuilder> builder;
+	maybe_accept( ty, builder );
+	return std::move(builder.pass.sub);
+}
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/AST/GenericSubstitution.hpp
===================================================================
--- src/AST/GenericSubstitution.hpp	(revision f23de79d3598c65a1d98ae2a637ac911add4fd81)
+++ src/AST/GenericSubstitution.hpp	(revision f23de79d3598c65a1d98ae2a637ac911add4fd81)
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+// GenericSubstitution.hpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Wed May 22 14:15:00 2019
+// Last Modified By : Aaron B. Moss
+// Created On       : Wed May 22 14:15:00 2019
+// Update Count     : 1
+//
+
+#pragma once
+
+#include "TypeSubstitution.hpp"
+
+namespace ast {
+
+class Type;
+
+TypeSubstitution genericSubsitution( const Type * );
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/AST/Node.cpp
===================================================================
--- src/AST/Node.cpp	(revision 5902625312b0f730ef5d4cca6254a8283c7cff36)
+++ src/AST/Node.cpp	(revision f23de79d3598c65a1d98ae2a637ac911add4fd81)
@@ -35,10 +35,6 @@
 void ast::ptr_base<node_t, ref_t>::_dec( const node_t * node ) { node->decrement(ref_t); }
 
-/// Sets this pointer to a mutated version of a pointer (possibly) owned elsehere.
-/// Returns a mutable version of the pointer in this node.
-template< typename node_t, enum ast::Node::ref_type ref_t >
-node_t * ast::ptr_base<node_t, ref_t>::set_and_mutate( const node_t * n ) {
-	// ensure ownership of `n` by this node to avoid spurious single-owner mutates
-	assign( n );
+template< typename node_t, enum ast::Node::ref_type ref_t >
+node_t * ast::ptr_base<node_t, ref_t>::get_and_mutate() {
 	// get mutable version of `n`
 	auto r = mutate( node );
@@ -46,4 +42,12 @@
 	assign( r );
 	return r;
+}
+
+template< typename node_t, enum ast::Node::ref_type ref_t >
+node_t * ast::ptr_base<node_t, ref_t>::set_and_mutate( const node_t * n ) {
+	// ensure ownership of `n` by this node to avoid spurious single-owner mutates
+	assign( n );
+	// return mutable version
+	return get_and_mutate();
 }
 
Index: src/AST/Node.hpp
===================================================================
--- src/AST/Node.hpp	(revision 5902625312b0f730ef5d4cca6254a8283c7cff36)
+++ src/AST/Node.hpp	(revision f23de79d3598c65a1d98ae2a637ac911add4fd81)
@@ -94,4 +94,10 @@
 std::ostream& operator<< ( std::ostream& out, const Node * node );
 
+/// Call a visitor on a possibly-null node
+template<typename node_t>
+auto maybe_accept( const node_t * n, Visitor & v ) -> decltype( n->accept(v) ) {
+	return n ? n->accept( v ) : nullptr;
+}
+
 /// Base class for the smart pointer types
 /// should never really be used.
@@ -141,4 +147,7 @@
 	const o_node_t * as() const { return dynamic_cast<const o_node_t *>(node); }
 
+	/// Returns a mutable version of the pointer in this node.
+	node_t * get_and_mutate();
+
 	/// Sets this pointer to a mutated version of a pointer (possibly) owned elsehere.
 	/// Returns a mutable version of the pointer in this node.
Index: src/AST/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision 5902625312b0f730ef5d4cca6254a8283c7cff36)
+++ src/AST/Pass.hpp	(revision f23de79d3598c65a1d98ae2a637ac911add4fd81)
@@ -223,4 +223,5 @@
 };
 
+/// Apply a pass to an entire translation unit
 template<typename pass_t>
 void accept_all( std::list< ast::ptr<ast::Decl> > &, ast::Pass<pass_t> & visitor );
Index: src/AST/Print.cpp
===================================================================
--- src/AST/Print.cpp	(revision 5902625312b0f730ef5d4cca6254a8283c7cff36)
+++ src/AST/Print.cpp	(revision f23de79d3598c65a1d98ae2a637ac911add4fd81)
@@ -656,4 +656,20 @@
 
 	virtual const ast::TypeSubstitution * visit( const ast::TypeSubstitution * node ) {
+		os << indent << "Types:" << std::endl;
+		for ( const auto& i : *node ) {
+			os << indent+1 << i.first << " -> ";
+			indent += 2;
+			i.second->accept( *this );
+			indent -= 2;
+			os << std::endl;
+		}
+		os << indent << "Non-types:" << std::endl;
+		for ( auto i = node->beginVar(); i != node->endVar(); ++i ) {
+			os << indent+1 << i->first << " -> ";
+			indent += 2;
+			i->second->accept( *this );
+			indent -= 2;
+			os << std::endl;
+		}
 		return node;
 	}
Index: src/AST/Type.hpp
===================================================================
--- src/AST/Type.hpp	(revision 5902625312b0f730ef5d4cca6254a8283c7cff36)
+++ src/AST/Type.hpp	(revision f23de79d3598c65a1d98ae2a637ac911add4fd81)
@@ -47,9 +47,9 @@
 	bool is_atomic() const { return qualifiers.is_atomic; }
 
-	void set_const( bool v ) { qualifiers.is_const = v; }
-	void set_restrict( bool v ) { qualifiers.is_restrict = v; }
-	void set_lvalue( bool v ) { qualifiers.is_lvalue = v; }
-	void set_mutex( bool v ) { qualifiers.is_mutex = v; }
-	void set_atomic( bool v ) { qualifiers.is_atomic = v; }
+	Type * set_const( bool v ) { qualifiers.is_const = v; return this; }
+	Type * set_restrict( bool v ) { qualifiers.is_restrict = v; return this; }
+	Type * set_lvalue( bool v ) { qualifiers.is_lvalue = v; return this; }
+	Type * set_mutex( bool v ) { qualifiers.is_mutex = v; return this; }
+	Type * set_atomic( bool v ) { qualifiers.is_atomic = v; return this; }
 
 	/// How many elemental types are represented by this type
Index: src/AST/TypeSubstitution.hpp
===================================================================
--- src/AST/TypeSubstitution.hpp	(revision 5902625312b0f730ef5d4cca6254a8283c7cff36)
+++ src/AST/TypeSubstitution.hpp	(revision f23de79d3598c65a1d98ae2a637ac911add4fd81)
@@ -25,9 +25,10 @@
 #include "Fwd.hpp"        // for UniqueId
 #include "ParseNode.hpp"
-#include "Type.hpp"       // for ptr<Type>
+#include "Type.hpp"       
 #include "Common/SemanticError.h"  // for SemanticError
 #include "Visitor.hpp"
 #include "Decl.hpp"
 #include "Expr.hpp"
+#include "Node.hpp"
 
 namespace ast {
@@ -43,6 +44,22 @@
 	TypeSubstitution &operator=( const TypeSubstitution &other );
 
-	template< typename SynTreeClass > int apply( SynTreeClass *&input ) const;
-	template< typename SynTreeClass > int applyFree( SynTreeClass *&input ) const;
+	template< typename SynTreeClass > int apply( const SynTreeClass *& input ) const;
+	template< typename SynTreeClass > int applyFree( const SynTreeClass *& input ) const;
+
+	template< typename node_t, enum Node::ref_type ref_t >
+	int apply( ptr_base< node_t, ref_t > & input ) const {
+		const node_t * p = input.get();
+		int ret = apply(p);
+		input = p;
+		return ret;
+	}
+
+	template< typename node_t, enum Node::ref_type ref_t >
+	int applyFree( ptr_base< node_t, ref_t > & input ) const {
+		const node_t * p = input.get();
+		int ret = applyFree(p);
+		input = p;
+		return ret;
+	}
 
 	void add( std::string formalType, const Type *actualType );
@@ -162,9 +179,8 @@
 
 template< typename SynTreeClass >
-int TypeSubstitution::apply( SynTreeClass *&input ) const {
+int TypeSubstitution::apply( const SynTreeClass *& input ) const {
 	assert( input );
 	Pass<Substituter> sub( *this, false );
-	input = dynamic_cast< SynTreeClass * >( input->acceptMutator( sub ) );
-	assert( input );
+	input = strict_dynamic_cast< const SynTreeClass * >( input->accept( sub ) );
 ///	std::cerr << "substitution result is: ";
 ///	newType->print( std::cerr );
@@ -174,9 +190,8 @@
 
 template< typename SynTreeClass >
-int TypeSubstitution::applyFree( SynTreeClass *&input ) const {
+int TypeSubstitution::applyFree( const SynTreeClass *& input ) const {
 	assert( input );
 	Pass<Substituter> sub( *this, true );
-	input = dynamic_cast< SynTreeClass * >( input->acceptMutator( sub ) );
-	assert( input );
+	input = strict_dynamic_cast< const SynTreeClass * >( input->accept( sub ) );
 ///	std::cerr << "substitution result is: ";
 ///	newType->print( std::cerr );
Index: src/AST/porting.md
===================================================================
--- src/AST/porting.md	(revision 5902625312b0f730ef5d4cca6254a8283c7cff36)
+++ src/AST/porting.md	(revision f23de79d3598c65a1d98ae2a637ac911add4fd81)
@@ -38,7 +38,5 @@
 
 `N->print(std::ostream&)` is a visitor now, port these methods to `ast::Print` class
-* **TODO** write this visitor
-* **TODO** write `std::ostream& operator<< ( std::ostream& out, const Node* node )` in `Node.hpp` in terms of `ast::Print`
-* `Declaration::printShort` should also be integrated
+* **TODO** `Declaration::printShort` should also be integrated
 
 `clone` is private to `Node` now
@@ -208,7 +206,4 @@
 
 `CompoundStmt`
-* **TODO** port copy operator
-  * Needs to be an almost-shallow clone, where the declarations are cloned only if needed
-  * **TODO** port `DeclReplacer`
 * Still a `std::list` for children, rather than `std::vector`
   * allows more-efficient splicing for purposes of later code generation
@@ -229,5 +224,5 @@
   * `getAggr()` => `aggr()`
     * also now returns `const AggregateDecl *`
-* `genericSubstitution()` moved to own visitor in `AST/GenericSubstitution.hpp` **TODO** write
+* `genericSubstitution()` moved to own visitor in `AST/GenericSubstitution.hpp`
 
 `BasicType`
