Index: src/AST/Decl.cpp
===================================================================
--- src/AST/Decl.cpp	(revision 92f5279765056ffb48eeb946fe0fbe43bd7d563b)
+++ src/AST/Decl.cpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -52,5 +52,7 @@
 
 const Type * FunctionDecl::get_type() const { return type.get(); }
-void FunctionDecl::set_type(Type * t) { type = strict_dynamic_cast< FunctionType* >( t ); }
+void FunctionDecl::set_type( const Type * t ) {
+	type = strict_dynamic_cast< const FunctionType * >( t );
+}
 
 // --- TypeDecl
Index: src/AST/Decl.hpp
===================================================================
--- src/AST/Decl.hpp	(revision 92f5279765056ffb48eeb946fe0fbe43bd7d563b)
+++ src/AST/Decl.hpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -87,5 +87,5 @@
 	virtual const Type * get_type() const = 0;
 	/// Set type of this declaration. May be verified by subclass
-	virtual void set_type(Type *) = 0;
+	virtual void set_type( const Type * ) = 0;
 
 	const DeclWithType * accept( Visitor & v ) const override = 0;
@@ -110,5 +110,5 @@
 
 	const Type* get_type() const override { return type; }
-	void set_type( Type * ty ) override { type = ty; }
+	void set_type( const Type * ty ) override { type = ty; }
 
 	const DeclWithType * accept( Visitor& v ) const override { return v.visit( this ); }
@@ -132,5 +132,5 @@
 
 	const Type * get_type() const override;
-	void set_type(Type * t) override;
+	void set_type( const Type * t ) override;
 
 	bool has_body() const { return stmts; }
@@ -149,6 +149,7 @@
 	std::vector<ptr<DeclWithType>> assertions;
 
-	NamedTypeDecl( const CodeLocation& loc, const std::string& name, Storage::Classes storage,
-		Type* b, Linkage::Spec spec = Linkage::Cforall )
+	NamedTypeDecl( 
+		const CodeLocation & loc, const std::string & name, Storage::Classes storage,
+		const Type * b, Linkage::Spec spec = Linkage::Cforall )
 	: Decl( loc, name, storage, spec ), base( b ), params(), assertions() {}
 
@@ -185,6 +186,7 @@
 	};
 
-	TypeDecl( const CodeLocation& loc, const std::string& name, Storage::Classes storage, Type* b,
-		TypeVar::Kind k, bool s, Type* i = nullptr )
+	TypeDecl( 
+		const CodeLocation & loc, const std::string & name, Storage::Classes storage, 
+		const Type * b, TypeVar::Kind k, bool s, const Type * i = nullptr )
 	: NamedTypeDecl( loc, name, storage, b ), kind( k ), sized( k == TypeVar::Ttype || s ),
 	  init( i ) {}
@@ -201,5 +203,4 @@
 	TypeDecl * clone() const override { return new TypeDecl{ *this }; }
 	MUTATE_FRIEND
-	friend class ParameterizedType;  // to allow deep clones
 };
 
