Index: src/Common/PassVisitor.h
===================================================================
--- src/Common/PassVisitor.h	(revision d24d4e166c6a6516f309efbac59c59da18c7169f)
+++ src/Common/PassVisitor.h	(revision 2a7b3ca400fe85559a7ebbd5d3b09859d5dfc4da)
@@ -12,4 +12,5 @@
 #include "SynTree/Expression.h"
 #include "SynTree/Constant.h"
+#include "SynTree/TypeSubstitution.h"
 
 #include "PassVisitor.proto.h"
Index: src/Common/PassVisitor.impl.h
===================================================================
--- src/Common/PassVisitor.impl.h	(revision d24d4e166c6a6516f309efbac59c59da18c7169f)
+++ src/Common/PassVisitor.impl.h	(revision 2a7b3ca400fe85559a7ebbd5d3b09859d5dfc4da)
@@ -105,4 +105,10 @@
 	SemanticError errors;
 
+	// don't want statements from outer CompoundStmts to be added to this CompoundStmt
+	ValueGuardPtr< StmtList_t > oldBeforeStmts( get_beforeStmts() );
+	ValueGuardPtr< StmtList_t > oldAfterStmts ( get_afterStmts () );
+	ValueGuardPtr< DeclList_t > oldBeforeDecls( get_beforeDecls() );
+	ValueGuardPtr< DeclList_t > oldAfterDecls ( get_afterDecls () );
+
 	StmtList_t* beforeStmts = get_beforeStmts();
 	StmtList_t* afterStmts  = get_afterStmts();
@@ -565,4 +571,7 @@
 	VISIT_START( node );
 
