Index: src/SymTab/Autogen.cc
===================================================================
--- src/SymTab/Autogen.cc	(revision 0270824d417173d850d0b904d76e8e5873a6ec65)
+++ src/SymTab/Autogen.cc	(revision 207c7e1ddfd88d45fb9f62e00f173daf5b443541)
@@ -25,4 +25,5 @@
 #include "Autogen.h"
 #include "GenPoly/ScopedSet.h"
+#include "Common/ScopedMap.h"
 #include "SymTab/Mangler.h"
 #include "GenPoly/DeclMutator.h"
@@ -30,6 +31,16 @@
 namespace SymTab {
 	Type * SizeType = 0;
-
-	class AutogenerateRoutines : public Visitor {
+	typedef ScopedMap< std::string, bool > TypeMap;
+
+	/// Data used to generate functions generically. Specifically, the name of the generated function, a function which generates the routine protoype, and a map which contains data to determine whether a function should be generated.
+	struct FuncData {
+		typedef FunctionType * (*TypeGen)( Type * );
+		FuncData( const std::string & fname, const TypeGen & genType, TypeMap & map ) : fname( fname ), genType( genType ), map( map ) {}
+		std::string fname;
+		TypeGen genType;
+		TypeMap & map;
+	};
+
+	class AutogenerateRoutines final : public Visitor {
 	  public:
 		std::list< Declaration * > &get_declsToAdd() { return declsToAdd; }
@@ -37,4 +48,6 @@
 		typedef Visitor Parent;
 		using Parent::visit;
+
+		AutogenerateRoutines();
 
 		virtual void visit( EnumDecl *enumDecl );
@@ -57,4 +70,9 @@
 		std::set< std::string > structsDone;
 		unsigned int functionNesting = 0;     // current level of nested functions
+		/// Note: the following maps could be ScopedSets, but it should be easier to work
+		/// deleted functions in if they are maps, since the value false can be inserted
+		/// at the current scope without affecting outer scopes or requiring copies.
+		TypeMap copyable, assignable, constructable, destructable;
+		std::vector< FuncData > data;
 	};
 
@@ -144,4 +162,66 @@
 		decl->fixUniqueId();
 		return decl;
+	}
+
+	/// inserts base type of first argument into map if pred(funcDecl) is true
+	void insert( FunctionDecl *funcDecl, TypeMap & map, FunctionDecl * (*pred)(Declaration *) ) {
+		// insert type into constructable, etc. map if appropriate
+		if ( pred( funcDecl ) ) {
+			FunctionType * ftype = funcDecl->get_functionType();
+			assert( ! ftype->get_parameters().empty() );
+			Type * t = safe_dynamic_cast< PointerType * >( ftype->get_parameters().front()->get_type() )->get_base();
+			map.insert( Mangler::mangleType( t ), true );
+		}
+	}
+
+	/// using map and t, determines if is constructable, etc.
+	bool lookup( const TypeMap & map, Type * t ) {
+		if ( dynamic_cast< PointerType * >( t ) ) {
+			// will need more complicated checking if we want this to work with pointer types, since currently
+			return true;
+		} else if ( ArrayType * at = dynamic_cast< ArrayType * >( t ) ) {
+			// an array's constructor, etc. is generated on the fly based on the base type's constructor, etc.
+			return lookup( map, at->get_base() );
+		}
+		TypeMap::const_iterator it = map.find( Mangler::mangleType( t ) );
+		if ( it != map.end() ) return it->second;
+		// something that does not appear in the map is by default not constructable, etc.
+		return false;
+	}
+
+	/// 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;
+			}
+		}
+		return true;
+	}
+
+	/// data structure for abstracting the generation of special functions
+	template< typename OutputIterator >
+	struct FuncGenerator {
+		StructDecl *aggregateDecl;
+		StructInstType *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 ) {}
+
+		/// 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 ) {
+			if ( ! shouldGenerate( data.map, aggregateDecl ) ) return;
+			FunctionType * ftype = data.genType( refType );
+			cloneAll( typeParams, ftype->get_forall() );
+			*out++ = genFunc( data.fname, ftype, functionNesting );
+			data.map.insert( Mangler::mangleType( refType ), true );
+		}
+	};
+
+	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 );
 	}
 
@@ -308,28 +388,17 @@
 
 	/// generates struct constructors, destructor, and assignment functions
