Index: src/GenPoly/GenPoly.cc
===================================================================
--- src/GenPoly/GenPoly.cc	(revision c19edd15735decafa879b4d91a601aead4397b7e)
+++ src/GenPoly/GenPoly.cc	(revision 7f6a7c9815ca57db7bd45062dda4b61c275ad2ec)
@@ -9,7 +9,7 @@
 // Author           : Richard C. Bilson
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Peter A. Buhr
-// Last Modified On : Wed Jun 29 21:45:53 2016
-// Update Count     : 14
+// Last Modified By : Andrew Beach
+// Last Modified On : Wed Sep 14  9:24:00 2022
+// Update Count     : 15
 //
 
@@ -83,4 +83,15 @@
 		}
 
+		bool hasDynParams( const std::vector<ast::ptr<ast::Expr>> & params, const TyVarMap &tyVars, const ast::TypeSubstitution *typeSubs ) {
+			for ( ast::ptr<ast::Expr> const & param : params ) {
+				auto paramType = param.as<ast::TypeExpr>();
+				assertf( paramType, "Aggregate parameters should be type expressions." );
+				if ( isDynType( paramType->type, tyVars, typeSubs ) ) {
+					return true;
+				}
+			}
+			return false;
+		}
+
 		/// Checks a parameter list for inclusion of polymorphic parameters; will substitute according to env if present
 		bool includesPolyParams( std::list< Expression* >& params, const TypeSubstitution *env ) {
@@ -198,4 +209,24 @@
 		}
 		return 0;
+	}
+
+	const ast::BaseInstType *isDynType( const ast::Type *type, const TyVarMap &tyVars, const ast::TypeSubstitution *typeSubs ) {
+		type = replaceTypeInst( type, typeSubs );
+
+		if ( auto inst = dynamic_cast<ast::TypeInstType const *>( type ) ) {
+			auto var = tyVars.find( inst->name );
+			if ( var != tyVars.end() && var->second.isComplete ) {
+				return inst;
+			}
+		} else if ( auto inst = dynamic_cast<ast::StructInstType const *>( type ) ) {
+			if ( hasDynParams( inst->params, tyVars, typeSubs ) ) {
+				return inst;
+			}
+		} else if ( auto inst = dynamic_cast<ast::UnionInstType const *>( type ) ) {
+			if ( hasDynParams( inst->params, tyVars, typeSubs ) ) {
+				return inst;
+			}
+		}
+		return nullptr;
 	}
 
@@ -378,4 +409,9 @@
 		inline D* as( B* p ) { return reinterpret_cast<D*>(p); }
 
+		template<typename D, typename B>
+		inline D const * as( B const * p ) {
+			return reinterpret_cast<D const *>( p );
+		}
+
 		/// Flattens a declaration list
 		template<typename Output>
@@ -391,4 +427,11 @@
 			for ( Type* ty : src ) {
 				ResolvExpr::flatten( ty, out );
+			}
+		}
+
+		void flattenList( vector<ast::ptr<ast::Type>> const & src,
+				vector<ast::ptr<ast::Type>> & out ) {
+			for ( auto const & type : src ) {
+				ResolvExpr::flatten( type, out );
 			}
 		}
@@ -409,4 +452,29 @@
 				// if ( is<VoidType>( aparam->get_type() ) || is<VoidType>( bparam->get_type() ) ) continue;
 				if ( ! typesPolyCompatible( aparam->get_type(), bparam->get_type() ) ) return false;
+			}
+
+			return true;
+		}
+
+		bool paramListsPolyCompatible(
+				std::vector<ast::ptr<ast::Expr>> const & lparams,
+				std::vector<ast::ptr<ast::Expr>> const & rparams ) {
+			if ( lparams.size() != rparams.size() ) {
+				return false;
+			}
+
+			for ( auto lparam = lparams.begin(), rparam = rparams.begin() ;
+					lparam != lparams.end() ; ++lparam, ++rparam ) {
+				ast::TypeExpr const * lexpr = lparam->as<ast::TypeExpr>();
+				assertf( lexpr, "Aggregate parameters should be type expressions" );
+				ast::TypeExpr const * rexpr = rparam->as<ast::TypeExpr>();
+				assertf( rexpr, "Aggregate parameters should be type expressions" );
+
+				// xxx - might need to let VoidType be a wildcard here too; could have some voids
+				// stuffed in for dtype-statics.
+				// if ( is<VoidType>( lexpr->type() ) || is<VoidType>( bparam->get_type() ) ) continue;
+				if ( !typesPolyCompatible( lexpr->type, rexpr->type ) ) {
+					return false;
+				}
 			}
 
@@ -505,4 +573,116 @@
 	}
 