Index: src/AST/ForallSubstitutionTable.cpp
===================================================================
--- src/AST/ForallSubstitutionTable.cpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
+++ src/AST/ForallSubstitutionTable.cpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -0,0 +1,54 @@
+//
+// 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.
+//
+// ForallSubstitutionTable.cpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Thu Jun 27 14:00:00 2019
+// Last Modified By : Aaron B. Moss
+// Last Modified On : Thu Jun 27 14:00:00 2019
+// Update Count     : 1
+//
+
+#include "ForallSubstitutionTable.hpp"
+
+#include <cassert>
+#include <vector>
+
+#include "Decl.hpp"
+#include "Node.hpp"
+#include "Type.hpp"
+#include "Visitor.hpp"
+
+namespace ast {
+
+std::vector< ptr< TypeDecl > > ForallSubstitutionTable::clone( 
+	const std::vector< ptr< TypeDecl > > & forall, Visitor & v 
+) {
+	std::vector< ptr< TypeDecl > > new_forall;
+	new_forall.reserve( forall.size() );
+
+	for ( const ast::TypeDecl * d : forall ) {
+		// create cloned type decl and insert into substitution map before further mutation
+		auto new_d = new ast::TypeDecl{
+			d->location, d->name, d->storage, d->base, d->kind, d->sized, d->init };
+		decls.insert( d, new_d );
+		// perform other mutations and add to output
+		auto newer_d = v.visit( new_d );
+		assert( new_d == newer_d && "Newly cloned TypeDecl must retain identity" );
+		new_forall.emplace_back( new_d );
+	}
+
+	return new_forall;
+}
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/AST/ForallSubstitutionTable.hpp
===================================================================
--- src/AST/ForallSubstitutionTable.hpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
+++ src/AST/ForallSubstitutionTable.hpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -0,0 +1,57 @@
+//
+// 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.
+//
+// ForallSubstitutionTable.hpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Thu Jun 27 14:00:00 2019
+// Last Modified By : Aaron B. Moss
+// Last Modified On : Thu Jun 27 14:00:00 2019
+// Update Count     : 1
+//
+
+#pragma once
+
+#include <vector>
+
+#include "Node.hpp"  // for ptr
+#include "Common/ScopedMap.h"
+
+namespace ast {
+
+class TypeDecl;
+class Visitor;
+
+/// Wrapper for TypeDecl substitution table
+class ForallSubstitutionTable {
+	ScopedMap< const TypeDecl *, const TypeDecl * > decls;
+
+public:
+	/// Replaces given declaration with value in the table, if present, otherwise returns argument
+	const TypeDecl * replace( const TypeDecl * d ) {
+		auto it = decls.find( d );
+		if ( it != decls.end() ) return it->second;
+		return d;
+	}
+
+	/// Builds a new forall list mutated according to the given visitor
+	std::vector< ptr< TypeDecl > > clone( 
+		const std::vector< ptr< TypeDecl > > & forall, Visitor & v );
+
+	/// Introduces a new lexical scope
+	void beginScope() { decls.beginScope(); }
+
+	/// Concludes a lexical scope
+	void endScope() { decls.endScope(); }
+};
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/AST/ForallSubstitutor.hpp
===================================================================
--- src/AST/ForallSubstitutor.hpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
+++ src/AST/ForallSubstitutor.hpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -0,0 +1,58 @@
+//
+// 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.
+//
+// ForallSubstitutor.hpp --
+//
+// Author           : Aaron B. Moss
+// Created On       : Wed Jun 26 15:00:00 2019
+// Last Modified By : Aaron B. Moss
+// Last Modified On : Wed Jun 26 15:00:00 2019
+// Update Count     : 1
+//
+
+#include "Pass.hpp"
+
+namespace ast {
+
+class Expr;
+
+/// Visitor that correctly substitutes TypeDecl while maintaining TypeInstType bindings.
+/// Also has some convenience methods to mutate fields.
+struct ForallSubstitutor : public WithForallSubstitutor, public WithVisitorRef<ForallSubstitutor> {
+	/// Substitute TypeInstType base type
+	readonly< TypeDecl > operator() ( const readonly< TypeDecl > & o ) {
+		return subs.replace( o );
+	}
+	
+	/// Make new forall-list clone
+	ParameterizedType::ForallList operator() ( const ParameterizedType::ForallList & o ) {
+		return subs.clone( o, *visitor );
+	}
+
+	/// Substitute parameter/return type
+	std::vector< ptr< DeclWithType > > operator() ( const std::vector< ptr< DeclWithType > > & o ) {
+		std::vector< ptr< DeclWithType > > n;
+		n.reserve( o.size() );
+		for ( const DeclWithType * d : o ) { n.emplace_back( d->accept( *visitor ) ); }
+		return n;
+	}
+
+	/// Substitute type parameter list
+	std::vector< ptr< Expr > > operator() ( const std::vector< ptr< Expr > > & o ) {
+		std::vector< ptr< Expr > > n;
+		n.reserve( o.size() );
+		for ( const Expr * d : o ) { n.emplace_back( d->accept( *visitor ) ); }
+		return n;
+	}
+};
+
+} // namespace ast
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/AST/Node.cpp
===================================================================
--- src/AST/Node.cpp	(revision 92f5279765056ffb48eeb946fe0fbe43bd7d563b)
+++ src/AST/Node.cpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -56,5 +56,7 @@
 
 template< typename node_t, enum ast::Node::ref_type ref_t >
-void ast::ptr_base<node_t, ref_t>::_check() const { if(node) assert(node->was_ever_strong == false || node->strong_count > 0); }
+void ast::ptr_base<node_t, ref_t>::_check() const { 
+	// if(node) assert(node->was_ever_strong == false || node->strong_count > 0);
+}
 
 template< typename node_t, enum ast::Node::ref_type ref_t >
Index: src/AST/Node.hpp
===================================================================
--- src/AST/Node.hpp	(revision 92f5279765056ffb48eeb946fe0fbe43bd7d563b)
+++ src/AST/Node.hpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -125,4 +125,13 @@
 }
 
+/// Mutate an entire indexed collection by cloning to accepted value
+template<typename node_t, typename parent_t, typename coll_t>
+const node_t * mutate_each( const node_t * node, coll_t parent_t::* field, Visitor & v ) {
+	for ( unsigned i = 0; i < (node->*field).size(); ++i ) {
+		node = mutate_field_index( node, field, i, (node->*field)[i]->accept( v ) );
+	}
+	return node;
+}
+
 std::ostream& operator<< ( std::ostream& out, const Node * node );
 
Index: src/AST/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision 92f5279765056ffb48eeb946fe0fbe43bd7d563b)
+++ src/AST/Pass.hpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -35,4 +35,6 @@
 #include "AST/SymbolTable.hpp"
 