-	void makeStructFunctions( StructDecl *aggregateDecl, StructInstType *refType, unsigned int functionNesting, std::list< Declaration * > & declsToAdd ) {
-
+	void makeStructFunctions( StructDecl *aggregateDecl, StructInstType *refType, unsigned int functionNesting, std::list< Declaration * > & declsToAdd, const std::vector< FuncData > & data ) {
 		// Make function polymorphic in same parameters as generic struct, if applicable
 		const std::list< TypeDecl* > & typeParams = aggregateDecl->get_parameters(); // List of type variables to be placed on the generated functions
 		bool isDynamicLayout = hasDynamicLayout( aggregateDecl );  // NOTE this flag is an incredibly ugly kludge; we should fix the assignment signature instead (ditto for union)
 
-		// T ?=?(T *, T);
-		FunctionType *assignType = genAssignType( refType );
-		cloneAll( typeParams, assignType->get_forall() );
-
-		// void ?{}(T *); void ^?{}(T *);
-		FunctionType *ctorType = genDefaultType( refType );
-		cloneAll( typeParams, ctorType->get_forall() );
-		FunctionType *dtorType = genDefaultType( refType );
-		cloneAll( typeParams, dtorType->get_forall() );
-
-		// void ?{}(T *, T);
-		FunctionType *copyCtorType = genCopyType( refType );
-		cloneAll( typeParams, copyCtorType->get_forall() );
-
-		FunctionDecl *assignDecl = genFunc( "?=?", assignType, functionNesting );
-		FunctionDecl *ctorDecl = genFunc( "?{}", ctorType, functionNesting );
-		FunctionDecl *copyCtorDecl = genFunc( "?{}", copyCtorType, functionNesting );
-		FunctionDecl *dtorDecl = genFunc( "^?{}", dtorType, functionNesting );
+		// 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 ) );
+		for ( const FuncData & d : data ) {
+			generator.gen( d );
+		}
+		// field ctors are only generated if default constructor and copy constructor are both generated
+		unsigned numCtors = std::count_if( newFuncs.begin(), newFuncs.end(), [](FunctionDecl * dcl) { return InitTweak::isConstructor( dcl->get_name() ); } );
 
 		if ( functionNesting == 0 ) {
@@ -338,54 +407,58 @@
 			// Note: this is necessary if we want structs which contain
 			// generic (otype) structs as members.
-			addForwardDecl( assignDecl, declsToAdd );
-			addForwardDecl( ctorDecl, declsToAdd );
-			addForwardDecl( copyCtorDecl, declsToAdd );
-			addForwardDecl( dtorDecl, declsToAdd );
+			for ( FunctionDecl * dcl : newFuncs ) {
+				addForwardDecl( dcl, declsToAdd );
+			}
+		}
+
+		for ( FunctionDecl * dcl : newFuncs ) {
+			// 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
+			if ( ! InitTweak::isDestructor( dcl->get_name() ) ) {
+				makeStructFunctionBody( aggregateDecl->get_members().begin(), aggregateDecl->get_members().end(), dcl, isDynamicLayout );
+			} else {
+				makeStructFunctionBody( aggregateDecl->get_members().rbegin(), aggregateDecl->get_members().rend(), dcl, isDynamicLayout, false );
+			}
+			if ( InitTweak::isAssignment( dcl->get_name() ) ) {
+				// assignment needs to return a value
+				FunctionType * assignType = dcl->get_functionType();
+				assert( assignType->get_parameters().size() == 2 );
+				ObjectDecl * srcParam = safe_dynamic_cast< ObjectDecl * >( assignType->get_parameters().back() );
+				dcl->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, new VariableExpr( srcParam ) ) );
+			}
+			declsToAdd.push_back( dcl );
 		}
 
 		// create constructors which take each member type as a parameter.
 		// for example, for struct A { int x, y; }; generate
