Index: src/GenPoly/Box.cc
===================================================================
--- src/GenPoly/Box.cc	(revision 9dbf7c8c2d637a1c6898bc9874f8560581d06476)
+++ src/GenPoly/Box.cc	(revision ae1b9eaca1bda2fba72d857681b6459557fd6784)
@@ -715,37 +715,31 @@
 		void Pass1::boxParam( Type *param, Expression *&arg, const TyVarMap &exprTyVars ) {
 			assertf( arg->result, "arg does not have result: %s", toString( arg ).c_str() );
-			if ( isPolyType( param, exprTyVars ) ) {
-				Type * newType = arg->get_result()->clone();
+			if ( ! needsBoxing( param, arg->result, exprTyVars, env ) ) return;
+
+			if ( arg->result->get_lvalue() ) {
+				// argument expression may be CFA lvalue, but not C lvalue -- apply generalizedLvalue transformations.
+				// if ( VariableExpr * varExpr = dynamic_cast< VariableExpr * >( arg ) ) {
+				// 	if ( dynamic_cast<ArrayType *>( varExpr->var->get_type() ) ){
+				// 		// temporary hack - don't box arrays, because &arr is not the same as &arr[0]
+				// 		return;
+				// 	}
+				// }
+				arg =  generalizedLvalue( new AddressExpr( arg ) );
+				if ( ! ResolvExpr::typesCompatible( param, arg->get_result(), SymTab::Indexer() ) ) {
+					// silence warnings by casting boxed parameters when the actual type does not match up with the formal type.
+					arg = new CastExpr( arg, param->clone() );
+				}
+			} else {
+				// use type computed in unification to declare boxed variables
+				Type * newType = param->clone();
 				if ( env ) env->apply( newType );
-				std::unique_ptr<Type> manager( newType );
-				if ( isPolyType( newType ) ) {
-					// if the argument's type is polymorphic, we don't need to box again!
-					return;
-				} else if ( arg->get_result()->get_lvalue() ) {
-					// argument expression may be CFA lvalue, but not C lvalue -- apply generalizedLvalue transformations.
-					// if ( VariableExpr * varExpr = dynamic_cast< VariableExpr * >( arg ) ) {
-					// 	if ( dynamic_cast<ArrayType *>( varExpr->var->get_type() ) ){
-					// 		// temporary hack - don't box arrays, because &arr is not the same as &arr[0]
-					// 		return;
-					// 	}
-					// }
-					arg =  generalizedLvalue( new AddressExpr( arg ) );
-					if ( ! ResolvExpr::typesCompatible( param, arg->get_result(), SymTab::Indexer() ) ) {
-						// silence warnings by casting boxed parameters when the actual type does not match up with the formal type.
-						arg = new CastExpr( arg, param->clone() );
-					}
-				} else {
-					// use type computed in unification to declare boxed variables
-					Type * newType = param->clone();
-					if ( env ) env->apply( newType );
-					ObjectDecl *newObj = new ObjectDecl( tempNamer.newName(), Type::StorageClasses(), LinkageSpec::C, 0, newType, 0 );
-					newObj->get_type()->get_qualifiers() = Type::Qualifiers(); // TODO: is this right???
-					stmtsToAddBefore.push_back( new DeclStmt( noLabels, newObj ) );
-					UntypedExpr *assign = new UntypedExpr( new NameExpr( "?=?" ) ); // TODO: why doesn't this just use initialization syntax?
-					assign->get_args().push_back( new VariableExpr( newObj ) );
-					assign->get_args().push_back( arg );
-					stmtsToAddBefore.push_back( new ExprStmt( noLabels, assign ) );
-					arg = new AddressExpr( new VariableExpr( newObj ) );
-				} // if
+				ObjectDecl *newObj = new ObjectDecl( tempNamer.newName(), Type::StorageClasses(), LinkageSpec::C, 0, newType, 0 );
+				newObj->get_type()->get_qualifiers() = Type::Qualifiers(); // TODO: is this right???
+				stmtsToAddBefore.push_back( new DeclStmt( noLabels, newObj ) );
+				UntypedExpr *assign = new UntypedExpr( new NameExpr( "?=?" ) ); // TODO: why doesn't this just use initialization syntax?
+				assign->get_args().push_back( new VariableExpr( newObj ) );
+				assign->get_args().push_back( arg );
+				stmtsToAddBefore.push_back( new ExprStmt( noLabels, assign ) );
+				arg = new AddressExpr( new VariableExpr( newObj ) );
 			} // if
 		}
Index: src/GenPoly/GenPoly.cc
===================================================================
--- src/GenPoly/GenPoly.cc	(revision 9dbf7c8c2d637a1c6898bc9874f8560581d06476)
+++ src/GenPoly/GenPoly.cc	(revision ae1b9eaca1bda2fba72d857681b6459557fd6784)
@@ -432,4 +432,22 @@
 	}
 
+	bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, TypeSubstitution * env ) {
+		// is parameter is not polymorphic, don't need to box
+		if ( ! isPolyType( param, exprTyVars ) ) return false;
+		Type * newType = arg->clone();
+		if ( env ) env->apply( newType );
+		std::unique_ptr<Type> manager( newType );
+		// if the argument's type is polymorphic, we don't need to box again!
+		return ! isPolyType( newType );
+	}
+
+	bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, TypeSubstitution * env ) {
+		FunctionType * function = getFunctionType( appExpr->function->result );
+		assertf( function, "ApplicationExpr has non-function type: %s", toString( appExpr->function->result ).c_str() );
+		TyVarMap exprTyVars( TypeDecl::Data{} );
+		makeTyVarMap( function, exprTyVars );
+		return needsBoxing( param, arg, exprTyVars, env );
+	}
+
 	void addToTyVarMap( TypeDecl * tyVar, TyVarMap &tyVarMap ) {
 		// xxx - should this actually be insert?
Index: src/GenPoly/GenPoly.h
===================================================================
--- src/GenPoly/GenPoly.h	(revision 9dbf7c8c2d637a1c6898bc9874f8560581d06476)
+++ src/GenPoly/GenPoly.h	(revision ae1b9eaca1bda2fba72d857681b6459557fd6784)
@@ -80,4 +80,10 @@
 	bool typesPolyCompatible( Type *aty, Type *bty );
 
+	/// true if arg requires boxing given exprTyVars
+	bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, TypeSubstitution * env );
+
+	/// true if arg requires boxing in the call to appExpr
+	bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, TypeSubstitution * env );
+
 	/// Adds the type variable `tyVar` to `tyVarMap`
 	void addToTyVarMap( TypeDecl * tyVar, TyVarMap &tyVarMap );