+#include "AST/ForallSubstitutionTable.hpp"
+
 // Private prelude header, needed for some of the magic tricks this class pulls off
 #include "AST/Pass.proto.hpp"
@@ -46,20 +48,21 @@
 //
 // Several additional features are available through inheritance
-// | WithTypeSubstitution - provides polymorphic const TypeSubstitution * env for the
-//                          current expression
-// | WithStmtsToAdd       - provides the ability to insert statements before or after the current
-//                          statement by adding new statements into stmtsToAddBefore or
-//                          stmtsToAddAfter respectively.
-// | WithDeclsToAdd       - provides the ability to insert declarations before or after the current
-//                          declarations by adding new DeclStmt into declsToAddBefore or
-//                          declsToAddAfter respectively.
-// | WithShortCircuiting  - provides the ability to skip visiting child nodes; set visit_children
-//                          to false in pre{visit,visit} to skip visiting children
-// | WithGuards           - provides the ability to save/restore data like a LIFO stack; to save,
-//                          call GuardValue with the variable to save, the variable will
-//                          automatically be restored to its previous value after the corresponding
-//                          postvisit/postmutate teminates.
-// | WithVisitorRef       - provides an pointer to the templated visitor wrapper
-// | WithSymbolTable      - provides symbol table functionality
+// | WithTypeSubstitution  - provides polymorphic const TypeSubstitution * env for the
+//                           current expression
+// | WithStmtsToAdd        - provides the ability to insert statements before or after the current
+//                           statement by adding new statements into stmtsToAddBefore or
+//                           stmtsToAddAfter respectively.
+// | WithDeclsToAdd        - provides the ability to insert declarations before or after the
+//                           current declarations by adding new DeclStmt into declsToAddBefore or
+//                           declsToAddAfter respectively.
+// | WithShortCircuiting   - provides the ability to skip visiting child nodes; set visit_children
+//                           to false in pre{visit,visit} to skip visiting children
+// | WithGuards            - provides the ability to save/restore data like a LIFO stack; to save,
+//                           call GuardValue with the variable to save, the variable will
+//                           automatically be restored to its previous value after the
+//                           corresponding postvisit/postmutate teminates.
+// | WithVisitorRef        - provides an pointer to the templated visitor wrapper
+// | WithSymbolTable       - provides symbol table functionality
+// | WithForallSubstitutor - maintains links between TypeInstType and TypeDecl under mutation
 //-------------------------------------------------------------------------------------------------
 template< typename pass_t >
@@ -201,4 +204,8 @@
 	container_t< ptr<node_t> > call_accept( const container_t< ptr<node_t> > & container );
 
+	/// Mutate forall-list, accounting for presence of type substitution map
+	template<typename node_t>
+	void mutate_forall( const node_t *& );
+
 public:
 	/// Logic to call the accept and mutate the parent if needed, delegates call to accept
@@ -221,4 +228,13 @@
 	};
 
+	/// Internal RAII guard for forall substitutions
+	struct guard_forall_subs {
+		guard_forall_subs( Pass<pass_t> & pass, const ParameterizedType * type )
+		: pass( pass ), type( type ) { __pass::forall::enter(pass.pass, 0, type ); }
+		~guard_forall_subs()         { __pass::forall::leave(pass.pass, 0, type ); }
+		Pass<pass_t> & pass;
+		const ParameterizedType * type;
+	};
+
 private:
 	bool inFunction = false;
