Index: src/ResolvExpr/AlternativeFinder.cc
===================================================================
--- src/ResolvExpr/AlternativeFinder.cc	(revision c95b11526c10568ee3415f3873af592f6779ea99)
+++ src/ResolvExpr/AlternativeFinder.cc	(revision 490db327533a4cac681c8fe17b7d300026d46a79)
@@ -22,4 +22,5 @@
 #include <memory>                  // for allocator_traits<>::value_type
 #include <utility>                 // for pair
+#include <vector>                  // for vector
 
 #include "Alternative.h"           // for AltList, Alternative
@@ -333,5 +334,4 @@
 		tmpCost.incPoly( -tmpCost.get_polyCost() );
 		if ( tmpCost != Cost::zero ) {
-		// if ( convCost != Cost::zero ) {
 			Type *newType = formalType->clone();
 			env.apply( newType );
@@ -405,120 +405,4 @@
 ///     needAssertions.insert( needAssertions.end(), (*tyvar)->get_assertions().begin(), (*tyvar)->get_assertions().end() );
 		}
-	}
-
-	/// instantiate a single argument by matching actuals from [actualIt, actualEnd) against formalType,
-	/// producing expression(s) in out and their total cost in cost.
-	template< typename AltIterator, typename OutputIterator >
-	bool instantiateArgument( Type * formalType, Initializer * defaultValue, AltIterator & actualIt, AltIterator actualEnd, OpenVarSet & openVars, TypeEnvironment & resultEnv, AssertionSet & resultNeed, AssertionSet & resultHave, const SymTab::Indexer & indexer, Cost & cost, OutputIterator out ) {
-		if ( TupleType * tupleType = dynamic_cast< TupleType * >( formalType ) ) {
-			// formalType is a TupleType - group actuals into a TupleExpr whose type unifies with the TupleType
-			std::list< Expression * > exprs;
-			for ( Type * type : *tupleType ) {
-				if ( ! instantiateArgument( type, defaultValue, actualIt, actualEnd, openVars, resultEnv, resultNeed, resultHave, indexer, cost, back_inserter( exprs ) ) ) {
-					deleteAll( exprs );
-					return false;
-				}
-			}
-			*out++ = new TupleExpr( exprs );
-		} else if ( TypeInstType * ttype = Tuples::isTtype( formalType ) ) {
-			// xxx - mixing default arguments with variadic??
-			std::list< Expression * > exprs;
-			for ( ; actualIt != actualEnd; ++actualIt ) {
-				exprs.push_back( actualIt->expr->clone() );
-				cost += actualIt->cost;
-			}
-			Expression * arg = nullptr;
-			if ( exprs.size() == 1 && Tuples::isTtype( exprs.front()->get_result() ) ) {
-				// the case where a ttype value is passed directly is special, e.g. for argument forwarding purposes
-				// xxx - what if passing multiple arguments, last of which is ttype?
-				// xxx - what would happen if unify was changed so that unifying tuple types flattened both before unifying lists? then pass in TupleType(ttype) below.
-				arg = exprs.front();
-			} else {
-				arg = new TupleExpr( exprs );
-			}
-			assert( arg && arg->get_result() );
-			if ( ! unify( ttype, arg->get_result(), resultEnv, resultNeed, resultHave, openVars, indexer ) ) {
-				return false;
-			}
-			*out++ = arg;
-		} else if ( actualIt != actualEnd ) {
-			// both actualType and formalType are atomic (non-tuple) types - if they unify
-			// then accept actual as an argument, otherwise return false (fail to instantiate argument)
-			Expression * actual = actualIt->expr;
-			Type * actualType = actual->get_result();
-
-			PRINT(
-				std::cerr << "formal type is ";
-				formalType->print( std::cerr );
-				std::cerr << std::endl << "actual type is ";
-				actualType->print( std::cerr );
-				std::cerr << std::endl;
-			)
-			if ( ! unify( formalType, actualType, resultEnv, resultNeed, resultHave, openVars, indexer ) ) {
-				// std::cerr << "unify failed" << std::endl;
-				return false;
-			}
-			// move the expression from the alternative to the output iterator
-			*out++ = actual;
-			actualIt->expr = nullptr;
-			cost += actualIt->cost;
-			++actualIt;
-		} else {
-			// End of actuals - Handle default values
-			if ( SingleInit *si = dynamic_cast<SingleInit *>( defaultValue )) {
-				if ( CastExpr * castExpr = dynamic_cast< CastExpr * >( si->get_value() ) ) {
-					// so far, only constant expressions are accepted as default values
-					if ( ConstantExpr *cnstexpr = dynamic_cast<ConstantExpr *>( castExpr->get_arg() ) ) {
-						if ( Constant *cnst = dynamic_cast<Constant *>( cnstexpr->get_constant() ) ) {
-							if ( unify( formalType, cnst->get_type(), resultEnv, resultNeed, resultHave, openVars, indexer ) ) {
-								*out++ = cnstexpr->clone();
-								return true;
-							} // if
-						} // if
-					} // if
-				}
-			} // if
-			return false;
-		} // if
-		return true;
-	}
-
-	bool AlternativeFinder::instantiateFunction( std::list< DeclarationWithType* >& formals, const AltList &actuals, bool isVarArgs, OpenVarSet& openVars, TypeEnvironment &resultEnv, AssertionSet &resultNeed, AssertionSet &resultHave, AltList & out ) {
-		simpleCombineEnvironments( actuals.begin(), actuals.end(), resultEnv );
-		// make sure we don't widen any existing bindings
-		for ( TypeEnvironment::iterator i = resultEnv.begin(); i != resultEnv.end(); ++i ) {
-			i->allowWidening = false;
-		}
-		resultEnv.extractOpenVars( openVars );
-
-		// flatten actuals so that each actual has an atomic (non-tuple) type
-		AltList exploded;
-		Tuples::explode( actuals, indexer, back_inserter( exploded ) );
-
-		AltList::iterator actualExpr = exploded.begin();
-		AltList::iterator actualEnd = exploded.end();
-		for ( DeclarationWithType * formal : formals ) {
-			// match flattened actuals with formal parameters - actuals will be grouped to match
-			// with formals as appropriate
-			Cost cost = Cost::zero;
-			std::list< Expression * > newExprs;
-			ObjectDecl * obj = strict_dynamic_cast< ObjectDecl * >( formal );
-			if ( ! instantiateArgument( obj->get_type(), obj->get_init(), actualExpr, actualEnd, openVars, resultEnv, resultNeed, resultHave, indexer, cost, back_inserter( newExprs ) ) ) {
-				deleteAll( newExprs );
-				return false;
-			}
-			// success - produce argument as a new alternative
-			assert( newExprs.size() == 1 );
-			out.push_back( Alternative( newExprs.front(), resultEnv, cost ) );
-		}
-		if ( actualExpr != actualEnd ) {
-			// there are still actuals remaining, but we've run out of formal parameters to match against
-			// this is okay only if the function is variadic
-			if ( ! isVarArgs ) {
-				return false;
-			}
-			out.splice( out.end(), exploded, actualExpr, actualEnd );
-		}
-		return true;
 	}
 
