Index: src/ResolvExpr/AlternativeFinder.cc
===================================================================
--- src/ResolvExpr/AlternativeFinder.cc	(revision 907eccb29a08fe51aa5e5c20aa28f841482b4436)
+++ src/ResolvExpr/AlternativeFinder.cc	(revision 1e3d5b661a71dbe22595492bdc80be8525be1611)
@@ -359,25 +359,27 @@
 			}
 			*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;
+			return true;
 		} 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)
Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision 907eccb29a08fe51aa5e5c20aa28f841482b4436)
+++ src/ResolvExpr/Unify.cc	(revision 1e3d5b661a71dbe22595492bdc80be8525be1611)
@@ -517,6 +517,20 @@
 			} // if
 		} // for
-		if ( list1Begin != list1End || list2Begin != list2End ) {
-			return false;
+		// if ( list1Begin != list1End || list2Begin != list2End ) {
+		// 	return false;
+		if ( list1Begin != list1End ) {
+			// try unifying empty tuple type with ttype
+			Type * t1 = (*list1Begin)->get_type();
+			if ( Tuples::isTtype( t1 ) ) {
+				Type * combinedType = combineTypes( list2Begin, list2End );
+				return unifyExact( t1, combinedType, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
+			} else return false;
+		} else if ( list2Begin != list2End ) {
+			// try unifying empty tuple type with ttype
+			Type * t2 = (*list2Begin)->get_type();
+			if ( Tuples::isTtype( t2 ) ) {
+				Type * combinedType = combineTypes( list1Begin, list1End );
+				return unifyExact( combinedType, t2, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
+			} else return false;
 		} else {
 			return true;