@@ -313,4 +329,10 @@
 	SymbolTable symtab;
 };
+
+/// Use when the templated visitor needs to keep TypeInstType instances properly linked to TypeDecl
+struct WithForallSubstitutor {
+	ForallSubstitutionTable subs;
+};
+
 }
 
Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision 92f5279765056ffb48eeb946fe0fbe43bd7d563b)
+++ src/AST/Pass.impl.hpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -127,8 +127,7 @@
 			, decltype( node->accept(*this) )
 		>::type
-
 	{
 		__pedantic_pass_assert( __visit_children() );
-		__pedantic_pass_assert( expr );
+		__pedantic_pass_assert( node );
 
 		static_assert( !std::is_base_of<ast::Expr, node_t>::value, "ERROR");
@@ -323,4 +322,20 @@
 	}
 
+
+	template< typename pass_t >
+	template< typename node_t >
+	void ast::Pass< pass_t >::mutate_forall( const node_t *& node ) {
+		if ( auto subs = __pass::forall::subs( pass, 0 ) ) {
+			// tracking TypeDecl substitution, full clone
+			if ( node->forall.empty() ) return;
+
+			node_t * mut = mutate( node );
+			mut->forall = subs->clone( node->forall, *this );
+			node = mut;
+		} else {
+			// not tracking TypeDecl substitution, just mutate
+			maybe_accept( node, &node_t::forall );
+		}
+	}
 }
 
@@ -1667,9 +1682,10 @@
 	VISIT_START( node );
 
-	VISIT(
-		maybe_accept( node, &FunctionType::forall  );
+	VISIT({
+		guard_forall_subs forall_guard { *this, node };
+		mutate_forall( node );
 		maybe_accept( node, &FunctionType::returns );
 		maybe_accept( node, &FunctionType::params  );
-	)
+	})
 
 	VISIT_END( Type, node );
@@ -1686,5 +1702,6 @@
 	VISIT({
 		guard_symtab guard { *this };
-		maybe_accept( node, &StructInstType::forall );
+		guard_forall_subs forall_guard { *this, node };
+		mutate_forall( node );
 		maybe_accept( node, &StructInstType::params );
 	})
@@ -1699,11 +1716,12 @@
 	VISIT_START( node );
 
-	__pass::symtab::addStruct( pass, 0, node->name );
-
-	{
+	__pass::symtab::addUnion( pass, 0, node->name );
+
+	VISIT({
 		guard_symtab guard { *this };
-		maybe_accept( node, &UnionInstType::forall );
+		guard_forall_subs forall_guard { *this, node };
+		mutate_forall( node );
 		maybe_accept( node, &UnionInstType::params );
-	}
+	})
 
 	VISIT_END( Type, node );
@@ -1716,8 +1734,9 @@
 	VISIT_START( node );
 
-	VISIT(
-		maybe_accept( node, &EnumInstType::forall );
+	VISIT({
+		guard_forall_subs forall_guard { *this, node };
+		mutate_forall( node );
 		maybe_accept( node, &EnumInstType::params );
-	)
+	})
 
 	VISIT_END( Type, node );
@@ -1730,8 +1749,9 @@
 	VISIT_START( node );
 
-	VISIT(
-		maybe_accept( node, &TraitInstType::forall );
+	VISIT({
+		guard_forall_subs forall_guard { *this, node };
+		mutate_forall( node );
 		maybe_accept( node, &TraitInstType::params );
-	)
+	})
 
 	VISIT_END( Type, node );
@@ -1745,6 +1765,11 @@
 
 	VISIT(
-		maybe_accept( node, &TypeInstType::forall );
-		maybe_accept( node, &TypeInstType::params );
+		{
+			guard_forall_subs forall_guard { *this, node };
+			mutate_forall( node );
+			maybe_accept( node, &TypeInstType::params );
+		}
+		// ensure that base re-bound if doing substitution
+		__pass::forall::replace( pass, 0, node );
 	)
 
Index: src/AST/Pass.proto.hpp
===================================================================
--- src/AST/Pass.proto.hpp	(revision 92f5279765056ffb48eeb946fe0fbe43bd7d563b)
+++ src/AST/Pass.proto.hpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -263,7 +263,7 @@
 		template<typename pass_t>
 		static inline void leave( pass_t &, long ) {}