@@ -675,31 +559,339 @@
 	}
 
-	template< typename OutputIterator >
-	void AlternativeFinder::makeFunctionAlternatives( const Alternative &func, FunctionType *funcType, const AltList &actualAlt, OutputIterator out ) {
-		OpenVarSet openVars;
-		AssertionSet resultNeed, resultHave;
-		TypeEnvironment resultEnv( func.env );
-		makeUnifiableVars( funcType, openVars, resultNeed );
-		resultEnv.add( funcType->get_forall() ); // add all type variables as open variables now so that those not used in the parameter list are still considered open
-		AltList instantiatedActuals; // filled by instantiate function
+	/// Gets a default value from an initializer, nullptr if not present
+	ConstantExpr* getDefaultValue( Initializer* init ) {
+		if ( SingleInit* si = dynamic_cast<SingleInit*>( init ) ) {
+			if ( CastExpr* ce = dynamic_cast<CastExpr*>( si->get_value() ) ) {
+				return dynamic_cast<ConstantExpr*>( ce->get_arg() );
+			}
+		}
+		return nullptr;
+	}
+
+	/// State to iteratively build a match of parameter expressions to arguments
+	struct ArgPack {
+		AltList actuals;                 ///< Arguments included in this pack
+		TypeEnvironment env;             ///< Environment for this pack
+		AssertionSet need;               ///< Assertions outstanding for this pack
+		AssertionSet have;               ///< Assertions found for this pack
+		OpenVarSet openVars;             ///< Open variables for this pack
+		unsigned nextArg;                ///< Index of next argument in arguments list
+		std::vector<Alternative> expls;  ///< Exploded actuals left over from last match
+		unsigned nextExpl;               ///< Index of next exploded alternative to use
+		std::vector<unsigned> tupleEls;  /// Number of elements in current tuple element(s)
+
+		ArgPack(const TypeEnvironment& env, const AssertionSet& need, const AssertionSet& have, 
+				const OpenVarSet& openVars)
+			: actuals(), env(env), need(need), have(have), openVars(openVars), nextArg(0),
+			  expls(), nextExpl(0), tupleEls() {}
+		
+		/// Starts a new tuple expression
+		void beginTuple() {
+			if ( ! tupleEls.empty() ) ++tupleEls.back();
+			tupleEls.push_back(0);
+		}
+
+		/// Ends a tuple expression, consolidating the appropriate actuals
+		void endTuple() {
+			// set up new Tuple alternative
+			std::list<Expression*> exprs;
+			Cost cost = Cost::zero;
+
+			// transfer elements into alternative
+			for (unsigned i = 0; i < tupleEls.back(); ++i) {
+				exprs.push_front( actuals.back().expr );
+				actuals.back().expr = nullptr;
+				cost += actuals.back().cost;
+				actuals.pop_back();
+			}
+			tupleEls.pop_back();
+
+			// build new alternative
+			actuals.emplace_back( new TupleExpr( exprs ), this->env, cost );
+		}
+
+		/// Clones and adds an actual, returns this
+		ArgPack& withArg( Expression* expr, Cost cost = Cost::zero ) {
+			actuals.emplace_back( expr->clone(), this->env, cost );
+			if ( ! tupleEls.empty() ) ++tupleEls.back();
+			return *this;
+		}
+	};
+
+	/// Instantiates an argument to match a formal, returns false if no results left
+	bool instantiateArgument( Type* formalType, Initializer* initializer, 
+			const std::vector< AlternativeFinder >& args, 
+			std::vector<ArgPack>& results, std::vector<ArgPack>& nextResults, 
+			const SymTab::Indexer& indexer ) {
+		if ( TupleType* tupleType = dynamic_cast<TupleType*>( formalType ) ) {
+			// formalType is a TupleType - group actuals into a TupleExpr
+			for ( ArgPack& result : results ) { result.beginTuple(); }
+			for ( Type* type : *tupleType ) {
+				// xxx - dropping initializer changes behaviour from previous, but seems correct
+				if ( ! instantiateArgument( type, nullptr, args, results, nextResults, indexer ) ) 
+					return false;
+			}
+			for ( ArgPack& result : results ) { result.endTuple(); }
+			return true;
+		} else if ( TypeInstType* ttype = Tuples::isTtype( formalType ) ) {
+			// formalType is a ttype, consumes all remaining arguments
+			// xxx - mixing default arguments with variadic??
+			std::vector<ArgPack> finalResults{};  /// list of completed tuples
+			// start tuples
+			for ( ArgPack& result : results ) {
+				result.beginTuple();
+
+				// use rest of exploded tuple if present
+				while ( result.nextExpl < result.expls.size() ) {
+					const Alternative& actual = result.expls[result.nextExpl];
+					result.env.addActual( actual.env, result.openVars );
+					result.withArg( actual.expr );
+					++result.nextExpl;
+				}
+			}
+			// iterate until all results completed
+			while ( ! results.empty() ) {
+				// add another argument to results
+				for ( ArgPack& result : results ) {
+					// finish result when out of arguments
+					if ( result.nextArg >= args.size() ) {
+						Type* argType = result.actuals.back().expr->get_result();
+						if ( result.tupleEls.back() == 1 && Tuples::isTtype( argType ) ) {
+							// the case where a ttype value is passed directly is special, e.g. for 
+							// argument forwarding purposes
+							// xxx - what if passing multiple arguments, last of which is ttype?
+							// xxx - what would happen if unify was changed so that unifying tuple 
+							// types flattened both before unifying lists? then pass in TupleType
+							// (ttype) below.
+							result.tupleEls.pop_back();
+						} else {
+							// collapse leftover arguments into tuple
+							result.endTuple();
+							argType = result.actuals.back().expr->get_result();
+						}
+						// check unification for ttype before adding to final
+						if ( unify( ttype, argType, result.env, result.need, result.have, 
+								result.openVars, indexer ) ) {
+							finalResults.push_back( std::move(result) );
+						}
+						continue;
+					}
+
+					// add each possible next argument
+					for ( const Alternative& actual : args[result.nextArg] ) {
+						ArgPack aResult = result;  // copy to clone everything
+						// add details of actual to result
+						aResult.env.addActual( actual.env, aResult.openVars );
+						Cost cost = actual.cost;
+		
+						// explode argument
+						std::vector<Alternative> exploded;
+						Tuples::explode( actual, indexer, back_inserter( exploded ) );
+						
+						// add exploded argument to tuple
+						for ( Alternative& aActual : exploded ) {
+							aResult.withArg( aActual.expr, cost );
+							cost = Cost::zero;
+						}
+						++aResult.nextArg;
+						nextResults.push_back( std::move(aResult) );
+					}
+				}
+
+				// reset for next round
+				results.swap( nextResults );
+				nextResults.clear();
+			}
+			results.swap( finalResults );
+			return ! results.empty();
+		}
+		
+		// iterate each current subresult
+		for ( unsigned iResult = 0; iResult < results.size(); ++iResult ) {
+			ArgPack& result = results[iResult];
+
+			if ( result.nextExpl < result.expls.size() ) {
+				// use remainder of exploded tuple if present
+				const Alternative& actual = result.expls[result.nextExpl];
+				result.env.addActual( actual.env, result.openVars );
+				Type* actualType = actual.expr->get_result();
+
+				PRINT(
+					std::cerr << "formal type is ";
+					formalType->print( std::cerr );
+					std::cerr << std::endl << "actual type is ";
+					actualType->print( std::cerr );
+					std::cerr << std::endl;
+				)
+				
+				if ( unify( formalType, actualType, result.env, result.need, result.have, 
+						result.openVars, indexer ) ) {
+					++result.nextExpl;
+					nextResults.push_back( std::move(result.withArg( actual.expr )) );
+				}
+
+				continue;
+			} else if ( result.nextArg >= args.size() ) {
+				// use default initializers if out of arguments
+				if ( ConstantExpr* cnstExpr = getDefaultValue( initializer ) ) {
+					if ( Constant* cnst = dynamic_cast<Constant*>( cnstExpr->get_constant() ) ) {
+						if ( unify( formalType, cnst->get_type(), result.env, result.need, 
+								result.have, result.openVars, indexer ) ) {
+							nextResults.push_back( std::move(result.withArg( cnstExpr )) );
+						}
+					}
+				}
+				continue;
+			}
+
+			// Check each possible next argument
+			for ( const Alternative& actual : args[result.nextArg] ) {
+				ArgPack aResult = result;  // copy to clone everything
+				// add details of actual to result
+				aResult.env.addActual( actual.env, aResult.openVars );
+
+				// explode argument
+				std::vector<Alternative> exploded;
+				Tuples::explode( actual, indexer, back_inserter( exploded ) );
+				if ( exploded.empty() ) {
+					// skip empty tuple arguments
+					++aResult.nextArg;
+					results.push_back( std::move(aResult) );
+					continue;
+				}
+
+				// consider only first exploded actual
+				const Alternative& aActual = exploded.front();
+				Type* actualType = aActual.expr->get_result()->clone();
+
+				PRINT(
+					std::cerr << "formal type is ";
+					formalType->print( std::cerr );
+					std::cerr << std::endl << "actual type is ";
+					actualType->print( std::cerr );
+					std::cerr << std::endl;
+				)
+
+				// attempt to unify types
+				if ( unify( formalType, actualType, aResult.env, aResult.need, aResult.have, aResult.openVars, indexer ) ) {
+					// add argument
+					aResult.withArg( aActual.expr, actual.cost );
+					++aResult.nextArg;
+					if ( exploded.size() > 1 ) {
+						// other parts of tuple left over
+						aResult.expls = std::move( exploded );
+						aResult.nextExpl = 1;
+					}
+					nextResults.push_back( std::move(aResult) );
+				}
+			}
+		}
+
+		// reset for next parameter
+		results.swap( nextResults );
+		nextResults.clear();
+		
+		return ! results.empty();
+	}	
+
+	template<typename OutputIterator>
+	void AlternativeFinder::makeFunctionAlternatives( const Alternative &func, 
+			FunctionType *funcType, const std::vector< AlternativeFinder > &args, 
+			OutputIterator out ) {
+		OpenVarSet funcOpenVars;
+		AssertionSet funcNeed, funcHave;
+		TypeEnvironment funcEnv( func.env );
+		makeUnifiableVars( funcType, funcOpenVars, funcNeed );
+		// add all type variables as open variables now so that those not used in the parameter 
+		// list are still considered open.
+		funcEnv.add( funcType->get_forall() );
+		
 		if ( targetType && ! targetType->isVoid() && ! funcType->get_returnVals().empty() ) {
 			// attempt to narrow based on expected target type
 			Type * returnType = funcType->get_returnVals().front()->get_type();
-			if ( ! unify( returnType, targetType, resultEnv, resultNeed, resultHave, openVars, indexer ) ) {
-				// unification failed, don't pursue this alternative
+			if ( ! unify( returnType, targetType, funcEnv, funcNeed, funcHave, funcOpenVars, 
+					indexer ) ) {
+				// unification failed, don't pursue this function alternative
 				return;
 			}
 		}
 
-		if ( instantiateFunction( funcType->get_parameters(), actualAlt, funcType->get_isVarArgs(), openVars, resultEnv, resultNeed, resultHave, instantiatedActuals ) ) {
+		// iteratively build matches, one parameter at a time
+		std::vector<ArgPack> results{ ArgPack{ funcEnv, funcNeed, funcHave, funcOpenVars } };
+		std::vector<ArgPack> nextResults{};
+		for ( DeclarationWithType* formal : funcType->get_parameters() ) {
+			ObjectDecl* obj = strict_dynamic_cast< ObjectDecl* >( formal );
+			if ( ! instantiateArgument( 
+					obj->get_type(), obj->get_init(), args, results, nextResults, indexer ) )
+				return;
+		}
+
+		// filter out results that don't use all the arguments, and aren't variadic
+		std::vector<ArgPack> finalResults{};
+		if ( funcType->get_isVarArgs() ) {
+			for ( ArgPack& result : results ) {
+				// use rest of exploded tuple if present
+				while ( result.nextExpl < result.expls.size() ) {
+					const Alternative& actual = result.expls[result.nextExpl];
+					result.env.addActual( actual.env, result.openVars );
+					result.withArg( actual.expr );
+					++result.nextExpl;
+				}
+			}
+
+			while ( ! results.empty() ) {
+				// build combinations for all remaining arguments
+				for ( ArgPack& result : results ) {
+					// keep if used all arguments
+					if ( result.nextArg >= args.size() ) {
+						finalResults.push_back( std::move(result) );
+						continue;
+					}
+
+					// add each possible next argument
+					for ( const Alternative& actual : args[result.nextArg] ) {
+						ArgPack aResult = result; // copy to clone everything
+						// add details of actual to result
+						aResult.env.addActual( actual.env, aResult.openVars );
+						Cost cost = actual.cost;
+
+						// explode argument
+						std::vector<Alternative> exploded;
+						Tuples::explode( actual, indexer, back_inserter( exploded ) );
+
+						// add exploded argument to arg list
+						for ( Alternative& aActual : exploded ) {
+							aResult.withArg( aActual.expr, cost );
+							cost = Cost::zero;
+						}
+						++aResult.nextArg;
+						nextResults.push_back( std::move(aResult) );
+					}
+				}
+
+				// reset for next round
+				results.swap( nextResults );
+				nextResults.clear();
+			}
+		} else {
+			// filter out results that don't use all the arguments
+			for ( ArgPack& result : results ) {
+				if ( result.nextExpl >= result.expls.size() && result.nextArg >= args.size() ) {
+					finalResults.push_back( std::move(result) );
+				}
+			}
+		}
+
+		// validate matching combos, add to final result list
+		for ( ArgPack& result : finalResults ) {
 			ApplicationExpr *appExpr = new ApplicationExpr( func.expr->clone() );
-			Alternative newAlt( appExpr, resultEnv, sumCost( instantiatedActuals ) );
-			makeExprList( instantiatedActuals, appExpr->get_args() );
+			Alternative newAlt( appExpr, result.env, sumCost( result.actuals ) );
+			makeExprList( result.actuals, appExpr->get_args() );
 			PRINT(
 				std::cerr << "instantiate function success: " << appExpr << std::endl;
 				std::cerr << "need assertions:" << std::endl;
-				printAssertionSet( resultNeed, std::cerr, 8 );
+				printAssertionSet( result.need, std::cerr, 8 );
 			)
-			inferParameters( resultNeed, resultHave, newAlt, openVars, out );
+			inferParameters( result.need, result.have, newAlt, result.openVars, out );
 		}
 	}
@@ -711,13 +903,11 @@
 		if ( funcFinder.alternatives.empty() ) return;
 
-		std::list< AlternativeFinder > argAlternatives;
-		findSubExprs( untypedExpr->begin_args(), untypedExpr->end_args(), back_inserter( argAlternatives ) );
-
-		std::list< AltList > possibilities;
-		combos( argAlternatives.begin(), argAlternatives.end(), back_inserter( possibilities ) );
+		std::vector< AlternativeFinder > argAlternatives;
+		findSubExprs( untypedExpr->begin_args(), untypedExpr->end_args(), 
+			back_inserter( argAlternatives ) );
 
 		// take care of possible tuple assignments
 		// if not tuple assignment, assignment is taken care of as a normal function call
-		Tuples::handleTupleAssignment( *this, untypedExpr, possibilities );
+		Tuples::handleTupleAssignment( *this, untypedExpr, argAlternatives );
 
 		// find function operators
@@ -744,9 +934,6 @@
 						Alternative newFunc( *func );
 						referenceToRvalueConversion( newFunc.expr );
-						for ( std::list< AltList >::iterator actualAlt = possibilities.begin(); actualAlt != possibilities.end(); ++actualAlt ) {
-							// XXX
-							//Designators::check_alternative( function, *actualAlt );
-							makeFunctionAlternatives( newFunc, function, *actualAlt, std::back_inserter( candidates ) );
-						}
+						makeFunctionAlternatives( newFunc, function, argAlternatives, 
+							std::back_inserter( candidates ) );
 					}
 				} else if ( TypeInstType *typeInst = dynamic_cast< TypeInstType* >( func->expr->get_result()->stripReferences() ) ) { // handle ftype (e.g. *? on function pointer)
@@ -756,31 +943,38 @@
 							Alternative newFunc( *func );
 							referenceToRvalueConversion( newFunc.expr );
-							for ( std::list< AltList >::iterator actualAlt = possibilities.begin(); actualAlt != possibilities.end(); ++actualAlt ) {
-								makeFunctionAlternatives( newFunc, function, *actualAlt, std::back_inserter( candidates ) );
-							} // for
+							makeFunctionAlternatives( newFunc, function, argAlternatives, 
+								std::back_inserter( candidates ) );
 						} // if
 					} // if
