Index: src/GenPoly/InstantiateGeneric.cpp
===================================================================
--- src/GenPoly/InstantiateGeneric.cpp	(revision 53f4b555094e994044173ff395eae0f5a2e92085)
+++ src/GenPoly/InstantiateGeneric.cpp	(revision 594671a1445f75decb2b5c2bf2d9818dcab289fe)
@@ -10,6 +10,6 @@
 // Created On       : Tue Aug 16 10:51:00 2022
 // Last Modified By : Andrew Beach
-// Last Modified On : Mon Oct 31 16:48:00 2022
-// Update Count     : 1
+// Last Modified On : Wed Mar 12 15:18:00 2025
+// Update Count     : 2
 //
 
@@ -159,4 +159,24 @@
 }
 
+/// Get the scrubbed type of a declaration (see scrubTypeVars functions).
+ast::TypeExpr const * scrubTypeDecl(
+		CodeLocation const & location, ast::TypeDecl const * typeDecl ) {
+	switch ( typeDecl->kind ) {
+	// Erase any incomplete dtype to `void` (`T *` -> `void *`).
+	case ast::TypeDecl::Dtype:
+		return new ast::TypeExpr( location, new ast::VoidType() );
+	// Erase any ftype to `void (*)(void)`.
+	case ast::TypeDecl::Ftype:
+		return new ast::TypeExpr( location, new ast::FunctionType() );
+	// Remaining cases are not supported.
+	case ast::TypeDecl::Ttype:
+		assertf( false, "Ttype parameters are not currently allowed as parameters to generic types." );
+		break;
+	default:
+		assertf( false, "Unhandled type parameter kind" );
+		break;
+	}
+}
+
 /// Makes substitutions of params into baseParams; returns dtypeStatic if
 /// there is a concrete instantiation based only on {d,f}type-to-void
@@ -190,21 +210,6 @@
 				gt |= GenericType::concrete;
 			}
-		} else switch ( (*baseParam)->kind ) {
-		case ast::TypeDecl::Dtype:
-			// Here, pretend that any incomplete dtype is `void`.
-			out.emplace_back( new ast::TypeExpr( paramExpr->location,
-				new ast::VoidType() ) );
-			break;
-		case ast::TypeDecl::Ftype:
-			// Here, pretend that any ftype is `void (*)(void)`.
-			out.emplace_back( new ast::TypeExpr( paramExpr->location,
-				new ast::FunctionType() ) );
-			break;
-		case ast::TypeDecl::Ttype:
-			assertf( false, "Ttype parameters are not currently allowed as parameters to generic types." );
-			break;
-		default:
-			assertf( false, "Unhandled type parameter kind" );
-			break;
+		} else {
+			out.emplace_back( scrubTypeDecl( paramExpr->location, *baseParam ) );
 		}
 	}
@@ -443,4 +448,7 @@
 		instantiations(), dtypeStatics(), typeNamer("_conc_") {}
 
+	ast::StructDecl const * previsit( ast::StructDecl const * );
+	ast::UnionDecl const * previsit( ast::UnionDecl const * );
+
 	ast::Type const * postvisit( ast::StructInstType const * inst );
 	ast::Type const * postvisit( ast::UnionInstType const * inst );
@@ -481,4 +489,7 @@
 
 	template<typename AggrDecl>
+	AggrDecl const * fixAggrDecl( AggrDecl const * decl );
+
+	template<typename AggrDecl>
 	ast::Type const * fixInstType( ast::SueInstType<AggrDecl> const * inst );
 
@@ -489,4 +500,33 @@
 		ast::vector<ast::TypeExpr> const & typeSubs );
 };
+
+ast::StructDecl const * GenericInstantiator::previsit( ast::StructDecl const * decl ) {
+	return fixAggrDecl( decl );
+}
+
+ast::UnionDecl const * GenericInstantiator::previsit( ast::UnionDecl const * decl ) {
+	return fixAggrDecl( decl );
+}
+
+template<typename AggrDecl>
+AggrDecl const * GenericInstantiator::fixAggrDecl( AggrDecl const * decl ) {
+	// This function and stripDtypeParams handle declarations before their
+	// first use (required to be in the previsit for types with a self use).
+	if ( decl->params.empty() || !isDtypeStatic( decl->params ) ) {
+		return decl;
+	}
+
+	ast::vector<ast::TypeExpr> typeSubs;
+	for ( auto const & param : decl->params ) {
+		assert( !param->isComplete() );
+		typeSubs.emplace_back( scrubTypeDecl( param->location, param ) );
+	}
+
+	assert( decl->unique() );
+	auto mutDecl = ast::mutate( decl );
+	stripDtypeParams( mutDecl, mutDecl->params, typeSubs );
+
+	return mutDecl;
+}
 
 ast::Type const * GenericInstantiator::postvisit(
@@ -531,4 +571,6 @@
 	case GenericType::dtypeStatic:
 	{
+		// This call to stripDtypeParams is used when a forward declaration
+		// has allowed an instance to appear before the full declaration.
 		auto mutInst = ast::mutate( inst );
 		assert( mutInst->base->unique() );
