Index: src/SymTab/Indexer.cc
===================================================================
--- src/SymTab/Indexer.cc	(revision 2ae171d8401152d8289643ca2935d2f09ce16b41)
+++ src/SymTab/Indexer.cc	(revision be9036d588c561590b98fa24451d88cd00ce0af1)
@@ -554,7 +554,6 @@
 
 
-	void Indexer::visit( TraitInstType *contextInst ) {
-		acceptAll( contextInst->get_parameters(), *this );
-		acceptAll( contextInst->get_members(), *this );
+	void Indexer::visit( TraitInstType *traitInst ) {
+		acceptAll( traitInst->get_parameters(), *this );
 	}
 
Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision 2ae171d8401152d8289643ca2935d2f09ce16b41)
+++ src/SymTab/Validate.cc	(revision be9036d588c561590b98fa24451d88cd00ce0af1)
@@ -127,13 +127,17 @@
 	  public:
 		LinkReferenceToTypes( bool doDebug, const Indexer *indexer );
-  		using Parent::visit;
+		using Parent::visit;
+		void visit( TypeInstType *typeInst ) final;
+
 		void visit( EnumInstType *enumInst ) final;
 		void visit( StructInstType *structInst ) final;
 		void visit( UnionInstType *unionInst ) final;
-		void visit( TraitInstType *contextInst ) final;
+		void visit( TraitInstType *traitInst ) final;
+
 		void visit( EnumDecl *enumDecl ) final;
 		void visit( StructDecl *structDecl ) final;
 		void visit( UnionDecl *unionDecl ) final;
-		void visit( TypeInstType *typeInst ) final;
+		void visit( TraitDecl * traitDecl ) final;
+
 	  private:
 		const Indexer *indexer;
@@ -453,39 +457,81 @@
 	}
 
-	void LinkReferenceToTypes::visit( TraitInstType *traitInst ) {
+	template< typename Decl >
+	void normalizeAssertions( std::list< Decl * > & assertions ) {
+		// ensure no duplicate trait members after the clone
+		auto pred = [](Decl * d1, Decl * d2) {
+			// only care if they're equal
+			DeclarationWithType * dwt1 = dynamic_cast<DeclarationWithType *>( d1 );
+			DeclarationWithType * dwt2 = dynamic_cast<DeclarationWithType *>( d2 );
+			if ( dwt1 && dwt2 ) {
+				if ( dwt1->get_name() == dwt2->get_name() && ResolvExpr::typesCompatible( dwt1->get_type(), dwt2->get_type(), SymTab::Indexer() ) ) {
+					// std::cerr << "=========== equal:" << std::endl;
+					// std::cerr << "d1: " << d1 << std::endl;
+					// std::cerr << "d2: " << d2 << std::endl;
+					return false;
+				}
+			}
+			return d1 < d2;
+		};
+		std::set<Decl *, decltype(pred)> unique_members( assertions.begin(), assertions.end(), pred );
+		// if ( unique_members.size() != assertions.size() ) {
+		// 	std::cerr << "============different" << std::endl;
+		// 	std::cerr << unique_members.size() << " " << assertions.size() << std::endl;
+		// }
+
+		std::list< Decl * > order;
+		order.splice( order.end(), assertions );
+		std::copy_if( order.begin(), order.end(), back_inserter( assertions ), [&]( Decl * decl ) {
+			return unique_members.count( decl );
+		});
+	}
+
+	// expand assertions from trait instance, performing the appropriate type variable substitutions
+	template< typename Iterator >
+	void expandAssertions( TraitInstType * inst, Iterator out ) {
+		assertf( inst->baseTrait, "Trait instance not linked to base trait: %s", toString( inst ).c_str() );
+		std::list< DeclarationWithType * > asserts;
+		for ( Declaration * decl : inst->baseTrait->members ) {
+			asserts.push_back( safe_dynamic_cast<DeclarationWithType *>( decl->clone() ) );
+		}
+		// substitute trait decl parameters for instance parameters
+		applySubstitution( inst->baseTrait->parameters.begin(), inst->baseTrait->parameters.end(), inst->parameters.begin(), asserts.begin(), asserts.end(), out );
+	}
+
+	void LinkReferenceToTypes::visit( TraitDecl * traitDecl ) {
+		Parent::visit( traitDecl );
+
+		if ( traitDecl->name == "sized" ) {
+			// "sized" is a special trait - flick the sized status on for the type variable
+			assertf( traitDecl->parameters.size() == 1, "Built-in trait 'sized' has incorrect number of parameters: %zd", traitDecl->parameters.size() );
+			TypeDecl * td = traitDecl->parameters.front();
+			td->set_sized( true );
+		}
+
+		// move assertions from type parameters into the body of the trait
+		for ( TypeDecl * td : traitDecl->parameters ) {
+			for ( DeclarationWithType * assert : td->assertions ) {
+				if ( TraitInstType * inst = dynamic_cast< TraitInstType * >( assert->get_type() ) ) {
+					expandAssertions( inst, back_inserter( traitDecl->members ) );
+				} else {
+					traitDecl->members.push_back( assert->clone() );
+				}
+			}
+			deleteAll( td->assertions );
+			td->assertions.clear();
+		} // for
+	}
+
+	void LinkReferenceToTypes::visit( TraitInstType * traitInst ) {
 		Parent::visit( traitInst );
-		if ( traitInst->get_name() == "sized" ) {
-			// "sized" is a special trait with no members - just flick the sized status on for the type variable
-			if ( traitInst->get_parameters().size() != 1 ) {
-				throw SemanticError( "incorrect number of trait parameters: ", traitInst );
-			}
-			TypeExpr * param = safe_dynamic_cast< TypeExpr * > ( traitInst->get_parameters().front() );
-			TypeInstType * inst = safe_dynamic_cast< TypeInstType * > ( param->get_type() );
-			TypeDecl * decl = inst->get_baseType();
-			decl->set_sized( true );
-			// since "sized" is special, the next few steps don't apply
-			return;
-		}
-
 		// handle other traits
-		TraitDecl *traitDecl = indexer->lookupTrait( traitInst->get_name() );
+		TraitDecl *traitDecl = indexer->lookupTrait( traitInst->name );
 		if ( ! traitDecl ) {
-			throw SemanticError( "use of undeclared trait " + traitInst->get_name() );
+			throw SemanticError( "use of undeclared trait " + traitInst->name );
 		} // if
 		if ( traitDecl->get_parameters().size() != traitInst->get_parameters().size() ) {
 			throw SemanticError( "incorrect number of trait parameters: ", traitInst );
 		} // if
-
-		for ( TypeDecl * td : traitDecl->get_parameters() ) {
-			for ( DeclarationWithType * assert : td->get_assertions() ) {
-				traitInst->get_members().push_back( assert->clone() );
-			} // for
-		} // for
-
-		// need to clone members of the trait for ownership purposes
-		std::list< Declaration * > members;
-		std::transform( traitDecl->get_members().begin(), traitDecl->get_members().end(), back_inserter( members ), [](Declaration * dwt) { return dwt->clone(); } );
-
-		applySubstitution( traitDecl->get_parameters().begin(), traitDecl->get_parameters().end(), traitInst->get_parameters().begin(), members.begin(), members.end(), back_inserter( traitInst->get_members() ) );
+		traitInst->baseTrait = traitDecl;
 
 		// need to carry over the 'sized' status of each decl in the instance
@@ -498,4 +544,5 @@
 			}
 		}
