Index: src/ResolvExpr/Unify.cc
===================================================================
--- src/ResolvExpr/Unify.cc	(revision 6c3a988fe7dbaec419f8cc1c6be3085432447a99)
+++ src/ResolvExpr/Unify.cc	(revision f3b0a07aaf0466e2dd02c4a242002b90d321b115)
@@ -163,5 +163,5 @@
 			case TypeDecl::Ttype:
 			// ttype unifies with any tuple type
-			return dynamic_cast< TupleType * >( type );
+			return dynamic_cast< TupleType * >( type ) || Tuples::isTtype( type );
 		} // switch
 		return false;
@@ -488,10 +488,10 @@
 	}
 
-	template< typename Iterator >
-	std::unique_ptr<Type> combineTypes( Iterator begin, Iterator end ) {
+	template< typename Iterator, typename Func >
+	std::unique_ptr<Type> combineTypes( Iterator begin, Iterator end, Func & toType ) {
 		std::list< Type * > types;
 		for ( ; begin != end; ++begin ) {
 			// it's guaranteed that a ttype variable will be bound to a flat tuple, so ensure that this results in a flat tuple
-			flatten( (*begin)->get_type(), back_inserter( types ) );
+			flatten( toType( *begin ), back_inserter( types ) );
 		}
 		return std::unique_ptr<Type>( new TupleType( Type::Qualifiers(), types ) );
@@ -500,4 +500,5 @@
 	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 ) {
+		auto get_type = [](DeclarationWithType * dwt){ return dwt->get_type(); };
 		for ( ; list1Begin != list1End && list2Begin != list2End; ++list1Begin, ++list2Begin ) {
 			Type * t1 = (*list1Begin)->get_type();
@@ -509,8 +510,8 @@
 			if ( isTtype1 && ! isTtype2 ) {
 				// combine all of the things in list2, then unify
-				return unifyExact( t1, combineTypes( list2Begin, list2End ).get(), env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
+				return unifyExact( t1, combineTypes( list2Begin, list2End, get_type ).get(), env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
 			} else if ( isTtype2 && ! isTtype1 ) {
 				// combine all of the things in list1, then unify
-				return unifyExact( combineTypes( list1Begin, list1End ).get(), t2, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
+				return unifyExact( combineTypes( list1Begin, list1End, get_type ).get(), t2, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
 			} else if ( ! unifyExact( t1, t2, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer ) ) {
 				return false;
@@ -522,5 +523,5 @@
 			Type * t1 = (*list1Begin)->get_type();
 			if ( Tuples::isTtype( t1 ) ) {
-				return unifyExact( t1, combineTypes( list2Begin, list2End ).get(), env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
+				return unifyExact( t1, combineTypes( list2Begin, list2End, get_type ).get(), env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
 			} else return false;
 		} else if ( list2Begin != list2End ) {
@@ -528,5 +529,5 @@
 			Type * t2 = (*list2Begin)->get_type();
 			if ( Tuples::isTtype( t2 ) ) {
-				return unifyExact( combineTypes( list1Begin, list1End ).get(), t2, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
+				return unifyExact( combineTypes( list1Begin, list1End, get_type ).get(), t2, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
 			} else return false;
 		} else {
@@ -665,21 +666,54 @@
 	template< typename Iterator1, typename Iterator2 >
 	bool unifyList( Iterator1 list1Begin, Iterator1 list1End, Iterator2 list2Begin, Iterator2 list2End, TypeEnvironment &env, AssertionSet &needAssertions, AssertionSet &haveAssertions, const OpenVarSet &openVars, WidenMode widenMode, const SymTab::Indexer &indexer ) {
+		auto get_type = [](Type * t) { return t; };
 		for ( ; list1Begin != list1End && list2Begin != list2End; ++list1Begin, ++list2Begin ) {
-			Type *commonType = 0;
-			if ( ! unifyInexact( *list1Begin, *list2Begin, env, needAssertions, haveAssertions, openVars, widenMode, indexer, commonType ) ) {
+			Type * t1 = *list1Begin;
+			Type * t2 = *list2Begin;
+			bool isTtype1 = Tuples::isTtype( t1 );
+			bool isTtype2 = Tuples::isTtype( t2 );
+			// xxx - assumes ttype must be last parameter
+			// xxx - there may be a nice way to refactor this, but be careful because the argument positioning might matter in some cases.
+			if ( isTtype1 && ! isTtype2 ) {
+				// combine all of the things in list2, then unify
+				return unifyExact( t1, combineTypes( list2Begin, list2End, get_type ).get(), env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
+			} else if ( isTtype2 && ! isTtype1 ) {
+				// combine all of the things in list1, then unify
+				return unifyExact( combineTypes( list1Begin, list1End, get_type ).get(), t2, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
+			} else if ( ! unifyExact( t1, t2, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer ) ) {
 				return false;
-			}
-			delete commonType;
+			} // if
+
 		} // for
-		if ( list1Begin != list1End || list2Begin != list2End ) {
-			return false;
+		if ( list1Begin != list1End ) {
+			// try unifying empty tuple type with ttype
+			Type * t1 = *list1Begin;
+			if ( Tuples::isTtype( t1 ) ) {
+				return unifyExact( t1, combineTypes( list2Begin, list2End, get_type ).get(), 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;
+			if ( Tuples::isTtype( t2 ) ) {
+				return unifyExact( combineTypes( list1Begin, list1End, get_type ).get(), t2, env, needAssertions, haveAssertions, openVars, WidenMode( false, false ), indexer );
+			} else return false;
 		} else {
 			return true;
-		} //if
+		} // if
 	}
 
 	void Unify::visit(TupleType *tupleType) {
 		if ( TupleType *otherTuple = dynamic_cast< TupleType* >( type2 ) ) {
-			result = unifyList( tupleType->get_types().begin(), tupleType->get_types().end(), otherTuple->get_types().begin(), otherTuple->get_types().end(), env, needAssertions, haveAssertions, openVars, widenMode, indexer );
+			std::unique_ptr<TupleType> flat1( tupleType->clone() );
+			std::unique_ptr<TupleType> flat2( otherTuple->clone() );
+			std::list<Type *> types1, types2;
+
+			TtypeExpander expander( env );
+			flat1->acceptMutator( expander );
+			flat2->acceptMutator( expander );
+
+			flatten( flat1.get(), back_inserter( types1 ) );
+			flatten( flat2.get(), back_inserter( types2 ) );
+
+			result = unifyList( types1.begin(), types1.end(), types2.begin(), types2.end(), env, needAssertions, haveAssertions, openVars, widenMode, indexer );
 		} // if
 	}
