Index: src/AST/Expr.cpp
===================================================================
--- src/AST/Expr.cpp	(revision 37eef7ae48492baf976bf55e5ae1abe5d8779967)
+++ src/AST/Expr.cpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
@@ -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");
+	// take ownership of member type
+	Type * res = result.set_and_mutate( mem->get_type() );
+	// substitute aggregate generic parameters into member type
+	genericSubsitution( aggregate->result ).apply( res );
+	// ensure lvalue and appropriate restrictions from aggregate type
+	res->set_lvalue( true );
+	res->qualifiers |= aggregate->result->qualifiers;
+	// ensure changes propegated back into result
+	result = res;
 }
 
Index: src/AST/GenericSubstitution.cpp
===================================================================
--- src/AST/GenericSubstitution.cpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
+++ src/AST/GenericSubstitution.cpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
@@ -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 * ) {
+			// do nothing; allows substitution from base type
+		}
+
+		void previsit( const ReferenceToType * ty ) {
+			// build substitution from base parameters
+			const AggregateDecl * aggr = ty->aggr();
+			sub = TypeSubstitution{ aggr->params.begin(), aggr->params.end(), ty->params.begin() };
+			visit_children = false;
+		}
+	};
+}
+
+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 d8938622b768abd2937d6a50f68591594aceea55)
+++ src/AST/GenericSubstitution.hpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
@@ -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.hpp
===================================================================
--- src/AST/Node.hpp	(revision 37eef7ae48492baf976bf55e5ae1abe5d8779967)
+++ src/AST/Node.hpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
@@ -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.
Index: src/AST/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision 37eef7ae48492baf976bf55e5ae1abe5d8779967)
+++ src/AST/Pass.hpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
@@ -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/Stmt.cpp
===================================================================
--- src/AST/Stmt.cpp	(revision 37eef7ae48492baf976bf55e5ae1abe5d8779967)
+++ src/AST/Stmt.cpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
@@ -22,4 +22,5 @@
 // --- CompoundStmt
 CompoundStmt::CompoundStmt( const CompoundStmt& o ) : Stmt(o), kids(o.kids) {
+#   warning unimplemented
 	assert(!"implemented");
 }
Index: src/AST/TypeSubstitution.hpp
===================================================================
--- src/AST/TypeSubstitution.hpp	(revision 37eef7ae48492baf976bf55e5ae1abe5d8779967)
+++ src/AST/TypeSubstitution.hpp	(revision d8938622b768abd2937d6a50f68591594aceea55)
@@ -25,5 +25,5 @@
 #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"
@@ -165,6 +165,5 @@
 	assert( input );
 	Pass<Substituter> sub( *this, false );
-	input = dynamic_cast< SynTreeClass * >( input->acceptMutator( sub ) );
-	assert( input );
+	input = strict_dynamic_cast< SynTreeClass * >( input->accept( sub ) );
 ///	std::cerr << "substitution result is: ";
 ///	newType->print( std::cerr );
@@ -177,6 +176,5 @@
 	assert( input );
 	Pass<Substituter> sub( *this, true );
-	input = dynamic_cast< SynTreeClass * >( input->acceptMutator( sub ) );
-	assert( input );
+	input = strict_dynamic_cast< SynTreeClass * >( input->accept( sub ) );
 ///	std::cerr << "substitution result is: ";
 ///	newType->print( std::cerr );
Index: src/AST/module.mk
===================================================================
--- src/AST/module.mk	(revision 37eef7ae48492baf976bf55e5ae1abe5d8779967)
+++ src/AST/module.mk	(revision d8938622b768abd2937d6a50f68591594aceea55)
@@ -21,4 +21,5 @@
 	AST/DeclReplacer.cpp \
 	AST/Expr.cpp \
+	AST/GenericSubstitution.cpp \
 	AST/Init.cpp \
 	AST/LinkageSpec.cpp \
Index: src/AST/porting.md
===================================================================
--- src/AST/porting.md	(revision 37eef7ae48492baf976bf55e5ae1abe5d8779967)
+++ src/AST/porting.md	(revision d8938622b768abd2937d6a50f68591594aceea55)
@@ -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`
Index: src/Makefile.in
===================================================================
--- src/Makefile.in	(revision 37eef7ae48492baf976bf55e5ae1abe5d8779967)
+++ src/Makefile.in	(revision d8938622b768abd2937d6a50f68591594aceea55)
@@ -167,8 +167,8 @@
 am__objects_1 = AST/Attribute.$(OBJEXT) AST/Convert.$(OBJEXT) \
 	AST/Decl.$(OBJEXT) AST/DeclReplacer.$(OBJEXT) \
-	AST/Expr.$(OBJEXT) AST/Init.$(OBJEXT) \
-	AST/LinkageSpec.$(OBJEXT) AST/Node.$(OBJEXT) \
-	AST/Print.$(OBJEXT) AST/Stmt.$(OBJEXT) AST/Type.$(OBJEXT) \
-	AST/TypeSubstitution.$(OBJEXT)
+	AST/Expr.$(OBJEXT) AST/GenericSubstitution.$(OBJEXT) \
+	AST/Init.$(OBJEXT) AST/LinkageSpec.$(OBJEXT) \
+	AST/Node.$(OBJEXT) AST/Print.$(OBJEXT) AST/Stmt.$(OBJEXT) \
+	AST/Type.$(OBJEXT) AST/TypeSubstitution.$(OBJEXT)
 am__objects_2 = CodeGen/CodeGenerator.$(OBJEXT) \
 	CodeGen/FixMain.$(OBJEXT) CodeGen/GenType.$(OBJEXT) \
@@ -574,4 +574,5 @@
 	AST/DeclReplacer.cpp \
 	AST/Expr.cpp \
+	AST/GenericSubstitution.cpp \
 	AST/Init.cpp \
 	AST/LinkageSpec.cpp \
@@ -738,4 +739,6 @@
 	AST/$(DEPDIR)/$(am__dirstamp)
 AST/Expr.$(OBJEXT): AST/$(am__dirstamp) AST/$(DEPDIR)/$(am__dirstamp)
+AST/GenericSubstitution.$(OBJEXT): AST/$(am__dirstamp) \
+	AST/$(DEPDIR)/$(am__dirstamp)
 AST/Init.$(OBJEXT): AST/$(am__dirstamp) AST/$(DEPDIR)/$(am__dirstamp)
 AST/LinkageSpec.$(OBJEXT): AST/$(am__dirstamp) \
@@ -1171,4 +1174,5 @@
 @AMDEP_TRUE@@am__include@ @am__quote@AST/$(DEPDIR)/DeclReplacer.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@AST/$(DEPDIR)/Expr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@AST/$(DEPDIR)/GenericSubstitution.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@AST/$(DEPDIR)/Init.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@AST/$(DEPDIR)/LinkageSpec.Po@am__quote@
