Index: src/ResolvExpr/AlternativeFinder.cc
===================================================================
--- src/ResolvExpr/AlternativeFinder.cc	(revision 66f8528e75e91260ef63ddd93ec58ba5e42ae5dc)
+++ src/ResolvExpr/AlternativeFinder.cc	(revision 53e3b4abe27a80dbbb460154523bd595743597f6)
@@ -267,81 +267,45 @@
 		std::list< Expression* >& actuals = appExpr->get_args();
 
-		std::list< Type * > formalTypes;
-		std::list< Type * >::iterator formalType = formalTypes.end();
-
 		for ( std::list< Expression* >::iterator actualExpr = actuals.begin(); actualExpr != actuals.end(); ++actualExpr ) {
-
+			Type * actualType = (*actualExpr)->get_result();
 			PRINT(
 				std::cerr << "actual expression:" << std::endl;
 				(*actualExpr)->print( std::cerr, 8 );
 				std::cerr << "--- results are" << std::endl;
-				(*actualExpr)->get_result()->print( std::cerr, 8 );
-			)
-			std::list< DeclarationWithType* >::iterator startFormal = formal;
+				actualType->print( std::cerr, 8 );
+			)
 			Cost actualCost;
-			std::list< Type * > flatActualTypes;
-			flatten( (*actualExpr)->get_result(), back_inserter( flatActualTypes ) );
-			for ( std::list< Type* >::iterator actualType = flatActualTypes.begin(); actualType != flatActualTypes.end(); ++actualType ) {
-
-
-				// tuple handling code
-				if ( formalType == formalTypes.end() ) {
-					// the type of the formal parameter may be a tuple type. To make this easier to work with,
-					// flatten the tuple type and traverse the resulting list of types, incrementing the formal
-					// iterator once its types have been extracted. Once a particular formal parameter's type has
-					// been exhausted load the next formal parameter's type.
-					if ( formal == formals.end() ) {
-						if ( function->get_isVarArgs() ) {
-							convCost += Cost( 1, 0, 0 );
-							break;
-						} else {
-							return Cost::infinity;
-						}
-					}
-					formalTypes.clear();
-					flatten( (*formal)->get_type(), back_inserter( formalTypes ) );
-					formalType = formalTypes.begin();
-					++formal;
+			if ( formal == formals.end() ) {
+				if ( function->get_isVarArgs() ) {
+					convCost += Cost( 1, 0, 0 );
+					continue;
+				} else {
+					return Cost::infinity;
 				}
-
-				PRINT(
-					std::cerr << std::endl << "converting ";
-					(*actualType)->print( std::cerr, 8 );
-					std::cerr << std::endl << " to ";
-					(*formalType)->print( std::cerr, 8 );
-				)
-				Cost newCost = conversionCost( *actualType, *formalType, indexer, alt.env );
-				PRINT(
-					std::cerr << std::endl << "cost is" << newCost << std::endl;
-				)
-
-				if ( newCost == Cost::infinity ) {
-					return newCost;
-				}
-				convCost += newCost;
-				actualCost += newCost;
-
-				convCost += Cost( 0, polyCost( *formalType, alt.env, indexer ) + polyCost( *actualType, alt.env, indexer ), 0 );
-
-				formalType++;
-			}
+			}
+			Type * formalType = (*formal)->get_type();
+			PRINT(
+				std::cerr << std::endl << "converting ";
+				actualType->print( std::cerr, 8 );
+				std::cerr << std::endl << " to ";
+				formalType->print( std::cerr, 8 );
+			)
+			Cost newCost = conversionCost( actualType, formalType, indexer, alt.env );
+			PRINT(
+				std::cerr << std::endl << "cost is" << newCost << std::endl;
+			)
+
+			if ( newCost == Cost::infinity ) {
+				return newCost;
+			}
+			convCost += newCost;
+			actualCost += newCost;
 			if ( actualCost != Cost( 0, 0, 0 ) ) {
-				std::list< DeclarationWithType* >::iterator startFormalPlusOne = startFormal;
-				startFormalPlusOne++;
-				if ( formal == startFormalPlusOne ) {
-					// not a tuple type
-					Type *newType = (*startFormal)->get_type()->clone();
-					alt.env.apply( newType );
-					*actualExpr = new CastExpr( *actualExpr, newType );
-				} else {
-					TupleType *newType = new TupleType( Type::Qualifiers() );
-					for ( std::list< DeclarationWithType* >::iterator i = startFormal; i != formal; ++i ) {
-						newType->get_types().push_back( (*i)->get_type()->clone() );
-					}
-					alt.env.apply( newType );
-					*actualExpr = new CastExpr( *actualExpr, newType );
-				}
-			}
-
+				Type *newType = formalType->clone();
+				alt.env.apply( newType );
+				*actualExpr = new CastExpr( *actualExpr, newType );
+			}
+			convCost += Cost( 0, polyCost( formalType, alt.env, indexer ) + polyCost( actualType, alt.env, indexer ), 0 );
+			++formal; // can't be in for-loop update because of the continue
 		}
 		if ( formal != formals.end() ) {
@@ -364,5 +328,4 @@
 			}
 			convCost += newCost;
-
 			convCost += Cost( 0, polyCost( assert->second.formalType, alt.env, indexer ) + polyCost( assert->second.actualType, alt.env, indexer ), 0 );
 		}