-		// void ?{}(A *, int) and void ?{}(A *, int, int)
-		std::list<Declaration *> memCtors;
-		FunctionType * memCtorType = ctorType->clone();
-		for ( std::list<Declaration *>::iterator i = aggregateDecl->get_members().begin(); i != aggregateDecl->get_members().end(); ++i ) {
-			DeclarationWithType * member = dynamic_cast<DeclarationWithType *>( *i );
-			assert( member );
-			if ( isUnnamedBitfield( dynamic_cast< ObjectDecl * > ( member ) ) ) {
-				// don't make a function whose parameter is an unnamed bitfield
-				continue;
-			} else if ( member->get_name() == "" ) {
-				// don't assign to anonymous members
-				// xxx - this is a temporary fix. Anonymous members tie into
-				// our inheritance model. I think the correct way to handle this is to
-				// cast the structure to the type of the member and let the resolver
-				// figure out whether it's valid and have a pass afterwards that fixes
-				// the assignment to use pointer arithmetic with the offset of the
-				// member, much like how generic type members are handled.
-				continue;
-			}
-			memCtorType->get_parameters().push_back( new ObjectDecl( member->get_name(), DeclarationNode::NoStorageClass, LinkageSpec::Cforall, 0, member->get_type()->clone(), 0 ) );
-			FunctionDecl * ctor = genFunc( "?{}", memCtorType->clone(), functionNesting );
-			makeStructFieldCtorBody( aggregateDecl->get_members().begin(), aggregateDecl->get_members().end(), ctor, isDynamicLayout );
-			memCtors.push_back( ctor );
-		}
-		delete memCtorType;
-
-		// generate appropriate calls to member ctor, assignment
-		makeStructFunctionBody( aggregateDecl->get_members().begin(), aggregateDecl->get_members().end(), assignDecl, isDynamicLayout );
-		makeStructFunctionBody( aggregateDecl->get_members().begin(), aggregateDecl->get_members().end(), ctorDecl, isDynamicLayout );
-		makeStructFunctionBody( aggregateDecl->get_members().begin(), aggregateDecl->get_members().end(), copyCtorDecl, isDynamicLayout );
-		// needs to do everything in reverse, so pass "forward" as false
-		makeStructFunctionBody( aggregateDecl->get_members().rbegin(), aggregateDecl->get_members().rend(), dtorDecl, isDynamicLayout, false );
-
-		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 );
+		//   void ?{}(A *, int) and void ?{}(A *, int, int)
+		// Field constructors are only generated if default and copy constructor
+		// are generated, since they need access to both
+		if ( numCtors == 2 ) {
+			FunctionType * memCtorType = genDefaultType( refType );
+			cloneAll( typeParams, memCtorType->get_forall() );
+			for ( std::list<Declaration *>::iterator i = aggregateDecl->get_members().begin(); i != aggregateDecl->get_members().end(); ++i ) {
+				DeclarationWithType * member = dynamic_cast<DeclarationWithType *>( *i );
+				assert( member );
+				if ( isUnnamedBitfield( dynamic_cast< ObjectDecl * > ( member ) ) ) {
+					// don't make a function whose parameter is an unnamed bitfield
+					continue;
+				} else if ( member->get_name() == "" ) {
+					// don't assign to anonymous members
+					// xxx - this is a temporary fix. Anonymous members tie into
+					// our inheritance model. I think the correct way to handle this is to
+					// cast the structure to the type of the member and let the resolver
+					// figure out whether it's valid and have a pass afterwards that fixes
+					// the assignment to use pointer arithmetic with the offset of the
+					// member, much like how generic type members are handled.
+					continue;
+				}
+				memCtorType->get_parameters().push_back( new ObjectDecl( member->get_name(), DeclarationNode::NoStorageClass, LinkageSpec::Cforall, 0, member->get_type()->clone(), 0 ) );
+				FunctionDecl * ctor = genFunc( "?{}", memCtorType->clone(), functionNesting );
+				makeStructFieldCtorBody( aggregateDecl->get_members().begin(), aggregateDecl->get_members().end(), ctor, isDynamicLayout );
+				declsToAdd.push_back( ctor );
+			}
+			delete memCtorType;
+		}
 	}
 
@@ -481,4 +554,13 @@
 	}
 
+	AutogenerateRoutines::AutogenerateRoutines() {
+		// the order here determines the order that these functions are generated.
+		// assignment should come last since it uses copy constructor in return.
+		data.push_back( FuncData( "?{}", genDefaultType, constructable ) );
+		data.push_back( FuncData( "?{}", genCopyType, copyable ) );
+		data.push_back( FuncData( "^?{}", genDefaultType, destructable ) );
+		data.push_back( FuncData( "?=?", genAssignType, assignable ) );
+	}
+
 	void AutogenerateRoutines::visit( EnumDecl *enumDecl ) {
 		if ( ! enumDecl->get_members().empty() ) {
@@ -491,11 +573,13 @@
 
 	void AutogenerateRoutines::visit( StructDecl *structDecl ) {
-		if ( ! structDecl->get_members().empty() && structsDone.find( structDecl->get_name() ) == structsDone.end() ) {
+		if ( structDecl->has_body() && structsDone.find( structDecl->get_name() ) == structsDone.end() ) {
 			StructInstType structInst( Type::Qualifiers(), structDecl->get_name() );
 			for ( TypeDecl * typeDecl : structDecl->get_parameters() ) {
+				// need to visit assertions so that they are added to the appropriate maps
+				acceptAll( typeDecl->get_assertions(), *this );
 				structInst.get_parameters().push_back( new TypeExpr( new TypeInstType( Type::Qualifiers(), typeDecl->get_name(), typeDecl ) ) );
 			}
 			structInst.set_baseStruct( structDecl );
-			makeStructFunctions( structDecl, &structInst, functionNesting, declsToAdd );
+			makeStructFunctions( structDecl, &structInst, functionNesting, declsToAdd, data );
 			structsDone.insert( structDecl->get_name() );
 		} // if
@@ -564,4 +648,10 @@
 
 	void AutogenerateRoutines::visit( FunctionDecl *functionDecl ) {
+		// record the existence of this function as appropriate
+		insert( functionDecl, constructable, InitTweak::isDefaultConstructor );
+		insert( functionDecl, assignable, InitTweak::isAssignment );
+		insert( functionDecl, copyable, InitTweak::isCopyConstructor );
+		insert( functionDecl, destructable, InitTweak::isDestructor );
+
 		maybeAccept( functionDecl->get_functionType(), *this );
 		acceptAll( functionDecl->get_oldDecls(), *this );
@@ -572,5 +662,13 @@
 
 	void AutogenerateRoutines::visit( CompoundStmt *compoundStmt ) {
+		constructable.beginScope();
+		assignable.beginScope();
+		copyable.beginScope();
+		destructable.beginScope();
 		visitStatement( compoundStmt );
+		constructable.endScope();
+		assignable.endScope();
+		copyable.endScope();
+		destructable.endScope();
 	}
 
