Index: src/ResolvExpr/AlternativeFinder.cc
===================================================================
--- src/ResolvExpr/AlternativeFinder.cc	(revision 5a3e1f1c53099fe4fae4fcf2d6706f2a64707236)
+++ src/ResolvExpr/AlternativeFinder.cc	(revision 1a5964106665838a81eb2b1e314a54175ea64f11)
@@ -687,9 +687,5 @@
 		/// as appropriate.
 		void bindAssertion( const DeclarationWithType* curDecl, const AssertionSetValue& assnInfo, 
-				AssertionResnState& resn, AssertionPack&& match ) {
-			addToIndexer( match.have, resn.indexer );
-			resn.newNeed.insert( match.need.begin(), match.need.end() );
-			resn.openVars = std::move(match.openVars);
-			resn.alt.env = std::move(match.env);
+				Alternative& alt, AssertionPack& match ) {
 
 			DeclarationWithType* candidate = match.cdata.id;
@@ -702,5 +698,5 @@
 			}
 
-			Expression* varExpr = match.cdata.combine( resn.alt.cvtCost );
+			Expression* varExpr = match.cdata.combine( alt.cvtCost );
 			varExpr->result = match.adjType;
 
@@ -708,5 +704,5 @@
 			// parameters to add the candidate o (i.e. the set of inferred parameters belonging 
 			// to the entity which requested the assertion parameter)
-			InferredParams* inferParams = &resn.alt.expr->inferParams;
+			InferredParams* inferParams = &alt.expr->inferParams;
 			for ( UniqueId id : assnInfo.idChain ) {
 				inferParams = (*inferParams)[ id ].inferParams.get();
@@ -759,5 +755,11 @@
 
 			// otherwise bind current match in ongoing scope
-			bindAssertion( curDecl, assnInfo, resn, std::move(matches.front()) );
+			AssertionPack& match = matches.front();
+			addToIndexer( match.have, resn.indexer );
+			resn.newNeed.insert( match.need.begin(), match.need.end() );
+			resn.openVars = std::move(match.openVars);
+			resn.alt.env = std::move(match.env);
+
+			bindAssertion( curDecl, assnInfo, resn.alt, match );
 			return true;
 		}
@@ -771,18 +773,37 @@
 			/// Stack of environments, to support backtracking
 			std::vector<TypeEnvironment> envs;
+			/// Open variables for environment combination
+			std::vector<OpenVarSet> varSets;
 			/// Indexer to use for environment merges
 			const SymTab::Indexer& indexer;
 		public:
-			/// Outputs a pair consisting of the merged environment and the list of interpretations
-			using OutType = std::pair<TypeEnvironment, std::vector<DeferElement>>;
-
-			InterpretationEnvMerger( const TypeEnvironment& env, const SymTab::Indexer& indexer ) 
-				: crnt{}, envs{}, indexer{indexer} { envs.push_back( env ); }
+			/// The merged environment/open variables and the list of interpretations
+			struct OutType {
+				TypeEnvironment env;
+				OpenVarSet openVars;
+				std::vector<DeferElement> assns;
+
+				OutType( const TypeEnvironment& env, const OpenVarSet& openVars, 
+					const std::vector<DeferElement>& assns )
+					: env(env), openVars(openVars), assns(assns) {}
+			};
+
+			InterpretationEnvMerger( const TypeEnvironment& env, const OpenVarSet& openVars, 
+				const SymTab::Indexer& indexer ) : crnt{}, envs{}, varSets{}, indexer{indexer} {
+				envs.push_back( env );
+				varSets.push_back( openVars );
+			}
 
 			ComboResult append( DeferElement i ) {
 				TypeEnvironment env = envs.back();
-				if ( ! env.combine( i.match.env, indexer ) ) return ComboResult::REJECT_THIS;
+				OpenVarSet openVars = varSets.back();
+				mergeOpenVars( openVars, i.match.openVars );
+
+				if ( ! env.combine( i.match.env, openVars, indexer ) )
+					return ComboResult::REJECT_THIS;
+				
 				crnt.push_back( i );
 				envs.push_back( env );
+				varSets.push_back( openVars );
 				return ComboResult::ACCEPT;
 			}
@@ -791,7 +812,8 @@
 				crnt.pop_back();
 				envs.pop_back();
-			}
-
-			OutType finalize() { return { envs.back(), crnt }; }
+				varSets.pop_back();
+			}
+
+			OutType finalize() { return { envs.back(), varSets.back(), crnt }; }
 		};
 	}
