Index: src/SymTab/Autogen.cc
===================================================================
--- src/SymTab/Autogen.cc	(revision a64644c8182e47ed436d2d8d7dd8ea0e08ce8362)
+++ src/SymTab/Autogen.cc	(revision 148611679a2e1e25cd25158649aa1734a3b0ec37)
@@ -24,4 +24,7 @@
 #include "MakeLibCfa.h"
 #include "Autogen.h"
+#include "GenPoly/ScopedSet.h"
+#include "SymTab/Mangler.h"
+#include "GenPoly/DeclMutator.h"
 
 namespace SymTab {
@@ -29,5 +32,5 @@
 
 	class AutogenerateRoutines : public Visitor {
-		public:
+	  public:
 		std::list< Declaration * > &get_declsToAdd() { return declsToAdd; }
 
@@ -48,16 +51,38 @@
 		virtual void visit( SwitchStmt *switchStmt );
 
-		AutogenerateRoutines() : functionNesting( 0 ) {}
-		private:
+	  private:
 		template< typename StmtClass > void visitStatement( StmtClass *stmt );
 
 		std::list< Declaration * > declsToAdd;
 		std::set< std::string > structsDone;
-		unsigned int functionNesting;     // current level of nested functions
+		unsigned int functionNesting = 0;     // current level of nested functions
 	};
 
+	/// generates routines for tuple types.
+	/// Doesn't really need to be a mutator, but it's easier to reuse DeclMutator than it is to use AddVisit
+	/// or anything we currently have that supports adding new declarations for visitors
+	class AutogenTupleRoutines : public GenPoly::DeclMutator {
+	  public:
+		typedef GenPoly::DeclMutator Parent;
+		using Parent::mutate;
+
+		virtual DeclarationWithType * mutate( FunctionDecl *functionDecl );
+
+		virtual Type * mutate( TupleType *tupleType );
+
+		virtual CompoundStmt * mutate( CompoundStmt *compoundStmt );
+
+	  private:
+		unsigned int functionNesting = 0;     // current level of nested functions
+		GenPoly::ScopedSet< std::string > seenTuples;
+	};
+
 	void autogenerateRoutines( std::list< Declaration * > &translationUnit ) {
-		AutogenerateRoutines visitor;
-		acceptAndAdd( translationUnit, visitor, false );
+		AutogenerateRoutines generator;
+		acceptAndAdd( translationUnit, generator, false );
+
+		// needs to be done separately because AutogenerateRoutines skips types that appear as function arguments, etc.
+		// AutogenTupleRoutines tupleGenerator;
+		// tupleGenerator.mutateDeclarationList( translationUnit );
 	}
 
@@ -93,5 +118,5 @@
 
 	/// given type T, generate type of assignment, i.e. function type T (*) (T *, T)
-	FunctionType * genAssignType( Type * paramType, const std::list< Expression* > & params = std::list< Expression* >() ) {
+	FunctionType * genAssignType( Type * paramType ) {
 		FunctionType *ftype = genCopyType( paramType );
 		ObjectDecl *returnVal = new ObjectDecl( "_ret", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, nullptr, paramType->clone(), nullptr );
@@ -173,8 +198,8 @@
 		copyCtorDecl->get_statements()->get_kids().push_back( new ExprStmt( noLabels, genEnumAssign( copyCtorType, assignDecl ) ) );
 
-		declsToAdd.push_back( assignDecl );
 		declsToAdd.push_back( ctorDecl );
 		declsToAdd.push_back( copyCtorDecl );
 		declsToAdd.push_back( dtorDecl );
+		declsToAdd.push_back( assignDecl ); // assignment should come last since it uses copy constructor in return
 	}
 
@@ -311,4 +336,6 @@
 			// 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.
 			addForwardDecl( assignDecl, declsToAdd );
 			addForwardDecl( ctorDecl, declsToAdd );
@@ -352,14 +379,12 @@
 		makeStructFunctionBody( aggregateDecl->get_members().rbegin(), aggregateDecl->get_members().rend(), dtorDecl, isDynamicLayout, false );
 
-		if ( ! isDynamicLayout ) {
-			assert( assignType->get_parameters().size() == 2 );
-			ObjectDecl * srcParam = safe_dynamic_cast< ObjectDecl * >( assignType->get_parameters().back() );
-			assignDecl->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, new VariableExpr( srcParam ) ) );
-		}
-
-		declsToAdd.push_back( assignDecl );
+		assert( assignType->get_parameters().size() == 2 );
+		ObjectDecl * srcParam = safe_dynamic_cast< ObjectDecl * >( assignType->get_parameters().back() );
+		assignDecl->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, new VariableExpr( srcParam ) ) );
+
 		declsToAdd.push_back( ctorDecl );
 		declsToAdd.push_back( copyCtorDecl );
 		declsToAdd.push_back( dtorDecl );
