Index: src/GenPoly/GenPoly.cc
===================================================================
--- src/GenPoly/GenPoly.cc	(revision 1c1395d2efc85a2d060e6cc92d06033044b1144d)
+++ src/GenPoly/GenPoly.cc	(revision 912cc7d77cdec9d2b823fd8bbc0df7ec460af004)
@@ -46,4 +46,12 @@
 		}
 
+		bool hasPolyParams( const std::vector<ast::ptr<ast::Expr>> & params, const ast::TypeSubstitution * env) {
+			for (auto &param : params) {
+				auto paramType = param.strict_as<ast::TypeExpr>();
+				if (isPolyType(paramType->type, env)) return true;
+			}
+			return false;
+		}
+
 		/// Checks a parameter list for polymorphic parameters from tyVars; will substitute according to env if present
 		bool hasPolyParams( std::list< Expression* >& params, const TyVarMap &tyVars, const TypeSubstitution *env ) {
@@ -56,4 +64,12 @@
 		}
 
+		bool hasPolyParams( const std::vector<ast::ptr<ast::Expr>> & params, const TyVarMap & tyVars, const ast::TypeSubstitution * env) {
+			for (auto &param : params) {
+				auto paramType = param.strict_as<ast::TypeExpr>();
+				if (isPolyType(paramType->type, tyVars, env)) return true;
+			}
+			return false;
+		}
+
 		/// Checks a parameter list for dynamic-layout parameters from tyVars; will substitute according to env if present
 		bool hasDynParams( std::list< Expression* >& params, const TyVarMap &tyVars, const TypeSubstitution *env ) {
@@ -92,4 +108,13 @@
 			Type *newType = env->lookup( typeInst->get_name() );
 			if ( newType ) return newType;
+		}
+		return type;
+	}
+
+	const ast::Type * replaceTypeInst(const ast::Type * type, const ast::TypeSubstitution * env) {
+		if (!env) return type;
+		if (auto typeInst = dynamic_cast<const ast::TypeInstType*> (type)) {
+			auto newType = env->lookup(typeInst->name);
+			if (newType) return newType;
 		}
 		return type;
@@ -111,4 +136,19 @@
 	}
 
+	const ast::Type * isPolyType(const ast::Type * type, const ast::TypeSubstitution * env) {
+		type = replaceTypeInst( type, env );
+
+		if ( dynamic_cast< const ast::TypeInstType * >( type ) ) {
+			return type;
+		} else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {
+			return isPolyType( arrayType->base, env );
+		} else if ( auto structType = dynamic_cast< const ast::StructInstType* >( type ) ) {
+			if ( hasPolyParams( structType->params, env ) ) return type;
+		} else if ( auto unionType = dynamic_cast< const ast::UnionInstType* >( type ) ) {
+			if ( hasPolyParams( unionType->params, env ) ) return type;
+		}
+		return 0;
+	}
+
 	Type *isPolyType( Type *type, const TyVarMap &tyVars, const TypeSubstitution *env ) {
 		type = replaceTypeInst( type, env );
@@ -126,4 +166,19 @@
 		}
 		return 0;
+	}
+
+	const ast::Type * isPolyType(const ast::Type * type, const TyVarMap & tyVars, const ast::TypeSubstitution * env) {
+		type = replaceTypeInst( type, env );
+
+		if ( auto typeInst = dynamic_cast< const ast::TypeInstType * >( type ) ) {
+			return tyVars.find(typeInst->name) != tyVars.end() ? type : nullptr;
+		} else if ( auto arrayType = dynamic_cast< const ast::ArrayType * >( type ) ) {
+			return isPolyType( arrayType->base, env );
+		} else if ( auto structType = dynamic_cast< const ast::StructInstType* >( type ) ) {
+			if ( hasPolyParams( structType->params, env ) ) return type;
+		} else if ( auto unionType = dynamic_cast< const ast::UnionInstType* >( type ) ) {
+			if ( hasPolyParams( unionType->params, env ) ) return type;
+		}
+		return nullptr;
 	}
 
@@ -449,4 +504,12 @@
 	}
 
+	namespace {
+		// temporary hack to avoid re-implementing anything related to TyVarMap
+		// does this work? these two structs have identical definitions.
+		inline TypeDecl::Data convData(const ast::TypeDecl::Data & data) {
+			return *reinterpret_cast<const TypeDecl::Data *>(&data);
+		}
+	}
+
 	bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, const TypeSubstitution * env ) {
 		// is parameter is not polymorphic, don't need to box
@@ -459,4 +522,13 @@
 	}
 