+bool typesPolyCompatible( ast::Type const * lhs, ast::Type const * rhs ) {
+	type_index const lid = typeid(*lhs);
+
+	// Polymorphic types always match:
+	if ( type_index(typeid(ast::TypeInstType)) == lid ) return true;
+
+	type_index const rid = typeid(*rhs);
+	if ( type_index(typeid(ast::TypeInstType)) == rid ) return true;
+
+	// All other types only match if they are the same type:
+	if ( lid != rid ) return false;
+
+	// So remaining types can be examined case by case.
+	// Recurse through type structure (conditions borrowed from Unify.cc).
+
+	if ( type_index(typeid(ast::BasicType)) == lid ) {
+		return as<ast::BasicType>(lhs)->kind == as<ast::BasicType>(rhs)->kind;
+	} else if ( type_index(typeid(ast::PointerType)) == lid ) {
+		ast::PointerType const * l = as<ast::PointerType>(lhs);
+		ast::PointerType const * r = as<ast::PointerType>(rhs);
+
+		// void pointers should match any other pointer type.
+		return is<ast::VoidType>( l->base.get() )
+			|| is<ast::VoidType>( r->base.get() )
+			|| typesPolyCompatible( l->base.get(), r->base.get() );
+	} else if ( type_index(typeid(ast::ReferenceType)) == lid ) {
+		ast::ReferenceType const * l = as<ast::ReferenceType>(lhs);
+		ast::ReferenceType const * r = as<ast::ReferenceType>(rhs);
+
+		// void references should match any other reference type.
+		return is<ast::VoidType>( l->base.get() )
+			|| is<ast::VoidType>( r->base.get() )
+			|| typesPolyCompatible( l->base.get(), r->base.get() );
+	} else if ( type_index(typeid(ast::ArrayType)) == lid ) {
+		ast::ArrayType const * l = as<ast::ArrayType>(lhs);
+		ast::ArrayType const * r = as<ast::ArrayType>(rhs);
+
+		if ( l->isVarLen ) {
+			if ( !r->isVarLen ) return false;
+		} else {
+			if ( r->isVarLen ) return false;
+
+			auto lc = l->dimension.as<ast::ConstantExpr>();
+			auto rc = r->dimension.as<ast::ConstantExpr>();
+			if ( lc && rc && lc->intValue() != rc->intValue() ) {
+				return false;
+			}
+		}
+
+		return typesPolyCompatible( l->base.get(), r->base.get() );
+	} else if ( type_index(typeid(ast::FunctionType)) == lid ) {
+		ast::FunctionType const * l = as<ast::FunctionType>(lhs);
+		ast::FunctionType const * r = as<ast::FunctionType>(rhs);
+
+		std::vector<ast::ptr<ast::Type>> lparams, rparams;
+		flattenList( l->params, lparams );
+		flattenList( r->params, rparams );
+		if ( lparams.size() != rparams.size() ) return false;
+		for ( unsigned i = 0; i < lparams.size(); ++i ) {
+			if ( !typesPolyCompatible( lparams[i], rparams[i] ) ) return false;
+		}
+
+		std::vector<ast::ptr<ast::Type>> lrets, rrets;
+		flattenList( l->returns, lrets );
+		flattenList( r->returns, rrets );
+		if ( lrets.size() != rrets.size() ) return false;
+		for ( unsigned i = 0; i < lrets.size(); ++i ) {
+			if ( !typesPolyCompatible( lrets[i], rrets[i] ) ) return false;
+		}
+		return true;
+	} else if ( type_index(typeid(ast::StructInstType)) == lid ) {
+		ast::StructInstType const * l = as<ast::StructInstType>(lhs);
+		ast::StructInstType const * r = as<ast::StructInstType>(rhs);
+
+		if ( l->name != r->name ) return false;
+		return paramListsPolyCompatible( l->params, r->params );
+	} else if ( type_index(typeid(ast::UnionInstType)) == lid ) {
+		ast::UnionInstType const * l = as<ast::UnionInstType>(lhs);
+		ast::UnionInstType const * r = as<ast::UnionInstType>(rhs);
+
+		if ( l->name != r->name ) return false;
+		return paramListsPolyCompatible( l->params, r->params );
+	} else if ( type_index(typeid(ast::EnumInstType)) == lid ) {
+		ast::EnumInstType const * l = as<ast::EnumInstType>(lhs);
+		ast::EnumInstType const * r = as<ast::EnumInstType>(rhs);
+
+		return l->name == r->name;
+	} else if ( type_index(typeid(ast::TraitInstType)) == lid ) {
+		ast::TraitInstType const * l = as<ast::TraitInstType>(lhs);
+		ast::TraitInstType const * r = as<ast::TraitInstType>(rhs);
+
+		return l->name == r->name;
+	} else if ( type_index(typeid(ast::TupleType)) == lid ) {
+		ast::TupleType const * l = as<ast::TupleType>(lhs);
+		ast::TupleType const * r = as<ast::TupleType>(rhs);
+
+		std::vector<ast::ptr<ast::Type>> ltypes, rtypes;
+		flattenList( l->types, ( ltypes ) );
+		flattenList( r->types, ( rtypes ) );
+		if ( ltypes.size() != rtypes.size() ) return false;
+
+		for ( unsigned i = 0 ; i < ltypes.size() ; ++i ) {
+			if ( !typesPolyCompatible( ltypes[i], rtypes[i] ) ) return false;
+		}
+		return true;
+	// The remaining types (VoidType, VarArgsType, ZeroType & OneType)
+	// have no variation so will always be equal.
+	} else {
+		return true;
+	}
+}
+
 	namespace {
 		// temporary hack to avoid re-implementing anything related to TyVarMap
