Index: src/SymTab/Autogen.cc
===================================================================
--- src/SymTab/Autogen.cc	(revision e3e16bcf91a7300a6d1f71eaad04e2f3f498cfc2)
+++ src/SymTab/Autogen.cc	(revision db70fe486deb21f8bc7f6dec306f1c81a12695b0)
@@ -163,5 +163,4 @@
 		// Routines at global scope marked "static" to prevent multiple definitions in separate translation units
 		// because each unit generates copies of the default routines for each aggregate.
-//		DeclarationNode::StorageClass sc = functionNesting > 0 ? DeclarationNode::NoStorageClass : DeclarationNode::Static;
 		Type::StorageClasses scs = functionNesting > 0 ? Type::StorageClasses() : Type::StorageClasses( Type::Static );
 		LinkageSpec::Spec spec = isIntrinsic ? LinkageSpec::Intrinsic : LinkageSpec::AutoGen;
@@ -186,4 +185,5 @@
 	/// using map and t, determines if is constructable, etc.
 	bool lookup( const TypeMap & map, Type * t ) {
+		assertf( t, "Autogenerate lookup was given non-type: %s", toString( t ).c_str() );
 		if ( dynamic_cast< PointerType * >( t ) ) {
 			// will need more complicated checking if we want this to work with pointer types, since currently
@@ -200,10 +200,8 @@
 
 	/// using map and aggr, examines each member to determine if constructor, etc. should be generated
-	template<typename AggrDecl>
-	bool shouldGenerate( const TypeMap & map, AggrDecl * aggr ) {
-		for ( Declaration * dcl : aggr->get_members() ) {
-			if ( DeclarationWithType * dwt = dynamic_cast< DeclarationWithType * >( dcl ) ) {
-				if ( ! lookup( map, dwt->get_type() ) ) return false;
-			}
+	template<typename Container>
+	bool shouldGenerate( const TypeMap & map, const Container & container ) {
+		for ( Type * t : container ) {
+			if ( ! lookup( map, t ) ) return false;
 		}
 		return true;
@@ -211,23 +209,23 @@
 
 	/// data structure for abstracting the generation of special functions
-	template< typename OutputIterator >
+	template< typename OutputIterator, typename Container >
 	struct FuncGenerator {
-		StructDecl *aggregateDecl;
-		StructInstType *refType;
+		const Container & container;
+		Type *refType;
 		unsigned int functionNesting;
 		const std::list< TypeDecl* > & typeParams;
 		OutputIterator out;
-		FuncGenerator( StructDecl *aggregateDecl, StructInstType *refType, unsigned int functionNesting, const std::list< TypeDecl* > & typeParams, OutputIterator out ) : aggregateDecl( aggregateDecl ), refType( refType ), functionNesting( functionNesting ), typeParams( typeParams ), out( out ) {}
+		FuncGenerator( const Container & container, Type *refType, unsigned int functionNesting, const std::list< TypeDecl* > & typeParams, OutputIterator out ) : container( container ), refType( refType ), functionNesting( functionNesting ), typeParams( typeParams ), out( out ) {}
 
 		/// generates a function (?{}, ?=?, ^?{}) based on the data argument and members. If function is generated, inserts the type into the map.
 		void gen( const FuncData & data, bool concurrent_type ) {
-			if ( ! shouldGenerate( data.map, aggregateDecl ) ) return;
+			if ( ! shouldGenerate( data.map, container ) ) return;
 			FunctionType * ftype = data.genType( refType );
 
 			if(concurrent_type && CodeGen::isDestructor( data.fname )) {
-				ftype->get_parameters().front()->get_type()->set_mutex( true );
-			}
-
-			cloneAll( typeParams, ftype->get_forall() );
+				ftype->parameters.front()->get_type()->set_mutex( true );
+			}
+
+			cloneAll( typeParams, ftype->forall );
 			*out++ = genFunc( data.fname, ftype, functionNesting );
 			data.map.insert( Mangler::mangleType( refType ), true );
@@ -235,7 +233,7 @@
 	};
 
-	template< typename OutputIterator >
-	FuncGenerator<OutputIterator> makeFuncGenerator( StructDecl *aggregateDecl, StructInstType *refType, unsigned int functionNesting, const std::list< TypeDecl* > & typeParams, OutputIterator out ) {
-		return FuncGenerator<OutputIterator>( aggregateDecl, refType, functionNesting, typeParams, out );
+	template< typename OutputIterator, typename Container >
+	FuncGenerator<OutputIterator, Container> makeFuncGenerator( const Container & container, Type *refType, unsigned int functionNesting, const std::list< TypeDecl* > & typeParams, OutputIterator out ) {
+		return FuncGenerator<OutputIterator, Container>( container, refType, functionNesting, typeParams, out );
 	}
 
@@ -393,4 +391,11 @@
 	}
 
+	Type * declToType( Declaration * decl ) {
+		if ( DeclarationWithType * dwt = dynamic_cast< DeclarationWithType * >( decl ) ) {
+			return dwt->get_type();
+		}
+		return nullptr;
+	}
+
 	/// generates struct constructors, destructor, and assignment functions
 	void makeStructFunctions( StructDecl *aggregateDecl, StructInstType *refType, unsigned int functionNesting, std::list< Declaration * > & declsToAdd, const std::vector< FuncData > & data ) {
@@ -406,5 +411,6 @@
 		// generate each of the functions based on the supplied FuncData objects
 		std::list< FunctionDecl * > newFuncs;
-		auto generator = makeFuncGenerator( aggregateDecl, refType, functionNesting, typeParams, back_inserter( newFuncs ) );
+		// structure that iterates aggregate decl members, returning their types
+		auto generator = makeFuncGenerator( lazy_map( aggregateDecl->members, declToType ), refType, functionNesting, typeParams, back_inserter( newFuncs ) );
 		for ( const FuncData & d : data ) {
 			generator.gen( d, aggregateDecl->is_thread() || aggregateDecl->is_monitor() );
@@ -605,26 +611,55 @@
 	}
 
+	Type * declToTypeDeclBase( Declaration * decl ) {
+		if ( TypeDecl * td = dynamic_cast< TypeDecl * >( decl ) ) {
+			return td->base;
+		}
+		return nullptr;
+	}
+
+	// generate ctor/dtors/assign for typedecls, e.g., otype T = int *;
 	void AutogenerateRoutines::visit( TypeDecl *typeDecl ) {
-		TypeInstType *typeInst = new TypeInstType( Type::Qualifiers(), typeDecl->get_name(), false );
-		typeInst->set_baseType( typeDecl );
-		ObjectDecl *src = new ObjectDecl( "_src", Type::StorageClasses(), LinkageSpec::Cforall, nullptr, typeInst->clone(), nullptr );
-		ObjectDecl *dst = new ObjectDecl( "_dst", Type::StorageClasses(), LinkageSpec::Cforall, nullptr, new PointerType( Type::Qualifiers(), typeInst->clone() ), nullptr );
-
-		std::list< Statement * > stmts;
-		if ( typeDecl->get_base() ) {
-			// xxx - generate ctor/dtors for typedecls, e.g.
-			// otype T = int *;
-			UntypedExpr *assign = new UntypedExpr( new NameExpr( "?=?" ) );
-			assign->get_args().push_back( new CastExpr( new VariableExpr( dst ), new PointerType( Type::Qualifiers(), typeDecl->get_base()->clone() ) ) );
-			assign->get_args().push_back( new CastExpr( new VariableExpr( src ), typeDecl->get_base()->clone() ) );
-			stmts.push_back( new ReturnStmt( std::list< Label >(), assign ) );
-		} // if
-		FunctionType *type = new FunctionType( Type::Qualifiers(), false );
-		type->get_returnVals().push_back( new ObjectDecl( "", Type::StorageClasses(), LinkageSpec::Cforall, 0, typeInst, 0 ) );
-		type->get_parameters().push_back( dst );
-		type->get_parameters().push_back( src );
-		FunctionDecl *func = genFunc( "?=?", type, functionNesting );
-		func->get_statements()->get_kids() = stmts;
-		declsToAddAfter.push_back( func );
+		if ( ! typeDecl->base ) return;
+
+		// generate each of the functions based on the supplied FuncData objects
+		std::list< FunctionDecl * > newFuncs;
+		std::list< Declaration * > tds { typeDecl };
+		std::list< TypeDecl * > typeParams;
+		TypeInstType refType( Type::Qualifiers(), typeDecl->name, typeDecl );
+		auto generator = makeFuncGenerator( lazy_map( tds, declToTypeDeclBase ), &refType, functionNesting, typeParams, back_inserter( newFuncs ) );
+		for ( const FuncData & d : data ) {
+			generator.gen( d, false );
+		}
+
+		if ( functionNesting == 0 ) {
+			// forward declare if top-level struct, so that
+			// type is complete as soon as its body ends
+			// Note: this is necessary if we want structs which contain
+			// generic (otype) structs as members.
+			for ( FunctionDecl * dcl : newFuncs ) {
+				addForwardDecl( dcl, declsToAddAfter );
+			}
+		}
+
+		for ( FunctionDecl * dcl : newFuncs ) {
+			FunctionType * ftype = dcl->type;
+			assertf( ftype->parameters.size() == 1 || ftype->parameters.size() == 2, "Incorrect number of parameters in autogenerated typedecl function: %zd", ftype->parameters.size() );
+			DeclarationWithType * dst = ftype->parameters.front();
+			DeclarationWithType * src = ftype->parameters.size() == 2 ? ftype->parameters.back() : nullptr;
+			// generate appropriate calls to member ctor, assignment
+			// destructor needs to do everything in reverse, so pass "forward" based on whether the function is a destructor
+			UntypedExpr * expr = new UntypedExpr( new NameExpr( dcl->name ) );
+			expr->args.push_back( new CastExpr( new VariableExpr( dst ), new ReferenceType( Type::Qualifiers(), typeDecl->base->clone() ) ) );
+			if ( src ) expr->args.push_back( new CastExpr( new VariableExpr( src ), typeDecl->base->clone() ) );
+			dcl->statements->kids.push_back( new ExprStmt( noLabels, expr ) );
+			if ( CodeGen::isAssignment( dcl->get_name() ) ) {
+				// assignment needs to return a value
+				FunctionType * assignType = dcl->type;
+				assert( assignType->parameters.size() == 2 );
+				ObjectDecl * srcParam = strict_dynamic_cast< ObjectDecl * >( assignType->parameters.back() );
+				dcl->statements->kids.push_back( new ReturnStmt( noLabels, new VariableExpr( srcParam ) ) );
+			}
+			declsToAddAfter.push_back( dcl );
+		}
 	}
 
Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision e3e16bcf91a7300a6d1f71eaad04e2f3f498cfc2)
+++ src/SymTab/Validate.cc	(revision db70fe486deb21f8bc7f6dec306f1c81a12695b0)
@@ -176,23 +176,30 @@
 	};
 
-	class EliminateTypedef : public Mutator {
-	  public:
+	struct EliminateTypedef final : public WithVisitorRef<EliminateTypedef>, public WithGuards {
 		EliminateTypedef() : scopeLevel( 0 ) {}
 		/// Replaces typedefs by forward declarations
 		static void eliminateTypedef( std::list< Declaration * > &translationUnit );
+
+		Type * postmutate( TypeInstType * aggregateUseType );
+		Declaration * postmutate( TypedefDecl * typeDecl );
+		void premutate( TypeDecl * typeDecl );
+		void premutate( FunctionDecl * funcDecl );
+		void premutate( ObjectDecl * objDecl );
+		DeclarationWithType * postmutate( ObjectDecl * objDecl );
+
+		void premutate( CastExpr * castExpr );
+
+		void premutate( CompoundStmt * compoundStmt );
+		CompoundStmt * postmutate( CompoundStmt * compoundStmt );
+
+		void premutate( StructDecl * structDecl );
+		Declaration * postmutate( StructDecl * structDecl );
+		void premutate( UnionDecl * unionDecl );
+		Declaration * postmutate( UnionDecl * unionDecl );
+		void premutate( EnumDecl * enumDecl );
+		Declaration * postmutate( EnumDecl * enumDecl );
+		Declaration * postmutate( TraitDecl * contextDecl );
+
 	  private:
-		virtual Declaration *mutate( TypedefDecl *typeDecl );
-		virtual TypeDecl *mutate( TypeDecl *typeDecl );
-		virtual DeclarationWithType *mutate( FunctionDecl *funcDecl );
-		virtual DeclarationWithType *mutate( ObjectDecl *objDecl );
-		virtual CompoundStmt *mutate( CompoundStmt *compoundStmt );
-		virtual Type *mutate( TypeInstType *aggregateUseType );
-		virtual Expression *mutate( CastExpr *castExpr );
-
-		virtual Declaration *mutate( StructDecl * structDecl );
-		virtual Declaration *mutate( UnionDecl * unionDecl );
-		virtual Declaration *mutate( EnumDecl * enumDecl );
-		virtual Declaration *mutate( TraitDecl * contextDecl );
-
 		template<typename AggDecl>
 		AggDecl *handleAggregate( AggDecl * aggDecl );
@@ -667,9 +674,9 @@
 
 	void EliminateTypedef::eliminateTypedef( std::list< Declaration * > &translationUnit ) {
-		EliminateTypedef eliminator;
+		PassVisitor<EliminateTypedef> eliminator;
 		mutateAll( translationUnit, eliminator );
-		if ( eliminator.typedefNames.count( "size_t" ) ) {
+		if ( eliminator.pass.typedefNames.count( "size_t" ) ) {
 			// grab and remember declaration of size_t
-			SizeType = eliminator.typedefNames["size_t"].first->get_base()->clone();
+			SizeType = eliminator.pass.typedefNames["size_t"].first->get_base()->clone();
 		} else {
 			// xxx - missing global typedef for size_t - default to long unsigned int, even though that may be wrong
@@ -681,5 +688,5 @@
 	}
 
-	Type *EliminateTypedef::mutate( TypeInstType * typeInst ) {
+	Type * EliminateTypedef::postmutate( TypeInstType * typeInst ) {
 		// instances of typedef types will come here. If it is an instance
 		// of a typdef type, link the instance to its actual type.
@@ -696,5 +703,5 @@
 				rtt->get_parameters().clear();
 				cloneAll( typeInst->get_parameters(), rtt->get_parameters() );
-				mutateAll( rtt->get_parameters(), *this );  // recursively fix typedefs on parameters
+				mutateAll( rtt->get_parameters(), *visitor );  // recursively fix typedefs on parameters
 			} // if
 			delete typeInst;
@@ -708,7 +715,5 @@
 	}
 
-	Declaration *EliminateTypedef::mutate( TypedefDecl * tyDecl ) {
-		Declaration *ret = Mutator::mutate( tyDecl );
-
+	Declaration *EliminateTypedef::postmutate( TypedefDecl * tyDecl ) {
 		if ( typedefNames.count( tyDecl->get_name() ) == 1 && typedefNames[ tyDecl->get_name() ].second == scopeLevel ) {
 			// typedef to the same name from the same scope
@@ -741,9 +746,9 @@
 			return new EnumDecl( enumDecl->get_name(), noAttributes, tyDecl->get_linkage() );
 		} else {
-			return ret->clone();
-		} // if
-	}
-
-	TypeDecl *EliminateTypedef::mutate( TypeDecl * typeDecl ) {
+			return tyDecl->clone();
+		} // if
+	}
+
+	void EliminateTypedef::premutate( TypeDecl * typeDecl ) {
 		TypedefMap::iterator i = typedefNames.find( typeDecl->get_name() );
 		if ( i != typedefNames.end() ) {
@@ -752,22 +757,18 @@
 
 		typedeclNames[ typeDecl->get_name() ] = typeDecl;
-		return Mutator::mutate( typeDecl );
-	}
-
-	DeclarationWithType *EliminateTypedef::mutate( FunctionDecl * funcDecl ) {
-		typedefNames.beginScope();
-		DeclarationWithType *ret = Mutator::mutate( funcDecl );
-		typedefNames.endScope();
-		return ret;
-	}
-
-	DeclarationWithType *EliminateTypedef::mutate( ObjectDecl * objDecl ) {
-		typedefNames.beginScope();
-		DeclarationWithType *ret = Mutator::mutate( objDecl );
-		typedefNames.endScope();
-
-		if ( FunctionType *funtype = dynamic_cast<FunctionType *>( ret->get_type() ) ) { // function type?
+	}
+
+	void EliminateTypedef::premutate( FunctionDecl * ) {
+		GuardScope( typedefNames );
+	}
+
+	void EliminateTypedef::premutate( ObjectDecl * ) {
+		GuardScope( typedefNames );
+	}
+
+	DeclarationWithType *EliminateTypedef::postmutate( ObjectDecl * objDecl ) {
+		if ( FunctionType *funtype = dynamic_cast<FunctionType *>( objDecl->get_type() ) ) { // function type?
 			// replace the current object declaration with a function declaration
-			FunctionDecl * newDecl = new FunctionDecl( ret->get_name(), ret->get_storageClasses(), ret->get_linkage(), funtype, 0, objDecl->get_attributes(), ret->get_funcSpec() );
+			FunctionDecl * newDecl = new FunctionDecl( objDecl->get_name(), objDecl->get_storageClasses(), objDecl->get_linkage(), funtype, 0, objDecl->get_attributes(), objDecl->get_funcSpec() );
 			objDecl->get_attributes().clear();
 			objDecl->set_type( nullptr );
@@ -775,19 +776,18 @@
 			return newDecl;
 		} // if
-		return ret;
-	}
-
-	Expression *EliminateTypedef::mutate( CastExpr * castExpr ) {
-		typedefNames.beginScope();
-		Expression *ret = Mutator::mutate( castExpr );
-		typedefNames.endScope();
-		return ret;
-	}
-
-	CompoundStmt *EliminateTypedef::mutate( CompoundStmt * compoundStmt ) {
-		typedefNames.beginScope();
+		return objDecl;
+	}
+
+	void EliminateTypedef::premutate( CastExpr * ) {
+		GuardScope( typedefNames );
+	}
+
+	void EliminateTypedef::premutate( CompoundStmt * ) {
+		GuardScope( typedefNames );
 		scopeLevel += 1;
-		CompoundStmt *ret = Mutator::mutate( compoundStmt );
-		scopeLevel -= 1;
+		GuardAction( [this](){ scopeLevel -= 1; } );
+	}
+
+	CompoundStmt *EliminateTypedef::postmutate( CompoundStmt * compoundStmt ) {
 		// remove and delete decl stmts
 		filter( compoundStmt->kids, [](Statement * stmt) {
@@ -799,6 +799,5 @@
 			return false;
 		}, true);
-		typedefNames.endScope();
-		return ret;
+		return compoundStmt;
 	}
 
@@ -827,25 +826,31 @@
 	}
 
-	Declaration *EliminateTypedef::mutate( StructDecl * structDecl ) {
+	void EliminateTypedef::premutate( StructDecl * structDecl ) {
 		addImplicitTypedef( structDecl );
-		Mutator::mutate( structDecl );
+	}
+
+
+	Declaration *EliminateTypedef::postmutate( StructDecl * structDecl ) {
 		return handleAggregate( structDecl );
 	}
 
-	Declaration *EliminateTypedef::mutate( UnionDecl * unionDecl ) {
+	void EliminateTypedef::premutate( UnionDecl * unionDecl ) {
 		addImplicitTypedef( unionDecl );
-		Mutator::mutate( unionDecl );
+	}
+
+	Declaration *EliminateTypedef::postmutate( UnionDecl * unionDecl ) {
 		return handleAggregate( unionDecl );
 	}
 
-	Declaration *EliminateTypedef::mutate( EnumDecl * enumDecl ) {
+	void EliminateTypedef::premutate( EnumDecl * enumDecl ) {
 		addImplicitTypedef( enumDecl );
-		Mutator::mutate( enumDecl );
+	}
+
+	Declaration *EliminateTypedef::postmutate( EnumDecl * enumDecl ) {
 		return handleAggregate( enumDecl );
 	}
 
-	Declaration *EliminateTypedef::mutate( TraitDecl * contextDecl ) {
-		Mutator::mutate( contextDecl );
-		return handleAggregate( contextDecl );
+	Declaration *EliminateTypedef::postmutate( TraitDecl * traitDecl ) {
+		return handleAggregate( traitDecl );
 	}
 