+	bool needsBoxing( const ast::Type * param, const ast::Type * arg, const TyVarMap &exprTyVars, const ast::TypeSubstitution * env) {
+		// is parameter is not polymorphic, don't need to box
+		if ( ! isPolyType( param, exprTyVars ) ) return false;
+		ast::ptr<ast::Type> newType = arg;
+		if ( env ) env->apply( 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, const TypeSubstitution * env ) {
 		FunctionType * function = getFunctionType( appExpr->function->result );
@@ -467,6 +539,19 @@
 	}
 
+	bool needsBoxing( const ast::Type * param, const ast::Type * arg, const ast::ApplicationExpr * appExpr, const ast::TypeSubstitution * env) {
+		const ast::FunctionType * function = getFunctionType(appExpr->func->result);
+		assertf( function, "ApplicationExpr has non-function type: %s", toString( appExpr->func->result ).c_str() );
+		TyVarMap exprTyVars(TypeDecl::Data{});
+		makeTyVarMap(function, exprTyVars);
+		return needsBoxing(param, arg, exprTyVars, env);
+
+	}
+
 	void addToTyVarMap( TypeDecl * tyVar, TyVarMap &tyVarMap ) {
 		tyVarMap.insert( tyVar->name, TypeDecl::Data{ tyVar } );
+	}
+
+	void addToTyVarMap( const ast::TypeDecl * tyVar, TyVarMap & tyVarMap) {
+		tyVarMap.insert(tyVar->name, convData(ast::TypeDecl::Data{tyVar}));
 	}
 
@@ -478,4 +563,16 @@
 		if ( PointerType *pointer = dynamic_cast< PointerType* >( type ) ) {
 			makeTyVarMap( pointer->get_base(), tyVarMap );
+		}
+	}
+
+	void makeTyVarMap(const ast::Type * type, TyVarMap & tyVarMap) {
+		if (auto ptype = dynamic_cast<const ast::ParameterizedType *>(type)) {
+ 			for (auto & tyVar : ptype->forall) {
+				assert (tyVar);
+				addToTyVarMap(tyVar, tyVarMap);
+			}
+		}
+		if (auto pointer = dynamic_cast<const ast::PointerType *>(type)) {
+			makeTyVarMap(pointer->base, tyVarMap);
 		}
 	}
Index: src/GenPoly/GenPoly.h
===================================================================
--- src/GenPoly/GenPoly.h	(revision 1c1395d2efc85a2d060e6cc92d06033044b1144d)
+++ src/GenPoly/GenPoly.h	(revision 912cc7d77cdec9d2b823fd8bbc0df7ec460af004)
@@ -26,6 +26,6 @@
 
 namespace GenPoly {
+
 	typedef ErasableScopedMap< std::string, TypeDecl::Data > TyVarMap;
-
 	/// Replaces a TypeInstType by its referrent in the environment, if applicable
 	Type* replaceTypeInst( Type* type, const TypeSubstitution* env );
@@ -33,7 +33,9 @@
 	/// returns polymorphic type if is polymorphic type, NULL otherwise; will look up substitution in env if provided
 	Type *isPolyType( Type *type, const TypeSubstitution *env = 0 );
+	const ast::Type * isPolyType(const ast::Type * type, const ast::TypeSubstitution * env = nullptr);
 
 	/// returns polymorphic type if is polymorphic type in tyVars, NULL otherwise; will look up substitution in env if provided
 	Type *isPolyType( Type *type, const TyVarMap &tyVars, const TypeSubstitution *env = 0 );
+	const ast::Type * isPolyType(const ast::Type * type, const TyVarMap & tyVars, const ast::TypeSubstitution * env = nullptr);
 
 	/// returns dynamic-layout type if is dynamic-layout type in tyVars, NULL otherwise; will look up substitution in env if provided
@@ -84,7 +86,9 @@
 	/// true if arg requires boxing given exprTyVars
 	bool needsBoxing( Type * param, Type * arg, const TyVarMap &exprTyVars, const TypeSubstitution * env );
+	bool needsBoxing( const ast::Type * param, const ast::Type * arg, const TyVarMap &exprTyVars, const ast::TypeSubstitution * env);
 
 	/// true if arg requires boxing in the call to appExpr
 	bool needsBoxing( Type * param, Type * arg, ApplicationExpr * appExpr, const TypeSubstitution * env );
+	bool needsBoxing( const ast::Type * param, const ast::Type * arg, const ast::ApplicationExpr * appExpr, const ast::TypeSubstitution * env);
 
 	/// Adds the type variable `tyVar` to `tyVarMap`
@@ -93,4 +97,5 @@
 	/// Adds the declarations in the forall list of type (and its pointed-to type if it's a pointer type) to `tyVarMap`
 	void makeTyVarMap( Type *type, TyVarMap &tyVarMap );
+	void makeTyVarMap(const ast::Type * type, TyVarMap & tyVarMap);
 
 	/// Prints type variable map
