Index: src/GenPoly/Box.cc
===================================================================
--- src/GenPoly/Box.cc	(revision aabc60c0994c2f07ad493223fedfaaf178c15bfe)
+++ 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 aabc60c0994c2f07ad493223fedfaaf178c15bfe)
+++ 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 aabc60c0994c2f07ad493223fedfaaf178c15bfe)
+++ 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 );
Index: src/InitTweak/FixInit.cc
===================================================================
--- src/InitTweak/FixInit.cc	(revision aabc60c0994c2f07ad493223fedfaaf178c15bfe)
+++ src/InitTweak/FixInit.cc	(revision ae1b9eaca1bda2fba72d857681b6459557fd6784)
@@ -93,5 +93,5 @@
 			/// true if type does not need to be copy constructed to ensure correctness
 			bool skipCopyConstruct( Type * type );
-			void copyConstructArg( Expression *& arg, ImplicitCopyCtorExpr * impCpCtorExpr );
+			void copyConstructArg( Expression *& arg, ImplicitCopyCtorExpr * impCpCtorExpr, Type * formal );
 			void destructRet( ObjectDecl * ret, ImplicitCopyCtorExpr * impCpCtorExpr );
 
@@ -260,4 +260,5 @@
 
 		GenStructMemberCalls::generate( translationUnit );
+
 		// xxx - ctor expansion currently has to be after FixCopyCtors, because there is currently a
 		// hack in the way untyped assignments are generated, where the first argument cannot have
@@ -384,15 +385,16 @@
 		}
 
-		void ResolveCopyCtors::copyConstructArg( Expression *& arg, ImplicitCopyCtorExpr * impCpCtorExpr ) {
+		void ResolveCopyCtors::copyConstructArg( Expression *& arg, ImplicitCopyCtorExpr * impCpCtorExpr, Type * formal ) {
 			static UniqueName tempNamer("_tmp_cp");
 			assert( env );
 			CP_CTOR_PRINT( std::cerr << "Type Substitution: " << *env << std::endl; )
 			assert( arg->result );
-			Type * result = arg->get_result();
+			Type * result = arg->result;
 			if ( skipCopyConstruct( result ) ) return; // skip certain non-copyable types
 
-			// type may involve type variables, so apply type substitution to get temporary variable's actual type
+			// type may involve type variables, so apply type substitution to get temporary variable's actual type.
+			// Use applyFree so that types bound in function pointers are not substituted, e.g. in forall(dtype T) void (*)(T).
 			result = result->clone();
-			env->apply( result );
+			env->applyFree( result );
 			ObjectDecl * tmp = ObjectDecl::newObject( "__tmp", result, nullptr );
 			tmp->get_type()->set_const( false );
@@ -405,7 +407,10 @@
 				// if the chosen constructor is intrinsic, the copy is unnecessary, so
 				// don't create the temporary and don't call the copy constructor
-				VariableExpr * function = dynamic_cast< VariableExpr * >( appExpr->get_function() );
-				assert( function );
-				if ( function->get_var()->get_linkage() == LinkageSpec::Intrinsic ) return;
+				VariableExpr * function = strict_dynamic_cast< VariableExpr * >( appExpr->function );
+				if ( function->var->linkage == LinkageSpec::Intrinsic ) {
+					// arguments that need to be boxed need a temporary regardless of whether the copy constructor is intrinsic,
+					// so that the object isn't changed inside of the polymorphic function
+					if ( ! GenPoly::needsBoxing( formal, result, impCpCtorExpr->callExpr, env ) ) return;
+				}
 			}
 
@@ -415,6 +420,6 @@
 			// replace argument to function call with temporary
 			arg = new CommaExpr( cpCtor, new VariableExpr( tmp ) );
-			impCpCtorExpr->get_tempDecls().push_back( tmp );
-			impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", tmp ) );
+			impCpCtorExpr->tempDecls.push_back( tmp );
+			impCpCtorExpr->dtors.push_front( makeCtorDtor( "^?{}", tmp ) );
 		}
 
@@ -426,9 +431,19 @@
 			CP_CTOR_PRINT( std::cerr << "ResolveCopyCtors: " << impCpCtorExpr << std::endl; )
 
-			ApplicationExpr * appExpr = impCpCtorExpr->get_callExpr();
+			ApplicationExpr * appExpr = impCpCtorExpr->callExpr;
 
 			// take each argument and attempt to copy construct it.
-			for ( Expression * & arg : appExpr->get_args() ) {
-				copyConstructArg( arg, impCpCtorExpr );
+			FunctionType * ftype = GenPoly::getFunctionType( appExpr->function->result );
+			assert( ftype );
+			auto & params = ftype->parameters;
+			auto iter = params.begin();
+			for ( Expression * & arg : appExpr->args ) {
+				Type * formal = nullptr;
+				if ( iter != params.end() ) {
+					DeclarationWithType * param = *iter++;
+					formal = param->get_type();
+				}
+
+				copyConstructArg( arg, impCpCtorExpr, formal );
 			} // for
 
@@ -436,5 +451,5 @@
 			// initialized with the return value and is destructed later
 			// xxx - handle named return values?
-			Type * result = appExpr->get_result();
+			Type * result = appExpr->result;
 			if ( ! result->isVoid() ) {
 				static UniqueName retNamer("_tmp_cp_ret");
@@ -442,6 +457,6 @@
 				env->apply( result );
 				ObjectDecl * ret = ObjectDecl::newObject( retNamer.newName(), result, nullptr );
-				ret->get_type()->set_const( false );
-				impCpCtorExpr->get_returnDecls().push_back( ret );
+				ret->type->set_const( false );
+				impCpCtorExpr->returnDecls.push_back( ret );
 				CP_CTOR_PRINT( std::cerr << "makeCtorDtor for a return" << std::endl; )
 				if ( ! dynamic_cast< ReferenceType * >( result ) ) {