-	};
-
-	// Finally certain pass desire an up to date symbol table automatically
+	} // namespace scope
+
+	// Certain passes desire an up to date symbol table automatically
 	// detect the presence of a member name `symtab` and call all the members appropriately
 	namespace symtab {
@@ -356,5 +356,49 @@
 		#undef SYMTAB_FUNC1
 		#undef SYMTAB_FUNC2
-	};
-};
-};
+	} // namespace symtab
+
+	// Some passes need to mutate TypeDecl and properly update their pointing TypeInstType.
+	// Detect the presence of a member name `subs` and call all members appropriately
+	namespace forall {
+		// Some simple scoping rules
+		template<typename pass_t>
+		static inline auto enter( pass_t & pass, int, const ast::ParameterizedType * type ) 
+		-> decltype( pass.subs, void() ) {
+			if ( ! type->forall.empty() ) pass.subs.beginScope();
+		}
+
+		template<typename pass_t>
+		static inline auto enter( pass_t &, long, const ast::ParameterizedType * ) {}
+
+		template<typename pass_t>
+		static inline auto leave( pass_t & pass, int, const ast::ParameterizedType * type ) 
+		-> decltype( pass.subs, void() ) {
+			if ( ! type->forall.empty() ) { pass.subs.endScope(); }
+		}
+
+		template<typename pass_t>
+		static inline auto leave( pass_t &, long, const ast::ParameterizedType * ) {}
+
+		// Get the substitution table, if present
+		template<typename pass_t>
+		static inline auto subs( pass_t & pass, int ) -> decltype( &pass.subs ) {
+			return &pass.subs;
+		}
+		
+		template<typename pass_t>
+		static inline ast::ForallSubstitutionTable * subs( pass_t &, long ) { return nullptr; }
+
+		// Replaces a TypeInstType's base TypeDecl according to the table
+		template<typename pass_t>
+		static inline auto replace( pass_t & pass, int, const ast::TypeInstType *& inst ) 
+		-> decltype( pass.subs, void() ) {
+			inst = ast::mutate_field( 
+				inst, &ast::TypeInstType::base, pass.subs.replace( inst->base ) );
+		}
+
+		template<typename pass_t>
+		static inline auto replace( pass_t &, long, const ast::TypeInstType *& ) {}
+
+	} // namespace forall
+} // namespace __pass
+} // namespace ast
Index: src/AST/Type.cpp
===================================================================
--- src/AST/Type.cpp	(revision 92f5279765056ffb48eeb946fe0fbe43bd7d563b)
+++ src/AST/Type.cpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -21,5 +21,7 @@
 
 #include "Decl.hpp"
+#include "ForallSubstitutor.hpp" // for substituteForall
 #include "Init.hpp"
+#include "Common/utility.h"      // for copy, move
 #include "InitTweak/InitTweak.h" // for getPointerBase
 #include "Tuples/Tuples.h"       // for isTtype
@@ -91,5 +93,22 @@
 );
 
+// --- ParameterizedType
+
+void ParameterizedType::initWithSub( 
+	const ParameterizedType & o, Pass< ForallSubstitutor > & sub 
+) {
+	forall = sub.pass( o.forall );
+}
+
 // --- FunctionType
