Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision 18e683be9f9f150b044bdcfc96461f948a613ef9)
+++ src/SymTab/Validate.cc	(revision c1398e48a1e93cff3edc8dfe33ad1453eead9738)
@@ -53,4 +53,5 @@
 #include "AST/SymbolTable.hpp"
 #include "AST/Type.hpp"
+#include "AST/TypeSubstitution.hpp"
 #include "CodeGen/CodeGenerator.h"     // for genName
 #include "CodeGen/OperatorTable.h"     // for isCtorDtor, isCtorDtorAssign
@@ -1430,4 +1431,21 @@
 	};
 
+	/// expand assertions from a trait instance, performing appropriate type variable substitutions
+	void expandAssertions( 
+		const ast::TraitInstType * inst, std::vector< ast::ptr< ast::DeclWithType > > & out 
+	) {
+		assertf( inst->base, "Trait instance not linked to base trait: %s", toCString( inst ) );
+
+		// build list of trait members, substituting trait decl parameters for instance parameters
+		ast::TypeSubstitution sub{ 
+			inst->base->params.begin(), inst->base->params.end(), inst->params.begin() };
+		// deliberately take ast::ptr by-value to ensure this does not mutate inst->base
+		for ( ast::ptr< ast::Decl > decl : inst->base->members ) {
+			auto member = decl.strict_as< ast::DeclWithType >();
+			sub.apply( member );
+			out.emplace_back( member );
+		}
+	}
+
 	/// Associates forward declarations of aggregates with their definitions
 	class LinkReferenceToTypes_new final 
@@ -1690,13 +1708,35 @@
 		const ast::Decl * postvisit( const ast::TraitDecl * traitDecl ) {
 			// set the "sized" status for the special "sized" trait
-			if ( traitDecl->name != "sized" ) return traitDecl;
-
-			assertf( traitDecl->params.size() == 1, "Built-in trait 'sized' has incorrect number "
-				"of parameters: %zd", traitDecl->params.size() );
-
-			return ast::mutate_field_index( 
-				traitDecl, &ast::TraitDecl::params, 0, 
-				ast::mutate_field( 
-					traitDecl->params.front().get(), &ast::TypeDecl::sized, true ) );
+			if ( traitDecl->name == "sized" ) {
+				assertf( traitDecl->params.size() == 1, "Built-in trait 'sized' has incorrect "
+					"number of parameters: %zd", traitDecl->params.size() );
+
+				traitDecl = ast::mutate_field_index( 
+					traitDecl, &ast::TraitDecl::params, 0, 
+					ast::mutate_field( 
+						traitDecl->params.front().get(), &ast::TypeDecl::sized, true ) );
+			}
+
+			// move assertions from type parameters into the body of the trait
+			std::vector< ast::ptr< ast::DeclWithType > > added;
+			for ( const ast::TypeDecl * td : traitDecl->params ) {
+				for ( const ast::DeclWithType * assn : td->assertions ) {
+					auto inst = dynamic_cast< const ast::TraitInstType * >( assn->get_type() );
+					if ( inst ) {
+						expandAssertions( inst, added );
+					} else {
+						added.emplace_back( assn );
+					}
+				}
+			}
+			if ( ! added.empty() ) {
+				auto mut = mutate( traitDecl );
+				for ( const ast::DeclWithType * decl : added ) {
+					mut->members.emplace_back( decl );
+				}
+				traitDecl = mut;
+			}
+			
+			return traitDecl;
 		}
 	};
@@ -1704,6 +1744,88 @@
 	/// Replaces array and function types in forall lists by appropriate pointer type and assigns 
 	/// each object and function declaration a unique ID
-	struct ForallPointerDecay_new {
-		#warning incomplete
+	class ForallPointerDecay_new {
+		const CodeLocation & location;
+	public:
+		ForallPointerDecay_new( const CodeLocation & loc ) : location( loc ) {}
+
+		const ast::ObjectDecl * previsit( const ast::ObjectDecl * obj ) {
+			// ensure that operator names only apply to functions or function pointers
+			if ( 
+				CodeGen::isOperator( obj->name ) 
+				&& ! dynamic_cast< const ast::FunctionType * >( obj->type->stripDeclarator() )
+			) {
+				SemanticError( obj->location, toCString( "operator ", obj->name.c_str(), " is not "
+					"a function or function pointer." )  );
+			}
+
+			// ensure object has unique ID
+			if ( obj->uniqueId ) return obj;
+			auto mut = mutate( obj );
+			mut->fixUniqueId();
+			return mut;
+		}
+
+		const ast::FunctionDecl * previsit( const ast::FunctionDecl * func ) {
+			// ensure function has unique ID
+			if ( func->uniqueId ) return func;
+			auto mut = mutate( func );
+			mut->fixUniqueId();
+			return mut;
+		}
+
+		/// Fix up assertions -- flattens assertion lists, removing all trait instances
+		template< typename node_t, typename parent_t >
+		static const node_t * forallFixer( 
+			const CodeLocation & loc, const node_t * node, 
+			ast::ParameterizedType::ForallList parent_t::* forallField
+		) {
+			for ( unsigned i = 0; i < (node->*forallField).size(); ++i ) {
+				const ast::TypeDecl * type = (node->*forallField)[i];
+				if ( type->assertions.empty() ) continue;
+
+				std::vector< ast::ptr< ast::DeclWithType > > asserts;
+				asserts.reserve( type->assertions.size() );
+
+				// expand trait instances into their members
+				for ( const ast::DeclWithType * assn : type->assertions ) {
+					auto traitInst = 
+						dynamic_cast< const ast::TraitInstType * >( assn->get_type() ); 
+					if ( traitInst ) {
+						// expand trait instance to all its members
+						expandAssertions( traitInst, asserts );
+					} else {
+						// pass other assertions through
+						asserts.emplace_back( assn );
+					}
+				}
+
+				// apply FixFunction to every assertion to check for invalid void type
+				for ( ast::ptr< ast::DeclWithType > & assn : asserts ) {
+					bool isVoid = false;
+					assn = fixFunction( assn, isVoid );
+					if ( isVoid ) {
+						SemanticError( loc, node, "invalid type void in assertion of function " );
+					}
+				}
+
+				// place mutated assertion list in node
+				auto mut = mutate( type );
+				mut->assertions = move( asserts );
+				node = ast::mutate_field_index( node, forallField, i, mut );
+			}
+			return node;
+		}
+
+		const ast::FunctionType * previsit( const ast::FunctionType * ftype ) {
+			return forallFixer( location, ftype, &ast::FunctionType::forall );
+		}
+
+		const ast::StructDecl * previsit( const ast::StructDecl * aggrDecl ) {
+			return forallFixer( aggrDecl->location, aggrDecl, &ast::StructDecl::params );
+		}
+
+		const ast::UnionDecl * previsit( const ast::UnionDecl * aggrDecl ) {
+			return forallFixer( aggrDecl->location, aggrDecl, &ast::UnionDecl::params );
+		}
 	};
 } // anonymous namespace
@@ -1713,5 +1835,5 @@
 	ast::Pass< EnumAndPointerDecay_new > epc;
 	ast::Pass< LinkReferenceToTypes_new > lrt{ loc, symtab };
-	ast::Pass< ForallPointerDecay_new > fpd;
+	ast::Pass< ForallPointerDecay_new > fpd{ loc };
 
 	return type->accept( epc )->accept( lrt )->accept( fpd );