@@ -398,4 +361,24 @@
 			*out++ = tupleExpr;
 		} else if ( actualIt != actualEnd ) {
+			if ( TypeInstType * ttype = Tuples::isTtype( formalType ) ) {
+				// xxx - mixing default arguments with variadic??
+				if ( ! Tuples::isTtype( actualIt->expr->get_result() ) ) {
+					// xxx - what if passing multiple arguments, last of which is ttype?
+
+					// consume all remaining arguments, variadic style
+					std::list< Expression * > exprs;
+					for ( ; actualIt != actualEnd; ++actualIt ) {
+						exprs.push_back( actualIt->expr->clone() );
+						cost += actualIt->cost;
+					}
+					TupleExpr * arg = new TupleExpr( exprs );
+					assert( arg->get_result() );
+					// unification run for side effects
+					bool unifyResult = unify( ttype, arg->get_result(), resultEnv, resultNeed, resultHave, openVars, indexer );
+					assertf( unifyResult, "Somehow unifying ttype failed..." );
+					*out++ = arg;
+					return true;
+				}
+			}
 			// 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)
@@ -609,5 +592,5 @@
 		makeUnifiableVars( funcType, openVars, resultNeed );
 		AltList instantiatedActuals; // filled by instantiate function
-		if ( targetType && ! targetType->isVoid() ) {
+		if ( targetType && ! targetType->isVoid() && ! funcType->get_returnVals().empty() ) {
 			// attempt to narrow based on expected target type
 			Type * returnType = funcType->get_returnVals().front()->get_type();
Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision 66f8528e75e91260ef63ddd93ec58ba5e42ae5dc)
+++ src/ResolvExpr/Unify.cc	(revision 53e3b4abe27a80dbbb460154523bd595743597f6)
@@ -26,5 +26,5 @@
 #include "SymTab/Indexer.h"
 #include "Common/utility.h"
-
+#include "Tuples/Tuples.h"
 
 // #define DEBUG
@@ -160,6 +160,8 @@
 		  case TypeDecl::Ftype:
 			return isFtype( type, indexer );
+			case TypeDecl::Ttype:
+			// ttype eats up any remaining parameters, no matter the type
+			return true;
 		} // switch
-		assert( false );
 		return false;
 	}
@@ -485,10 +487,32 @@
 	}
 
+	template< typename Iterator >
+	Type * combineTypes( Iterator begin, Iterator end ) {
+		std::list< Type * > types;
+		for ( ; begin != end; ++begin ) {
+			// might need to break apart these types too?
+			// yes, looks like we should flatten begin and push back each types individually
+			types.push_back( (*begin)->get_type()->clone() );
+		}
+		return new TupleType( Type::Qualifiers(), types );
+	}
+
 	template< typename Iterator1, typename Iterator2 >
 	bool unifyDeclList( Iterator1 list1Begin, Iterator1 list1End, Iterator2 list2Begin, Iterator2 list2End, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, const SymTab::Indexer &indexer ) {
 		for ( ; list1Begin != list1End && list2Begin != list2End; ++list1Begin, ++list2Begin ) {
-			// Type * commonType;
-			// if ( ! unifyInexact( (*list1Begin)->get_type(), (*list2Begin)->get_type(), env, needAssertions, haveAssertions, openVars, WidenMode( true, true ), indexer, commonType ) ) {
-			if ( ! unifyExact( (*list1Begin)->get_type(), (*list2Begin)->get_type(), env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer ) ) {
+			Type * t1 = (*list1Begin)->get_type();
+			Type * t2 = (*list2Begin)->get_type();
+			bool isTtype1 = Tuples::isTtype( t1 );
+			bool isTtype2 = Tuples::isTtype( t2 );
+			// xxx - assumes ttype must be last parameter; needs cleanup; use unique_ptr for combinedType
+			if ( isTtype1 && ! isTtype2 ) {
+				// combine all of the things in list2, then unify
+				Type * combinedType = combineTypes( list2Begin, list2End );
+				return unifyExact( t1, combinedType, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
+			} else if ( isTtype2 && ! isTtype1 ) {
+				// combine all of the things in list1, then unify
+				Type * combinedType = combineTypes( list1Begin, list1End );
+				return unifyExact( combinedType, t2, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
+			} else if ( ! unifyExact( t1, t2, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer ) ) {
 				return false;
 			} // if
@@ -504,5 +528,6 @@
 		FunctionType *otherFunction = dynamic_cast< FunctionType* >( type2 );
 		if ( otherFunction && functionType->get_isVarArgs() == otherFunction->get_isVarArgs() ) {
-			if ( functionType->get_parameters().size() == otherFunction->get_parameters().size() && functionType->get_returnVals().size() == otherFunction->get_returnVals().size() ) {
+			// sizes don't have to match if ttypes are involved; need to be more precise wrt where the ttype is to prevent errors
+			if ( (functionType->get_parameters().size() == otherFunction->get_parameters().size() && functionType->get_returnVals().size() == otherFunction->get_returnVals().size()) || functionType->isTtype() || otherFunction->isTtype() ) {
 				if ( unifyDeclList( functionType->get_parameters().begin(), functionType->get_parameters().end(), otherFunction->get_parameters().begin(), otherFunction->get_parameters().end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {
 					if ( unifyDeclList( functionType->get_returnVals().begin(), functionType->get_returnVals().end(), otherFunction->get_returnVals().begin(), otherFunction->get_returnVals().end(), env, needAssertions, haveAssertions, openVars, indexer ) ) {