+
+FunctionType::FunctionType( const FunctionType & o )
+: ParameterizedType( o.qualifiers, copy( o.attributes ) ), returns(), params(), 
+  isVarArgs( o.isVarArgs ) {
+	Pass< ForallSubstitutor > sub;
+	initWithSub( o, sub );           // initialize substitution map
+	returns = sub.pass( o.returns ); // apply to return and parameter types
+	params = sub.pass( o.params );
+}
 
 namespace {
@@ -107,4 +126,17 @@
 
 // --- ReferenceToType
+
+void ReferenceToType::initWithSub( const ReferenceToType & o, Pass< ForallSubstitutor > & sub ) {
+	ParameterizedType::initWithSub( o, sub ); // initialize substitution
+	params = sub.pass( o.params );            // apply to parameters
+}
+
+ReferenceToType::ReferenceToType( const ReferenceToType & o )
+: ParameterizedType( o.qualifiers, copy( o.attributes ) ), params(), name( o.name ), 
+  hoistType( o.hoistType ) {
+	Pass< ForallSubstitutor > sub;
+	initWithSub( o, sub );
+}
+
 std::vector<readonly<Decl>> ReferenceToType::lookup( const std::string& name ) const {
 	assertf( aggr(), "Must have aggregate to perform lookup" );
@@ -119,7 +151,7 @@
 // --- StructInstType
 
-StructInstType::StructInstType( const StructDecl * b, CV::Qualifiers q,
-	std::vector<ptr<Attribute>>&& as )
-: ReferenceToType( b->name, q, std::move(as) ), base( b ) {}
+StructInstType::StructInstType( 
+	const StructDecl * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as )
+: ReferenceToType( b->name, q, move(as) ), base( b ) {}
 
 bool StructInstType::isComplete() const { return base ? base->body : false; }
@@ -127,7 +159,7 @@
 // --- UnionInstType
 
-UnionInstType::UnionInstType( const UnionDecl * b, CV::Qualifiers q,
-	std::vector<ptr<Attribute>>&& as )
-: ReferenceToType( b->name, q, std::move(as) ), base( b ) {}
+UnionInstType::UnionInstType( 
+	const UnionDecl * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as )
+: ReferenceToType( b->name, q, move(as) ), base( b ) {}
 
 bool UnionInstType::isComplete() const { return base ? base->body : false; }
@@ -135,7 +167,7 @@
 // --- EnumInstType
 
-EnumInstType::EnumInstType( const EnumDecl * b, CV::Qualifiers q,
-	std::vector<ptr<Attribute>>&& as )
-: ReferenceToType( b->name, q, std::move(as) ), base( b ) {}
+EnumInstType::EnumInstType( 
+	const EnumDecl * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as )
+: ReferenceToType( b->name, q, move(as) ), base( b ) {}
 
 bool EnumInstType::isComplete() const { return base ? base->body : false; }
@@ -143,9 +175,16 @@
 // --- TraitInstType
 
-TraitInstType::TraitInstType( const TraitDecl * b, CV::Qualifiers q,
-	std::vector<ptr<Attribute>>&& as )
-: ReferenceToType( b->name, q, std::move(as) ), base( b ) {}
+TraitInstType::TraitInstType( 
+	const TraitDecl * b, CV::Qualifiers q, std::vector<ptr<Attribute>>&& as )
+: ReferenceToType( b->name, q, move(as) ), base( b ) {}
 
 // --- TypeInstType
+
+TypeInstType::TypeInstType( const TypeInstType & o ) 
+: ReferenceToType( o.name, o.qualifiers, copy( o.attributes ) ), base(), kind( o.kind ) {
+	Pass< ForallSubstitutor > sub;
+	initWithSub( o, sub );      // initialize substitution
+	base = sub.pass( o.base );  // apply to base type
+}
 
 void TypeInstType::set_base( const TypeDecl * b ) {
@@ -159,5 +198,5 @@
 
 TupleType::TupleType( std::vector<ptr<Type>> && ts, CV::Qualifiers q )
-: Type( q ), types( std::move(ts) ), members() {
+: Type( q ), types( move(ts) ), members() {
 	// This constructor is awkward. `TupleType` needs to contain objects so that members can be
 	// named, but members without initializer nodes end up getting constructors, which breaks
Index: src/AST/Type.hpp
===================================================================
--- src/AST/Type.hpp	(revision 92f5279765056ffb48eeb946fe0fbe43bd7d563b)
+++ src/AST/Type.hpp	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -33,4 +33,7 @@
 
 namespace ast {
+
+template< typename T > class Pass;
+class ForallSubstitutor;
 
 class Type : public Node {
@@ -266,4 +269,7 @@
 /// Base type for potentially forall-qualified types
 class ParameterizedType : public Type {
+protected:
+	/// initializes forall with substitutor
+	void initWithSub( const ParameterizedType & o, Pass< ForallSubstitutor > & sub );
 public:
 	using ForallList = std::vector<ptr<TypeDecl>>;
@@ -278,9 +284,6 @@
 	: Type(q, std::move(as)), forall() {}
 
-	ParameterizedType( const ParameterizedType & o ) : Type( o ), forall() {
-		// one-level deep clone to avoid weak-reference errors
-		forall.reserve( o.forall.size() );
-		for ( const TypeDecl * d : o.forall ) { forall.emplace_back( d->clone() ); }
-	}
+	// enforce use of ForallSubstitutor to copy parameterized type
+	ParameterizedType( const ParameterizedType & ) = delete;
 
 	ParameterizedType( ParameterizedType && ) = default;
@@ -312,4 +315,6 @@
 	: ParameterizedType(q), returns(), params(), isVarArgs(va) {}
 
+	FunctionType( const FunctionType & o );
+
 	/// true if either the parameters or return values contain a tttype
 	bool isTtype() const;
@@ -325,4 +330,7 @@
 /// base class for types that refer to types declared elsewhere (aggregates and typedefs)
 class ReferenceToType : public ParameterizedType {
+protected:
+	/// Initializes forall and parameters based on substitutor
+	void initWithSub( const ReferenceToType & o, Pass< ForallSubstitutor > & sub );
 public:
 	std::vector<ptr<Expr>> params;
@@ -330,7 +338,9 @@
 	bool hoistType = false;
 
-	ReferenceToType( const std::string& n, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} )
+	ReferenceToType( 
+		const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
 	: ParameterizedType(q, std::move(as)), params(), name(n) {}
+
+	ReferenceToType( const ReferenceToType & o );
 
 	/// Gets aggregate declaration this type refers to
@@ -349,9 +359,10 @@
 	readonly<StructDecl> base;
 
-	StructInstType( const std::string& n, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} )
+	StructInstType( 
+		const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
 	: ReferenceToType( n, q, std::move(as) ), base() {}
-	StructInstType( const StructDecl * b, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} );
+
+	StructInstType( 
+		const StructDecl * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
 
 	bool isComplete() const override;
@@ -370,9 +381,10 @@
 	readonly<UnionDecl> base;
 
-	UnionInstType( const std::string& n, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} )
+	UnionInstType( 
+		const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
 	: ReferenceToType( n, q, std::move(as) ), base() {}
-	UnionInstType( const UnionDecl * b, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} );
+
+	UnionInstType( 
+		const UnionDecl * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
 
 	bool isComplete() const override;
@@ -391,9 +403,10 @@
 	readonly<EnumDecl> base;
 
-	EnumInstType( const std::string& n, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} )
+	EnumInstType( 
+		const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
 	: ReferenceToType( n, q, std::move(as) ), base() {}
-	EnumInstType( const EnumDecl * b, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} );
+
+	EnumInstType( 
+		const EnumDecl * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
 
 	bool isComplete() const override;
@@ -412,9 +425,10 @@
 	readonly<TraitDecl> base;
 
-	TraitInstType( const std::string& n, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} )
+	TraitInstType( 
+		const std::string& n, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} )
 	: ReferenceToType( n, q, std::move(as) ), base() {}
-	TraitInstType( const TraitDecl * b, CV::Qualifiers q = {},
-		std::vector<ptr<Attribute>> && as = {} );
+	
+	TraitInstType( 
+		const TraitDecl * b, CV::Qualifiers q = {}, std::vector<ptr<Attribute>> && as = {} );
 
 	// not meaningful for TraitInstType
@@ -435,10 +449,15 @@
 	TypeVar::Kind kind;
 
-	TypeInstType( const std::string& n, const TypeDecl * b, CV::Qualifiers q = {},
+	TypeInstType( 
+		const std::string& n, const TypeDecl * b, CV::Qualifiers q = {},
 		std::vector<ptr<Attribute>> && as = {} )
 	: ReferenceToType( n, q, std::move(as) ), base( b ), kind( b->kind ) {}
-	TypeInstType( const std::string& n, TypeVar::Kind k, CV::Qualifiers q = {},
+	
+	TypeInstType( 
+		const std::string& n, TypeVar::Kind k, CV::Qualifiers q = {},
 		std::vector<ptr<Attribute>> && as = {} )
 	: ReferenceToType( n, q, std::move(as) ), base(), kind( k ) {}
+
+	TypeInstType( const TypeInstType & o );
 
 	/// sets `base`, updating `kind` correctly
Index: src/AST/module.mk
===================================================================
--- src/AST/module.mk	(revision 92f5279765056ffb48eeb946fe0fbe43bd7d563b)
+++ src/AST/module.mk	(revision e0e9a0be9170b7c4033834533d5ce2734166eb97)
@@ -22,4 +22,5 @@
 	AST/DeclReplacer.cpp \
 	AST/Expr.cpp \
+	AST/ForallSubstitutionTable.cpp \
 	AST/GenericSubstitution.cpp \
 	AST/Init.cpp \