+		declsToAdd.push_back( assignDecl ); // assignment should come last since it uses copy constructor in return
 		declsToAdd.splice( declsToAdd.end(), memCtors );
 	}
@@ -402,16 +427,17 @@
 		// void ?{}(T *); void ^?{}(T *);
 		FunctionType *ctorType = genDefaultType( refType );
-		cloneAll( typeParams, ctorType->get_forall() );
 		FunctionType *dtorType = genDefaultType( refType );
-		cloneAll( typeParams, dtorType->get_forall() );
 
 		// copy ctor needs both parameters
 		// void ?{}(T *, T);
 		FunctionType *copyCtorType = genCopyType( refType );
-		cloneAll( typeParams, copyCtorType->get_forall() );
 
 		// assignment needs both and return value
 		// T ?=?(T *, T);
 		FunctionType *assignType = genAssignType( refType );
+
+		cloneAll( typeParams, ctorType->get_forall() );
+		cloneAll( typeParams, dtorType->get_forall() );
+		cloneAll( typeParams, copyCtorType->get_forall() );
 		cloneAll( typeParams, assignType->get_forall() );
 
@@ -448,8 +474,8 @@
 		}
 
-		declsToAdd.push_back( assignDecl );
 		declsToAdd.push_back( ctorDecl );
 		declsToAdd.push_back( copyCtorDecl );
 		declsToAdd.push_back( dtorDecl );
+		declsToAdd.push_back( assignDecl ); // assignment should come last since it uses copy constructor in return
 		declsToAdd.splice( declsToAdd.end(), memCtors );
 	}
@@ -552,3 +578,89 @@
 		visitStatement( switchStmt );
 	}