-				}
-
-				// try each function operator ?() with the current function alternative and each of the argument combinations
-				for ( AltList::iterator funcOp = funcOpFinder.alternatives.begin(); funcOp != funcOpFinder.alternatives.end(); ++funcOp ) {
-					// check if the type is pointer to function
-					if ( PointerType *pointer = dynamic_cast< PointerType* >( funcOp->expr->get_result()->stripReferences() ) ) {
-						if ( FunctionType *function = dynamic_cast< FunctionType* >( pointer->get_base() ) ) {
+				}			
+			} catch ( SemanticError &e ) {
+				errors.append( e );
+			}
+		} // for
+
+		// try each function operator ?() with each function alternative
+		if ( ! funcOpFinder.alternatives.empty() ) {
+			// add function alternatives to front of argument list
+			argAlternatives.insert( argAlternatives.begin(), std::move(funcFinder) );
+
+			for ( AltList::iterator funcOp = funcOpFinder.alternatives.begin();
+					funcOp != funcOpFinder.alternatives.end(); ++funcOp ) {
+				try {
+					// check if type is a pointer to function
+					if ( PointerType* pointer = dynamic_cast<PointerType*>( 
+							funcOp->expr->get_result()->stripReferences() ) ) {
+						if ( FunctionType* function = 
+								dynamic_cast<FunctionType*>( pointer->get_base() ) ) {
 							Alternative newFunc( *funcOp );
 							referenceToRvalueConversion( newFunc.expr );
-							for ( std::list< AltList >::iterator actualAlt = possibilities.begin(); actualAlt != possibilities.end(); ++actualAlt ) {
-								AltList currentAlt;
-								currentAlt.push_back( *func );
-								currentAlt.insert( currentAlt.end(), actualAlt->begin(), actualAlt->end() );
-								makeFunctionAlternatives( newFunc, function, currentAlt, std::back_inserter( candidates ) );
-							} // for
-						} // if
-					} // if
-				} // for
-			} catch ( SemanticError &e ) {
-				errors.append( e );
-			}
-		} // for
+							makeFunctionAlternatives( newFunc, function, argAlternatives, 
+								std::back_inserter( candidates ) );
+						}
+					}
+				} catch ( SemanticError &e ) {
+					errors.append( e );
+				}
+			}
+		}
 
 		// Implement SFINAE; resolution errors are only errors if there aren't any non-erroneous resolutions