@@ -833,5 +855,6 @@
 
 					std::vector<InterpretationEnvMerger::OutType> compatible = filterCombos( 
-						resn.deferred, InterpretationEnvMerger{ resn.alt.env, resn.indexer });
+						resn.deferred, 
+						InterpretationEnvMerger{ resn.alt.env, resn.openVars, resn.indexer } );
 					
 					for ( auto& compat : compatible ) {
@@ -839,11 +862,15 @@
 
 						// add compatible assertions to new resolution state
-						for ( DeferElement el : compat.second ) {
-							bindAssertion( 
-								el.curDecl, el.assnInfo, new_resn, AssertionPack{el.match} );
+						for ( DeferElement el : compat.assns ) {
+							AssertionPack match = el.match;
+							addToIndexer( match.have, new_resn.indexer );
+							resn.newNeed.insert( match.need.begin(), match.need.end() );
+							
+							bindAssertion( el.curDecl, el.assnInfo, new_resn.alt, match );
 						}
 
 						// set mutual environment into resolution state
-						new_resn.alt.env = std::move(compat.first);
+						new_resn.alt.env = std::move(compat.env);
+						new_resn.openVars = std::move(compat.openVars);
 
 						// add successful match or push back next state
Index: src/ResolvExpr/TypeEnvironment.cc
===================================================================
--- src/ResolvExpr/TypeEnvironment.cc	(revision 5a3e1f1c53099fe4fae4fcf2d6706f2a64707236)
+++ src/ResolvExpr/TypeEnvironment.cc	(revision 1a5964106665838a81eb2b1e314a54175ea64f11)
@@ -36,5 +36,5 @@
 
 namespace ResolvExpr {
-	#if 0
+	#ifdef EXPENSIVE_ENV_VALIDATION
 	#define PRE_POST_VALIDATE auto dbg = ValidateGuard{this, __func__};
 	#define PRE_POST_VALIDATE_NOM auto dbg = ValidateGuard{this};
@@ -411,5 +411,6 @@
 	}
 
-	bool TypeEnvironment::combine( const TypeEnvironment& o, const SymTab::Indexer& indexer ) {
+	bool TypeEnvironment::combine( const TypeEnvironment& o, OpenVarSet& openVars, 
+			const SymTab::Indexer& indexer ) {
 		// short-circuit for empty cases
 		if ( o.isEmpty() ) return true;
@@ -657,5 +658,4 @@
 				Type* common = nullptr;
 				AssertionSet need, have;
-				OpenVarSet openVars;
 				if ( unifyInexact( ebound.type->clone(), nbound.type->clone(), *this, need, have, 
 						openVars, WidenMode{ ebound.allowWidening, nbound.allowWidening }, 
Index: src/ResolvExpr/TypeEnvironment.h
===================================================================
--- src/ResolvExpr/TypeEnvironment.h	(revision 5a3e1f1c53099fe4fae4fcf2d6706f2a64707236)
+++ src/ResolvExpr/TypeEnvironment.h	(revision 1a5964106665838a81eb2b1e314a54175ea64f11)
@@ -78,4 +78,9 @@
 	typedef std::map< std::string, TypeDecl::Data > OpenVarSet;
 
+	/// merges one set of open vars into another
+	static inline void mergeOpenVars( OpenVarSet& dst, const OpenVarSet& src ) {
+		for ( const auto& entry : src ) { dst[ entry.first ] = entry.second; }
+	}
+
 	void printAssertionSet( const AssertionSet &, std::ostream &, int indent = 0 );
 	void printOpenVarSet( const OpenVarSet &, std::ostream &, int indent = 0 );
@@ -138,5 +143,8 @@
 	};
 
+	//#define EXPENSIVE_ENV_VALIDATION
+	#ifdef EXPENSIVE_ENV_VALIDATION
 	class ValidateGuard;
+	#endif
 
 	class TypeEnvironment {
@@ -156,7 +164,9 @@
 		Bindings* bindings;
 
-		// for debugging
+		#ifdef EXPENSIVE_ENV_VALIDATION
+		/// for debugging
 		friend ValidateGuard;
 		const char* last_fn = "<none>";
+		#endif
 
 		/// Merges the classes rooted at root1 and root2, returning a pair containing the root and 
@@ -225,5 +235,6 @@
 		/// from the same initial environment.
 		/// Returns false if unsuccessful, but does NOT roll back partial changes
-		bool combine( const TypeEnvironment& o, const SymTab::Indexer& indexer );
+		bool combine( const TypeEnvironment& o, OpenVarSet& openVars, 
+			const SymTab::Indexer& indexer );
 	
 		void extractOpenVars( OpenVarSet &openVars ) const;