+
+	void makeTupleFunctionBody( FunctionDecl * function ) {
+		FunctionType * ftype = function->get_functionType();
+		assertf( ftype->get_parameters().size() == 1 || ftype->get_parameters().size() == 2, "too many parameters in generated tuple function" );
+
+		UntypedExpr * untyped = new UntypedExpr( new NameExpr( function->get_name() ) );
+
+		/// xxx - &* is used to make this easier for later passes to handle
+		untyped->get_args().push_back( new AddressExpr( UntypedExpr::createDeref( new VariableExpr( ftype->get_parameters().front() ) ) ) );
+		if ( ftype->get_parameters().size() == 2 ) {
+			untyped->get_args().push_back( new VariableExpr( ftype->get_parameters().back() ) );
+		}
+		function->get_statements()->get_kids().push_back( new ExprStmt( noLabels, untyped ) );
+		function->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, UntypedExpr::createDeref( new VariableExpr( ftype->get_parameters().front() ) ) ) );
+	}
+
+	Type * AutogenTupleRoutines::mutate( TupleType * tupleType ) {
+		tupleType = safe_dynamic_cast< TupleType * >( Parent::mutate( tupleType ) );
+		std::string mangleName = SymTab::Mangler::mangleType( tupleType );
+		if ( seenTuples.find( mangleName ) != seenTuples.end() ) return tupleType;
+		seenTuples.insert( mangleName );
+
+		// T ?=?(T *, T);
+		FunctionType *assignType = genAssignType( tupleType );
+
+		// void ?{}(T *); void ^?{}(T *);
+		FunctionType *ctorType = genDefaultType( tupleType );
+		FunctionType *dtorType = genDefaultType( tupleType );
+
+		// void ?{}(T *, T);
+		FunctionType *copyCtorType = genCopyType( tupleType );
+
+		std::set< TypeDecl* > done;
+		std::list< TypeDecl * > typeParams;
+		for ( Type * t : *tupleType ) {
+			if ( TypeInstType * ty = dynamic_cast< TypeInstType * >( t ) ) {
+				if ( ! done.count( ty->get_baseType() ) ) {
+					TypeDecl * newDecl = new TypeDecl( ty->get_baseType()->get_name(), DeclarationNode::NoStorageClass, nullptr, TypeDecl::Any );
+					TypeInstType * inst = new TypeInstType( Type::Qualifiers(), newDecl->get_name(), newDecl );
+					newDecl->get_assertions().push_back( new FunctionDecl( "?=?", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, genAssignType( inst ), nullptr, true, false ) );
+					newDecl->get_assertions().push_back( new FunctionDecl( "?{}", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, genDefaultType( inst ), nullptr, true, false ) );
+					newDecl->get_assertions().push_back( new FunctionDecl( "?{}", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, genCopyType( inst ), nullptr, true, false ) );
+					newDecl->get_assertions().push_back( new FunctionDecl( "^?{}", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, genDefaultType( inst ), nullptr, true, false ) );
+					typeParams.push_back( newDecl );
+					done.insert( ty->get_baseType() );
+				}
+			}
+		}
+		cloneAll( typeParams, ctorType->get_forall() );
+		cloneAll( typeParams, dtorType->get_forall() );
+		cloneAll( typeParams, copyCtorType->get_forall() );
+		cloneAll( typeParams, assignType->get_forall() );
+
+		FunctionDecl *assignDecl = genFunc( "?=?", assignType, functionNesting );
+		FunctionDecl *ctorDecl = genFunc( "?{}", ctorType, functionNesting );
+		FunctionDecl *copyCtorDecl = genFunc( "?{}", copyCtorType, functionNesting );
+		FunctionDecl *dtorDecl = genFunc( "^?{}", dtorType, functionNesting );
+
+		makeTupleFunctionBody( assignDecl );
+		makeTupleFunctionBody( ctorDecl );
+		makeTupleFunctionBody( copyCtorDecl );
+		makeTupleFunctionBody( dtorDecl );
+
+		addDeclaration( ctorDecl );
+		addDeclaration( copyCtorDecl );
+		addDeclaration( dtorDecl );
+		addDeclaration( assignDecl ); // assignment should come last since it uses copy constructor in return
+
+		return tupleType;
+	}
+
+	DeclarationWithType * AutogenTupleRoutines::mutate( FunctionDecl *functionDecl ) {
+		functionDecl->set_functionType( maybeMutate( functionDecl->get_functionType(), *this ) );
+		mutateAll( functionDecl->get_oldDecls(), *this );
+		functionNesting += 1;
+		functionDecl->set_statements( maybeMutate( functionDecl->get_statements(), *this ) );
+		functionNesting -= 1;
+		return functionDecl;
+	}
+
+	CompoundStmt * AutogenTupleRoutines::mutate( CompoundStmt *compoundStmt ) {
+		seenTuples.beginScope();
+		compoundStmt = safe_dynamic_cast< CompoundStmt * >( Parent::mutate( compoundStmt ) );
+		seenTuples.endScope();
+		return compoundStmt;
+	}
 } // SymTab
