Index: src/GenPoly/Specialize.cc
===================================================================
--- src/GenPoly/Specialize.cc	(revision 7350ff97adaef2b46f0f42ff27d7b92617a742d7)
+++ src/GenPoly/Specialize.cc	(revision eb2e723e2366bc9652cf9888645a9249f5fd129a)
@@ -35,7 +35,5 @@
 
 namespace GenPoly {
-	class Specializer;
 	class Specialize final : public PolyMutator {
-		friend class Specializer;
 	  public:
 		using PolyMutator::mutate;
@@ -47,40 +45,13 @@
 		// virtual Expression * mutate( CommaExpr *commaExpr );
 
-		Specializer * specializer = nullptr;
 		void handleExplicitParams( ApplicationExpr *appExpr );
+		Expression * createThunkFunction( FunctionType *funType, Expression *actual, InferredParams *inferParams );
+		Expression * doSpecialization( Type *formalType, Expression *actual, InferredParams *inferParams = nullptr );
+
+		std::string paramPrefix = "_p";
 	};
 
-	class Specializer {
-	  public:
-		Specializer( Specialize & spec ) : spec( spec ), env( spec.env ), stmtsToAdd( spec.stmtsToAdd ) {}
-		virtual bool needsSpecialization( Type * formalType, Type * actualType, TypeSubstitution * env ) = 0;
-		virtual Expression *createThunkFunction( FunctionType *funType, Expression *actual, InferredParams *inferParams ) = 0;
-		virtual Expression *doSpecialization( Type *formalType, Expression *actual, InferredParams *inferParams = 0 );
-
-	  protected:
-		Specialize & spec;
-		std::string paramPrefix = "_p";
-		TypeSubstitution *& env;
-		std::list< Statement * > & stmtsToAdd;
-	};
-
-	// for normal polymorphic -> monomorphic function conversion
-	class PolySpecializer : public Specializer {
-	  public:
-		PolySpecializer( Specialize & spec ) : Specializer( spec ) {}
-		virtual bool needsSpecialization( Type * formalType, Type * actualType, TypeSubstitution * env ) override;
-		virtual Expression *createThunkFunction( FunctionType *funType, Expression *actual, InferredParams *inferParams ) override;
-	};
-
-	// // for tuple -> non-tuple function conversion
-	class TupleSpecializer : public Specializer {
-	  public:
-		TupleSpecializer( Specialize & spec ) : Specializer( spec ) {}
-		virtual bool needsSpecialization( Type * formalType, Type * actualType, TypeSubstitution * env ) override;
-		virtual Expression *createThunkFunction( FunctionType *funType, Expression *actual, InferredParams *inferParams ) override;
-	};
-
 	/// Looks up open variables in actual type, returning true if any of them are bound in the environment or formal type.
-	bool PolySpecializer::needsSpecialization( Type *formalType, Type *actualType, TypeSubstitution *env ) {
+	bool needsPolySpecialization( Type *formalType, Type *actualType, TypeSubstitution *env ) {
 		if ( env ) {
 			using namespace ResolvExpr;
@@ -106,62 +77,16 @@
 	}
 
-	/// Generates a thunk that calls `actual` with type `funType` and returns its address
-	Expression * PolySpecializer::createThunkFunction( FunctionType *funType, Expression *actual, InferredParams *inferParams ) {
-		static UniqueName thunkNamer( "_thunk" );
-
-		FunctionType *newType = funType->clone();
-		if ( env ) {
-			// it is important to replace only occurrences of type variables that occur free in the
-			// thunk's type
-			env->applyFree( newType );
-		} // if
-		// create new thunk with same signature as formal type (C linkage, empty body)
-		FunctionDecl *thunkFunc = new FunctionDecl( thunkNamer.newName(), DeclarationNode::NoStorageClass, LinkageSpec::C, newType, new CompoundStmt( noLabels ), false, false );
-		thunkFunc->fixUniqueId();
-
-		// thunks may be generated and not used - silence warning with attribute
-		thunkFunc->get_attributes().push_back( new Attribute( "unused" ) );
-
-		// thread thunk parameters into call to actual function, naming thunk parameters as we go
-		UniqueName paramNamer( paramPrefix );
-		ApplicationExpr *appExpr = new ApplicationExpr( actual );
-		for ( std::list< DeclarationWithType* >::iterator param = thunkFunc->get_functionType()->get_parameters().begin(); param != thunkFunc->get_functionType()->get_parameters().end(); ++param ) {
-			(*param )->set_name( paramNamer.newName() );
-			appExpr->get_args().push_back( new VariableExpr( *param ) );
-		} // for
-		appExpr->set_env( maybeClone( env ) );
-		if ( inferParams ) {
-			appExpr->get_inferParams() = *inferParams;
-		} // if
-
-		// handle any specializations that may still be present
-		std::string oldParamPrefix = paramPrefix;
-		paramPrefix += "p";
-		// save stmtsToAdd in oldStmts
-		std::list< Statement* > oldStmts;
-		oldStmts.splice( oldStmts.end(), stmtsToAdd );
-		spec.handleExplicitParams( appExpr );
-		paramPrefix = oldParamPrefix;
-		// write any statements added for recursive specializations into the thunk body
-		thunkFunc->get_statements()->get_kids().splice( thunkFunc->get_statements()->get_kids().end(), stmtsToAdd );
-		// restore oldStmts into stmtsToAdd
-		stmtsToAdd.splice( stmtsToAdd.end(), oldStmts );
-
-		// add return (or valueless expression) to the thunk
-		Statement *appStmt;
-		if ( funType->get_returnVals().empty() ) {
-			appStmt = new ExprStmt( noLabels, appExpr );
-		} else {
-			appStmt = new ReturnStmt( noLabels, appExpr );
-		} // if
-		thunkFunc->get_statements()->get_kids().push_back( appStmt );
-
-		// add thunk definition to queue of statements to add
-		stmtsToAdd.push_back( new DeclStmt( noLabels, thunkFunc ) );
-		// return address of thunk function as replacement expression
-		return new AddressExpr( new VariableExpr( thunkFunc ) );
-	}
-
-	Expression * Specializer::doSpecialization( Type *formalType, Expression *actual, InferredParams *inferParams ) {
+	bool needsTupleSpecialization( Type *formalType, Type *actualType, TypeSubstitution *env ) {
+		if ( FunctionType * ftype = getFunctionType( formalType ) ) {
+			return ftype->isTtype();
+		}
+		return false;
+	}
+
+	bool needsSpecialization( Type *formalType, Type *actualType, TypeSubstitution *env ) {
+		return needsPolySpecialization( formalType, actualType, env ) || needsTupleSpecialization( formalType, actualType, env );
+	}
+
+	Expression * Specialize::doSpecialization( Type *formalType, Expression *actual, InferredParams *inferParams ) {
 		assertf( actual->has_result(), "attempting to specialize an untyped expression" );
 		if ( needsSpecialization( formalType, actual->get_result(), env ) ) {
@@ -185,11 +110,4 @@
 	}
 
-	bool TupleSpecializer::needsSpecialization( Type *formalType, Type *actualType, TypeSubstitution *env ) {
-		if ( FunctionType * ftype = getFunctionType( formalType ) ) {
-			return ftype->isTtype();
-		}
-		return false;
-	}
-
 	/// 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 >
@@ -207,21 +125,26 @@
 
 	/// 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.
+	/// [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 ) {
-		// 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;
-	}
-
-	Expression * TupleSpecializer::createThunkFunction( FunctionType *funType, Expression *actual, InferredParams *inferParams ) {
-		static UniqueName thunkNamer( "_tupleThunk" );
+		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;
+		}
+	}
+
+	/// Generates a thunk that calls `actual` with type `funType` and returns its address
+	Expression * Specialize::createThunkFunction( FunctionType *funType, Expression *actual, InferredParams *inferParams ) {
+		static UniqueName thunkNamer( "_thunk" );
 
 		FunctionType *newType = funType->clone();
@@ -253,5 +176,4 @@
 		std::list< DeclarationWithType * >::iterator formalEnd = funType->get_parameters().end();
 
-		Expression * last = nullptr;
 		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.
@@ -259,5 +181,5 @@
 			assertf( formalBegin != formalEnd, "Reached end of formal parameters before finding ttype parameter" );
 			if ( Tuples::isTtype((*formalBegin)->get_type()) ) {
-				last = new VariableExpr( param );
+				fixLastArg( new VariableExpr( param ), actualBegin, actualEnd, back_inserter( appExpr->get_args() ) );
 				break;
 			}
@@ -268,6 +190,4 @@
 			appExpr->get_args().push_back( new VariableExpr( param ) );
 		} // for
-		assert( last );
-		fixLastArg( last, actualBegin, actualEnd, back_inserter( appExpr->get_args() ) );
 		appExpr->set_env( maybeClone( env ) );
 		if ( inferParams ) {
@@ -281,5 +201,5 @@
 		std::list< Statement* > oldStmts;
 		oldStmts.splice( oldStmts.end(), stmtsToAdd );
-		spec.mutate( appExpr );
+		mutate( appExpr );
 		paramPrefix = oldParamPrefix;
 		// write any statements added for recursive specializations into the thunk body
@@ -311,5 +231,5 @@
 		std::list< Expression* >::iterator actual;
 		for ( formal = function->get_parameters().begin(), actual = appExpr->get_args().begin(); formal != function->get_parameters().end() && actual != appExpr->get_args().end(); ++formal, ++actual ) {
-			*actual = specializer->doSpecialization( (*formal )->get_type(), *actual, &appExpr->get_inferParams() );
+			*actual = doSpecialization( (*formal )->get_type(), *actual, &appExpr->get_inferParams() );
 		}
 	}
@@ -322,8 +242,10 @@
 			// create thunks for the inferred parameters
 			// don't need to do this for intrinsic calls, because they aren't actually passed
+			// need to handle explicit params before inferred params so that explicit params do not recieve a changed set of inferParams (and change them again)
+			// alternatively, if order starts to matter then copy appExpr's inferParams and pass them to handleExplicitParams.
+			handleExplicitParams( appExpr );
 			for ( InferredParams::iterator inferParam = appExpr->get_inferParams().begin(); inferParam != appExpr->get_inferParams().end(); ++inferParam ) {
-				inferParam->second.expr = specializer->doSpecialization( inferParam->second.formalType, inferParam->second.expr, inferParam->second.inferParams.get() );
+				inferParam->second.expr = doSpecialization( inferParam->second.formalType, inferParam->second.expr, inferParam->second.inferParams.get() );
 			}
-			handleExplicitParams( appExpr );
 		}
 		return appExpr;
@@ -333,5 +255,5 @@
 		addrExpr->get_arg()->acceptMutator( *this );
 		assert( addrExpr->has_result() );
-		addrExpr->set_arg( specializer->doSpecialization( addrExpr->get_result(), addrExpr->get_arg() ) );
+		addrExpr->set_arg( doSpecialization( addrExpr->get_result(), addrExpr->get_arg() ) );
 		return addrExpr;
 	}
@@ -343,5 +265,5 @@
 			return castExpr;
 		}
-		Expression *specialized = specializer->doSpecialization( castExpr->get_result(), castExpr->get_arg() );
+		Expression *specialized = doSpecialization( castExpr->get_result(), castExpr->get_arg() );
 		if ( specialized != castExpr->get_arg() ) {
 			// assume here that the specialization incorporates the cast
@@ -370,11 +292,4 @@
 	void convertSpecializations( std::list< Declaration* >& translationUnit ) {
 		Specialize spec;
-
-		TupleSpecializer tupleSpec( spec );
-		spec.specializer = &tupleSpec;
-		mutateAll( translationUnit, spec );
-
-		PolySpecializer polySpec( spec );
-		spec.specializer = &polySpec;
 		mutateAll( translationUnit, spec );
 	}