+	// maybeAccept( node->get_env(), *this );
+	maybeAccept( node->get_result(), *this );
+
 	for ( auto expr : node->get_args() ) {
 		visitExpression( expr );
@@ -575,4 +584,7 @@
 Expression * PassVisitor< pass_type >::mutate( UntypedExpr * node ) {
 	MUTATE_START( node );
+
+	node->set_env( maybeMutate( node->get_env(), *this ) );
+	node->set_result( maybeMutate( node->get_result(), *this ) );
 
 	for ( auto& expr : node->get_args() ) {
Index: src/Common/PassVisitor.proto.h
===================================================================
--- src/Common/PassVisitor.proto.h	(revision d24d4e166c6a6516f309efbac59c59da18c7169f)
+++ src/Common/PassVisitor.proto.h	(revision 2a7b3ca400fe85559a7ebbd5d3b09859d5dfc4da)
@@ -56,5 +56,5 @@
 // Deep magic (a.k.a template meta programming) to make the templated visitor work
 // Basically the goal is to make 2 previsit_impl
-// 1 - Use when a pass implements a valid previsit. This uses overloading which means the any overload of 
+// 1 - Use when a pass implements a valid previsit. This uses overloading which means the any overload of
 //     'pass.previsit( node )' that compiles will be used for that node for that type
 //     This requires that this option only compile for passes that actually define an appropriate visit.
Index: src/GenPoly/Box.cc
===================================================================
--- src/GenPoly/Box.cc	(revision d24d4e166c6a6516f309efbac59c59da18c7169f)
+++ src/GenPoly/Box.cc	(revision 2a7b3ca400fe85559a7ebbd5d3b09859d5dfc4da)
@@ -504,4 +504,5 @@
 		DeclarationWithType *Pass1::mutate( FunctionDecl *functionDecl ) {
 			if ( functionDecl->get_statements() ) {		// empty routine body ?
+				// std::cerr << "mutating function: " << functionDecl->get_mangleName() << std::endl;
 				doBeginScope();
 				scopeTyVars.beginScope();
@@ -548,4 +549,5 @@
 				retval = oldRetval;
 				doEndScope();
+				// std::cerr << "end function: " << functionDecl->get_mangleName() << std::endl;
 			} // if
 			return functionDecl;
@@ -1116,5 +1118,5 @@
 
 		Expression *Pass1::mutate( ApplicationExpr *appExpr ) {
-			// std::cerr << "mutate appExpr: ";
+			// std::cerr << "mutate appExpr: " << InitTweak::getFunctionName( appExpr ) << std::endl;
 			// for ( TyVarMap::iterator i = scopeTyVars.begin(); i != scopeTyVars.end(); ++i ) {
 			// 	std::cerr << i->first << " ";
@@ -1141,8 +1143,17 @@
 			ReferenceToType *dynRetType = isDynRet( function, exprTyVars );
 
+			// std::cerr << function << std::endl;
+			// std::cerr << "scopeTyVars: ";
+			// printTyVarMap( std::cerr, scopeTyVars );
+			// std::cerr << "exprTyVars: ";
+			// printTyVarMap( std::cerr, exprTyVars );
+			// std::cerr << "env: " << *env << std::endl;
+			// std::cerr << needsAdapter( function, scopeTyVars ) << ! needsAdapter( function, exprTyVars) << std::endl;
+
 			// NOTE: addDynRetParam needs to know the actual (generated) return type so it can make a temp variable, so pass the result type from the appExpr
 			// passTypeVars needs to know the program-text return type (i.e. the distinction between _conc_T30 and T3(int))
 			// concRetType may not be a good name in one or both of these places. A more appropriate name change is welcome.
 			if ( dynRetType ) {
+				// std::cerr << "dynRetType: " << dynRetType << std::endl;
 				Type *concRetType = appExpr->get_result()->isVoid() ? nullptr : appExpr->get_result();
 				ret = addDynRetParam( appExpr, concRetType, arg ); // xxx - used to use dynRetType instead of concRetType
Index: src/GenPoly/InstantiateGeneric.cc
===================================================================
--- src/GenPoly/InstantiateGeneric.cc	(revision d24d4e166c6a6516f309efbac59c59da18c7169f)
+++ src/GenPoly/InstantiateGeneric.cc	(revision 2a7b3ca400fe85559a7ebbd5d3b09859d5dfc4da)
@@ -22,9 +22,12 @@
 #include "InstantiateGeneric.h"
 
-#include "DeclMutator.h"
 #include "GenPoly.h"
 #include "ScopedSet.h"
 #include "ScrubTyVars.h"
-#include "PolyMutator.h"
+
+#include "Common/PassVisitor.h"
+#include "Common/ScopedMap.h"
+#include "Common/UniqueName.h"
+#include "Common/utility.h"
 
 #include "ResolvExpr/typeops.h"
@@ -34,7 +37,7 @@
 #include "SynTree/Type.h"
 
-#include "Common/ScopedMap.h"
-#include "Common/UniqueName.h"
-#include "Common/utility.h"
+
+#include "InitTweak/InitTweak.h"
+
 
 namespace GenPoly {
@@ -153,23 +156,6 @@
 	}
 
-	// collect the environments of each TypeInstType so that type variables can be replaced
-	// xxx - possibly temporary solution. Access to type environments is required in GenericInstantiator, but it needs to be a DeclMutator which does not provide easy access to the type environments.
-	class EnvFinder final : public GenPoly::PolyMutator {
-	public:
-		using GenPoly::PolyMutator::mutate;
-		virtual Type * mutate( TypeInstType * inst ) override {
-			if ( env ) envMap[inst] = env;
-			return inst;
-		}
-
-		// don't want to associate an environment with TypeInstTypes that occur in function types - this may actually only apply to function types belonging to DeclarationWithTypes (or even just FunctionDecl)?
-		virtual Type * mutate( FunctionType * ftype ) override {
-			return ftype;
-		}
-		std::unordered_map< ReferenceToType *, TypeSubstitution * > envMap;
-	};
-
 	/// Mutator pass that replaces concrete instantiations of generic types with actual struct declarations, scoped appropriately
-	class GenericInstantiator final : public DeclMutator {
+	struct GenericInstantiator final : public WithTypeSubstitution, public WithDeclsToAdd, public WithVisitorRef<GenericInstantiator>, public WithGuards {
 		/// Map of (generic type, parameter list) pairs to concrete type instantiations
 		InstantiationMap< AggregateDecl, AggregateDecl > instantiations;
@@ -178,15 +164,18 @@
 		/// Namer for concrete types
 		UniqueName typeNamer;
-		/// Reference to mapping of environments
-		const std::unordered_map< ReferenceToType *, TypeSubstitution * > & envMap;
-	public:
-		GenericInstantiator( const std::unordered_map< ReferenceToType *, TypeSubstitution * > & envMap ) : DeclMutator(), instantiations(), dtypeStatics(), typeNamer("_conc_"), envMap( envMap ) {}
-
-		using DeclMutator::mutate;
-		virtual Type* mutate( StructInstType *inst ) override;
-		virtual Type* mutate( UnionInstType *inst ) override;
-
-		virtual void doBeginScope() override;
-		virtual void doEndScope() override;
+		/// Should not make use of type environment to replace types of function parameter and return values.
+		bool inFunctionType = false;
+		GenericInstantiator() : instantiations(), dtypeStatics(), typeNamer("_conc_") {}
+
+		Type* postmutate( StructInstType *inst );
+		Type* postmutate( UnionInstType *inst );
+
+		void premutate( FunctionType * ftype ) {
+			GuardValue( inFunctionType );
+			inFunctionType = true;
+		}
+
+		void beginScope();
+		void endScope();
 	private:
 		/// Wrap instantiation lookup for structs
@@ -207,8 +196,6 @@
 
 	void instantiateGeneric( std::list< Declaration* > &translationUnit ) {
-		EnvFinder finder;
-		mutateAll( translationUnit, finder );
-		GenericInstantiator instantiator( finder.envMap );
-		instantiator.mutateDeclarationList( translationUnit );
+		PassVisitor<GenericInstantiator> instantiator;
+		mutateAll( translationUnit, instantiator );
 	}
 
@@ -306,6 +293,5 @@
 	Type *GenericInstantiator::replaceWithConcrete( Type *type, bool doClone ) {
 		if ( TypeInstType *typeInst = dynamic_cast< TypeInstType * >( type ) ) {
-			if ( envMap.count( typeInst ) ) {
-				TypeSubstitution * env = envMap.at( typeInst );
+			if ( env && ! inFunctionType ) {
 				Type *concrete = env->lookup( typeInst->get_name() );
 				if ( concrete ) {
@@ -331,10 +317,5 @@
 
 
-	Type* GenericInstantiator::mutate( StructInstType *inst ) {
-		// mutate subtypes
-		Type *mutated = Mutator::mutate( inst );
-		inst = dynamic_cast< StructInstType* >( mutated );
-		if ( ! inst ) return mutated;
-
+	Type* GenericInstantiator::postmutate( StructInstType *inst ) {
 		// exit early if no need for further mutation
 		if ( inst->get_parameters().empty() ) return inst;
@@ -368,6 +349,6 @@
 				substituteMembers( inst->get_baseStruct()->get_members(), *inst->get_baseParameters(), typeSubs, concDecl->get_members() );
 				insert( inst, typeSubs, concDecl ); // must insert before recursion
-				concDecl->acceptMutator( *this ); // recursively instantiate members
-				DeclMutator::addDeclaration( concDecl ); // must occur before declaration is added so that member instantiations appear first
+				concDecl->acceptMutator( *visitor ); // recursively instantiate members
+				declsToAddBefore.push_back( concDecl ); // must occur before declaration is added so that member instantiations appear first
 			}
 			StructInstType *newInst = new StructInstType( inst->get_qualifiers(), concDecl->get_name() );
@@ -388,10 +369,5 @@
 	}
 
-	Type* GenericInstantiator::mutate( UnionInstType *inst ) {
-		// mutate subtypes
-		Type *mutated = Mutator::mutate( inst );
-		inst = dynamic_cast< UnionInstType* >( mutated );
-		if ( ! inst ) return mutated;
-
+	Type* GenericInstantiator::postmutate( UnionInstType *inst ) {
 		// exit early if no need for further mutation
 		if ( inst->get_parameters().empty() ) return inst;
@@ -423,6 +399,6 @@
 				substituteMembers( inst->get_baseUnion()->get_members(), *inst->get_baseParameters(), typeSubs, concDecl->get_members() );
 				insert( inst, typeSubs, concDecl ); // must insert before recursion
-				concDecl->acceptMutator( *this ); // recursively instantiate members
-				DeclMutator::addDeclaration( concDecl ); // must occur before declaration is added so that member instantiations appear first
+				concDecl->acceptMutator( *visitor ); // recursively instantiate members
+				declsToAddBefore.push_back( concDecl ); // must occur before declaration is added so that member instantiations appear first
 			}
 			UnionInstType *newInst = new UnionInstType( inst->get_qualifiers(), concDecl->get_name() );
@@ -442,12 +418,10 @@
 	}
 
-	void GenericInstantiator::doBeginScope() {
-		DeclMutator::doBeginScope();
+	void GenericInstantiator::beginScope() {
 		instantiations.beginScope();
 		dtypeStatics.beginScope();
 	}
 
-	void GenericInstantiator::doEndScope() {
-		DeclMutator::doEndScope();
+	void GenericInstantiator::endScope() {
 		instantiations.endScope();
 		dtypeStatics.endScope();