+		// normalizeAssertions( traitInst->members );
 	}
 
@@ -561,28 +608,26 @@
 	void forallFixer( Type * func ) {
 		for ( TypeDecl * type : func->get_forall() ) {
-			std::list< DeclarationWithType * > toBeDone, nextRound;
-			toBeDone.splice( toBeDone.end(), type->get_assertions() );
-			while ( ! toBeDone.empty() ) {
-				for ( DeclarationWithType * assertion : toBeDone ) {
-					if ( TraitInstType *traitInst = dynamic_cast< TraitInstType * >( assertion->get_type() ) ) {
-						// expand trait instance into all of its members
-						for ( Declaration * member : traitInst->get_members() ) {
-							DeclarationWithType *dwt = safe_dynamic_cast< DeclarationWithType * >( member );
-							nextRound.push_back( dwt->clone() );
-						}
-						delete traitInst;
-					} else {
-						// pass assertion through
-						FixFunction fixer;
-						assertion = assertion->acceptMutator( fixer );
-						if ( fixer.get_isVoid() ) {
-							throw SemanticError( "invalid type void in assertion of function ", func );
-						}
-						type->get_assertions().push_back( assertion );
-					} // if
-				} // for
-				toBeDone.clear();
-				toBeDone.splice( toBeDone.end(), nextRound );
-			} // while
+			std::list< DeclarationWithType * > asserts;
+			asserts.splice( asserts.end(), type->assertions );
+			// expand trait instances into their members
+			for ( DeclarationWithType * assertion : asserts ) {
+				if ( TraitInstType *traitInst = dynamic_cast< TraitInstType * >( assertion->get_type() ) ) {
+					// expand trait instance into all of its members
+					expandAssertions( traitInst, back_inserter( type->assertions ) );
+					delete traitInst;
+				} else {
+					// pass other assertions through
+					type->assertions.push_back( assertion );
+				} // if
+			} // for
+			// apply FixFunction to every assertion to check for invalid void type
+			for ( DeclarationWithType *& assertion : type->assertions ) {
+				FixFunction fixer;
+				assertion = assertion->acceptMutator( fixer );
+				if ( fixer.get_isVoid() ) {
+					throw SemanticError( "invalid type void in assertion of function ", func );
+				} // if
+			} // for
+			// normalizeAssertions( type->assertions );
 		} // for
 	}