Index: src/ResolvExpr/AlternativeFinder.h
===================================================================
--- src/ResolvExpr/AlternativeFinder.h	(revision c95b11526c10568ee3415f3873af592f6779ea99)
+++ src/ResolvExpr/AlternativeFinder.h	(revision 490db327533a4cac681c8fe17b7d300026d46a79)
@@ -34,4 +34,31 @@
 	  public:
 		AlternativeFinder( const SymTab::Indexer &indexer, const TypeEnvironment &env );
+
+		AlternativeFinder( const AlternativeFinder& o )
+			: indexer(o.indexer), alternatives(o.alternatives), env(o.env), 
+			  targetType(o.targetType) {}
+		
+		AlternativeFinder( AlternativeFinder&& o )
+			: indexer(o.indexer), alternatives(std::move(o.alternatives)), env(o.env), 
+			  targetType(o.targetType) {}
+		
+		AlternativeFinder& operator= ( const AlternativeFinder& o ) {
+			if (&o == this) return *this;
+			
+			// horrific nasty hack to rebind references...
+			alternatives.~AltList();
+			new(this) AlternativeFinder(o);
+			return *this;
+		}
+
+		AlternativeFinder& operator= ( AlternativeFinder&& o ) {
+			if (&o == this) return *this;
+			
+			// horrific nasty hack to rebind references...
+			alternatives.~AltList();
+			new(this) AlternativeFinder(std::move(o));
+			return *this;
+		}
+
 		void find( Expression *expr, bool adjust = false, bool prune = true, bool failFast = true );
 		/// Calls find with the adjust flag set; adjustment turns array and function types into equivalent pointer types
