Index: src/GenPoly/Specialize.cc
===================================================================
--- src/GenPoly/Specialize.cc	(revision a9b657a26c16b0f0fe660b2c9b0ab868fa6d4245)
+++ src/GenPoly/Specialize.cc	(revision dc0557d7d57a0121d00fa3670ea7cf78d4c447b2)
@@ -77,7 +77,34 @@
 	}
 
+	/// True if both types have the same structure, but not necessarily the same types.
+	/// That is, either both types are tuple types with the same size (recursively), or
+	/// both are not tuple types.
+	bool matchingTupleStructure( Type * t1, Type * t2 ) {
+		TupleType * tuple1 = dynamic_cast< TupleType * >( t1 );
+		TupleType * tuple2 = dynamic_cast< TupleType * >( t2 );
+		if ( tuple1 && tuple2 ) {
+			if ( tuple1->size() != tuple2->size() ) return false;
+			for ( auto types : group_iterate( tuple1->get_types(), tuple2->get_types() ) ) {
+				if ( ! matchingTupleStructure( std::get<0>( types ), std::get<1>( types ) ) ) return false;
+			}
+			return true;
+		} else if ( ! tuple1 && ! tuple2 ) return true;
+		return false;
+	}
+
 	bool needsTupleSpecialization( Type *formalType, Type *actualType, TypeSubstitution *env ) {
-		if ( FunctionType * ftype = getFunctionType( formalType ) ) {
-			return ftype->isTtype();
+		// Needs tuple specialization if the structure of the formal type and actual type do not match.
+		// This is the case if the formal type has ttype polymorphism, or if the structure  of tuple types
+		// between the function do not match exactly.
+		if ( FunctionType * fftype = getFunctionType( formalType ) ) {
+			if ( fftype->isTtype() ) return true;
+			FunctionType * aftype = getFunctionType( actualType );
+			assertf( aftype, "formal type is a function type, but actual type is not." );
+			if ( fftype->get_parameters().size() != aftype->get_parameters().size() ) return true;
+			for ( auto params : group_iterate( fftype->get_parameters(), aftype->get_parameters() ) ) {
+				DeclarationWithType * formal = std::get<0>(params);
+				DeclarationWithType * actual = std::get<1>(params);
+				if ( ! matchingTupleStructure( formal->get_type(), actual->get_type() ) ) return true;
+			}
 		}
 		return false;
@@ -110,35 +137,32 @@
 	}
 
-	/// restructures arg to match the structure of a single formal parameter. Assumes that atomic types are compatible (as the Resolver should have ensured this)
-	template< typename OutIterator >
-	void matchOneFormal( Expression * arg, unsigned & idx, Type * formal, OutIterator out ) {
-		if ( TupleType * tupleType = dynamic_cast< TupleType * >( formal ) ) {
+	/// restructures the arguments to match the structure of the formal parameters of the actual function.
+	/// [begin, end) are the exploded arguments.
+	template< typename Iterator, typename OutIterator >
+	void structureArg( Type * type, Iterator & begin, Iterator end, OutIterator out ) {
+		if ( TupleType * tuple = dynamic_cast< TupleType * >( type ) ) {
 			std::list< Expression * > exprs;
-			for ( Type * t : *tupleType ) {
-				matchOneFormal( arg, idx, t, back_inserter( exprs ) );
+			for ( Type * t : *tuple ) {
+				structureArg( t, begin, end, back_inserter( exprs ) );
 			}
 			*out++ = new TupleExpr( exprs );
 		} else {
-			*out++ = new TupleIndexExpr( arg->clone(), idx++ );
-		}
-	}
-
-	/// restructures the ttype argument to match the structure of the formal parameters of the actual function.
-	/// [begin, end) are the formal parameters.
-	/// args is the list of arguments currently given to the actual function, the last of which needs to be restructured.
-	template< typename Iterator, typename OutIterator >
-	void fixLastArg( Expression * last, Iterator begin, Iterator end, OutIterator out ) {
-		if ( Tuples::isTtype( last->get_result() ) ) {
-			*out++ = last;
-		} else {
-			// safe_dynamic_cast for the assertion
-			safe_dynamic_cast< TupleType * >( last->get_result() );
-			unsigned idx = 0;
-			for ( ; begin != end; ++begin ) {
-				DeclarationWithType * formal = *begin;
-				Type * formalType = formal->get_type();
-				matchOneFormal( last, idx, formalType, out );
-			}
-			delete last;
+			assertf( begin != end, "reached the end of the arguments while structuring" );
+			*out++ = *begin++;
+		}
+	}
+
+	/// explode assuming simple cases: either type is pure tuple (but not tuple expr) or type is non-tuple.
+	template< typename OutputIterator >
+	void explodeSimple( Expression * expr, OutputIterator out ) {
+		if ( TupleType * tupleType = dynamic_cast< TupleType * > ( expr->get_result() ) ) {
+			// tuple type, recursively index into its components
+			for ( unsigned int i = 0; i < tupleType->size(); i++ ) {
+				explodeSimple( new TupleIndexExpr( expr->clone(), i ), out );
+			}
+			delete expr;
+		} else {
+			// non-tuple type - output a clone of the expression
+			*out++ = expr;
 		}
 	}
@@ -173,21 +197,18 @@
 		std::list< DeclarationWithType * >::iterator actualBegin = actualType->get_parameters().begin();
 		std::list< DeclarationWithType * >::iterator actualEnd = actualType->get_parameters().end();
-		std::list< DeclarationWithType * >::iterator formalBegin = funType->get_parameters().begin();
-		std::list< DeclarationWithType * >::iterator formalEnd = funType->get_parameters().end();
-
+
+		std::list< Expression * > args;
 		for ( DeclarationWithType* param : thunkFunc->get_functionType()->get_parameters() ) {
-			// walk the parameters to the actual function alongside the parameters to the thunk to find the location where the ttype parameter begins to satisfy parameters in the actual function.
+			// name each thunk parameter and explode it - these are then threaded back into the actual function call.
 			param->set_name( paramNamer.newName() );
-			assertf( formalBegin != formalEnd, "Reached end of formal parameters before finding ttype parameter" );
-			if ( Tuples::isTtype((*formalBegin)->get_type()) ) {
-				fixLastArg( new VariableExpr( param ), actualBegin, actualEnd, back_inserter( appExpr->get_args() ) );
-				break;
-			}
-			assertf( actualBegin != actualEnd, "reached end of actual function's arguments before finding ttype parameter" );
-			++actualBegin;
-			++formalBegin;
-
-			appExpr->get_args().push_back( new VariableExpr( param ) );
-		} // for
+			explodeSimple( new VariableExpr( param ), back_inserter( args ) );
+		}
+
+		// walk parameters to the actual function alongside the exploded thunk parameters and restructure the arguments to match the actual parameters.
+		std::list< Expression * >::iterator argBegin = args.begin(), argEnd = args.end();
+		for ( ; actualBegin != actualEnd; ++actualBegin ) {
+			structureArg( (*actualBegin)->get_type(), argBegin, argEnd, back_inserter( appExpr->get_args() ) );
+		}
+
 		appExpr->set_env( maybeClone( env ) );
 		if ( inferParams ) {