@@ -99,7 +126,6 @@
 		/// Adds alternatives for offsetof expressions, given the base type and name of the member
 		template< typename StructOrUnionType > void addOffsetof( StructOrUnionType *aggInst, const std::string &name );
-		bool instantiateFunction( std::list< DeclarationWithType* >& formals, const AltList &actuals, bool isVarArgs, OpenVarSet& openVars, TypeEnvironment &resultEnv, AssertionSet &resultNeed, AssertionSet &resultHave, AltList & out );
-		template< typename OutputIterator >
-		void makeFunctionAlternatives( const Alternative &func, FunctionType *funcType, const AltList &actualAlt, OutputIterator out );
+		template<typename OutputIterator>
+		void makeFunctionAlternatives( const Alternative &func, FunctionType *funcType, const std::vector< AlternativeFinder >& args, OutputIterator out );
 		template< typename OutputIterator >
 		void inferParameters( const AssertionSet &need, AssertionSet &have, const Alternative &newAlt, OpenVarSet &openVars, OutputIterator out );
Index: src/ResolvExpr/TypeEnvironment.cc
===================================================================
--- src/ResolvExpr/TypeEnvironment.cc	(revision c95b11526c10568ee3415f3873af592f6779ea99)
+++ src/ResolvExpr/TypeEnvironment.cc	(revision 490db327533a4cac681c8fe17b7d300026d46a79)
@@ -201,4 +201,15 @@
 	}
 
+	void TypeEnvironment::addActual( const TypeEnvironment& actualEnv, OpenVarSet& openVars ) {
+		for ( const EqvClass& c : actualEnv ) {
+			EqvClass c2 = c;
+			c2.allowWidening = false;
+			for ( const std::string& var : c2.vars ) {
+				openVars[ var ] = c2.data;
+			}
+			env.push_back( std::move(c2) );
+		}
+	}
+
 } // namespace ResolvExpr
 
Index: src/ResolvExpr/TypeEnvironment.h
===================================================================
--- src/ResolvExpr/TypeEnvironment.h	(revision c95b11526c10568ee3415f3873af592f6779ea99)
+++ src/ResolvExpr/TypeEnvironment.h	(revision 490db327533a4cac681c8fe17b7d300026d46a79)
@@ -86,4 +86,8 @@
 		TypeEnvironment *clone() const { return new TypeEnvironment( *this ); }
 
+		/// Iteratively adds the environment of a new actual (with allowWidening = false), 
+		/// and extracts open variables.
+		void addActual( const TypeEnvironment& actualEnv, OpenVarSet& openVars );
+
 		typedef std::list< EqvClass >::iterator iterator;
 		iterator begin() { return env.begin(); }
